Blender V4.5
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#include "MEM_guardedalloc.h"
11
12#include "GPU_capabilities.hh"
13#include "GPU_texture.hh"
14
16#include "IMB_imbuf.hh"
17#include "IMB_imbuf_types.hh"
18
19/* gpu ibuf utils */
20
22{
23 if (ibuf->planes > 8) {
24 return false;
25 }
26
27 if (ibuf->byte_buffer.data && !ibuf->float_buffer.data) {
28
31 {
32 /* Grey-scale byte buffers with these color transforms utilize float buffers under the hood
33 * and can therefore be optimized. */
34 return true;
35 }
36 /* TODO: Support gray-scale byte buffers.
37 * The challenge is that Blender always stores byte images as RGBA. */
38 return false;
39 }
40
41 /* Only #IMBuf's with color-space that do not modify the chrominance of the texture data relative
42 * to the scene color space can be uploaded as single channel textures. */
46 {
47 return true;
48 }
49 return false;
50}
51
52static void imb_gpu_get_format(const ImBuf *ibuf,
53 bool high_bitdepth,
54 bool use_grayscale,
55 eGPUTextureFormat *r_texture_format)
56{
57 const bool float_rect = (ibuf->float_buffer.data != nullptr);
58 const bool is_grayscale = use_grayscale && imb_is_grayscale_texture_format_compatible(ibuf);
59
60 if (float_rect) {
61 /* Float. */
62 const bool use_high_bitdepth = (!(ibuf->foptions.flag & OPENEXR_HALF) && high_bitdepth);
63 *r_texture_format = is_grayscale ? (use_high_bitdepth ? GPU_R32F : GPU_R16F) :
64 (use_high_bitdepth ? GPU_RGBA32F : GPU_RGBA16F);
65 }
66 else {
69 {
70 /* Non-color data or scene linear, just store buffer as is. */
71 *r_texture_format = (is_grayscale) ? GPU_R8 : GPU_RGBA8;
72 }
74 /* sRGB, store as byte texture that the GPU can decode directly. */
75 *r_texture_format = (is_grayscale) ? GPU_R16F : GPU_SRGB8_A8;
76 }
77 else {
78 /* Other colorspace, store as half float texture to avoid precision loss. */
79 *r_texture_format = (is_grayscale) ? GPU_R16F : GPU_RGBA16F;
80 }
81 }
82}
83
84static const char *imb_gpu_get_swizzle(const ImBuf *ibuf)
85{
86 return imb_is_grayscale_texture_format_compatible(ibuf) ? "rrra" : "rgba";
87}
88
89/* Return false if no suitable format was found. */
90static bool IMB_gpu_get_compressed_format(const ImBuf *ibuf, eGPUTextureFormat *r_texture_format)
91{
92 /* For DDS we only support data, scene linear and sRGB. Converting to
93 * different colorspace would break the compression. */
94 const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->byte_buffer.colorspace) &&
96
97 if (ibuf->dds_data.fourcc == FOURCC_DXT1) {
98 *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT1 : GPU_RGBA8_DXT1;
99 }
100 else if (ibuf->dds_data.fourcc == FOURCC_DXT3) {
101 *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT3 : GPU_RGBA8_DXT3;
102 }
103 else if (ibuf->dds_data.fourcc == FOURCC_DXT5) {
104 *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT5 : GPU_RGBA8_DXT5;
105 }
106 else {
107 return false;
108 }
109 return true;
110}
111
116static void *imb_gpu_get_data(const ImBuf *ibuf,
117 const bool do_rescale,
118 const int rescale_size[2],
119 const bool store_premultiplied,
120 const bool allow_grayscale,
121 bool *r_freedata,
122 eGPUDataFormat *r_data_format)
123{
124 bool is_float_rect = (ibuf->float_buffer.data != nullptr);
125 const bool is_grayscale = allow_grayscale && imb_is_grayscale_texture_format_compatible(ibuf);
126 void *data_rect = (is_float_rect) ? (void *)ibuf->float_buffer.data :
127 (void *)ibuf->byte_buffer.data;
128 bool freedata = false;
129
130 if (is_float_rect) {
131 /* Float image is already in scene linear colorspace or non-color data by
132 * convention, no colorspace conversion needed. But we do require 4 channels
133 * currently. */
134 if (ibuf->channels != 4 || !store_premultiplied) {
135 data_rect = MEM_malloc_arrayN<float>(4 * size_t(ibuf->x) * size_t(ibuf->y), __func__);
136 *r_freedata = freedata = true;
137
138 if (data_rect == nullptr) {
139 return nullptr;
140 }
141
143 (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
144 }
145 }
146 else {
147 /* Byte image is in original colorspace from the file, and may need conversion.
148 *
149 * We must also convert to premultiplied for correct texture interpolation
150 * and consistency with float images. */
152 /* Non-color data, just store buffer as is. */
153 }
156 {
157 /* sRGB or scene linear, store as byte texture that the GPU can decode directly. */
158 data_rect = MEM_mallocN((is_grayscale ? sizeof(float[4]) : sizeof(uchar[4])) *
160 __func__);
161 *r_freedata = freedata = true;
162
163 if (data_rect == nullptr) {
164 return nullptr;
165 }
166
167 /* Texture storage of images is defined by the alpha mode of the image. The
168 * downside of this is that there can be artifacts near alpha edges. However,
169 * this allows us to use sRGB texture formats and preserves color values in
170 * zero alpha areas, and appears generally closer to what game engines that we
171 * want to be compatible with do. */
172 if (is_grayscale) {
173 /* Convert to byte buffer to then pack as half floats reducing the buffer size by half. */
175 (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
176 is_float_rect = true;
177 }
178 else {
180 (uchar *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
181 }
182 }
183 else {
184 /* Other colorspace, store as float texture to avoid precision loss. */
185 data_rect = MEM_malloc_arrayN<float>(4 * size_t(ibuf->x) * size_t(ibuf->y), __func__);
186 *r_freedata = freedata = true;
187 is_float_rect = true;
188
189 if (data_rect == nullptr) {
190 return nullptr;
191 }
192
193 /* Texture storage of images is defined by the alpha mode of the image. The
194 * downside of this is that there can be artifacts near alpha edges. However,
195 * this allows us to use sRGB texture formats and preserves color values in
196 * zero alpha areas, and appears generally closer to what game engines that we
197 * want to be compatible with do. */
199 (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
200 }
201 }
202
203 if (do_rescale) {
204 const uint8_t *rect = (is_float_rect) ? nullptr : (uint8_t *)data_rect;
205 const float *rect_float = (is_float_rect) ? (float *)data_rect : nullptr;
206
207 ImBuf *scale_ibuf = IMB_allocFromBuffer(rect, rect_float, ibuf->x, ibuf->y, 4);
208 IMB_scale(scale_ibuf, UNPACK2(rescale_size), IMBScaleFilter::Box, false);
209
210 if (freedata) {
211 MEM_freeN(data_rect);
212 }
213
214 data_rect = (is_float_rect) ? (void *)scale_ibuf->float_buffer.data :
215 (void *)scale_ibuf->byte_buffer.data;
216 *r_freedata = freedata = true;
217 /* Steal the rescaled buffer to avoid double free. */
218 (void)IMB_steal_byte_buffer(scale_ibuf);
219 (void)IMB_steal_float_buffer(scale_ibuf);
220 IMB_freeImBuf(scale_ibuf);
221 }
222
223 /* Pack first channel data manually at the start of the buffer. */
224 if (is_grayscale) {
225 void *src_rect = data_rect;
226
227 if (freedata == false) {
228 data_rect = MEM_mallocN(
229 (is_float_rect ? sizeof(float) : sizeof(uchar)) * IMB_get_pixel_count(ibuf), __func__);
230 *r_freedata = freedata = true;
231 }
232
233 if (data_rect == nullptr) {
234 return nullptr;
235 }
236
237 size_t buffer_size = do_rescale ? size_t(rescale_size[0]) * size_t(rescale_size[1]) :
238 size_t(ibuf->x) * size_t(ibuf->y);
239 if (is_float_rect) {
240 for (size_t i = 0; i < buffer_size; i++) {
241 ((float *)data_rect)[i] = ((float *)src_rect)[i * 4];
242 }
243 }
244 else {
245 for (size_t i = 0; i < buffer_size; i++) {
246 ((uchar *)data_rect)[i] = ((uchar *)src_rect)[i * 4];
247 }
248 }
249 }
250
251 *r_data_format = (is_float_rect) ? GPU_DATA_FLOAT : GPU_DATA_UBYTE;
252 return data_rect;
253}
254
255GPUTexture *IMB_touch_gpu_texture(const char *name,
256 ImBuf *ibuf,
257 int w,
258 int h,
259 int layers,
260 bool use_high_bitdepth,
261 bool use_grayscale)
262{
263 eGPUTextureFormat tex_format;
264 imb_gpu_get_format(ibuf, use_high_bitdepth, use_grayscale, &tex_format);
265
266 GPUTexture *tex;
267 if (layers > 0) {
269 name, w, h, layers, 9999, tex_format, GPU_TEXTURE_USAGE_SHADER_READ, nullptr);
270 }
271 else {
273 name, w, h, 9999, tex_format, GPU_TEXTURE_USAGE_SHADER_READ, nullptr);
274 }
275
278 return tex;
279}
280
281void IMB_update_gpu_texture_sub(GPUTexture *tex,
282 ImBuf *ibuf,
283 int x,
284 int y,
285 int z,
286 int w,
287 int h,
288 bool use_high_bitdepth,
289 bool use_grayscale,
290 bool use_premult)
291{
292 const bool do_rescale = (ibuf->x != w || ibuf->y != h);
293 const int size[2] = {w, h};
294
295 eGPUTextureFormat tex_format;
296 imb_gpu_get_format(ibuf, use_high_bitdepth, use_grayscale, &tex_format);
297
298 bool freebuf = false;
299
300 eGPUDataFormat data_format;
301 void *data = imb_gpu_get_data(
302 ibuf, do_rescale, size, use_premult, use_grayscale, &freebuf, &data_format);
303
304 /* Update Texture. */
305 GPU_texture_update_sub(tex, data_format, data, x, y, z, w, h, 1);
306
307 if (freebuf) {
309 }
310}
311
312GPUTexture *IMB_create_gpu_texture(const char *name,
313 ImBuf *ibuf,
314 bool use_high_bitdepth,
315 bool use_premult)
316{
317 GPUTexture *tex = nullptr;
319 bool do_rescale = (ibuf->x != size[0]) || (ibuf->y != size[1]);
320
321 /* Correct the smaller size to maintain the original aspect ratio of the image. */
322 if (do_rescale && ibuf->x != ibuf->y) {
323 if (size[0] > size[1]) {
324 size[1] = int(ibuf->y * (float(size[0]) / ibuf->x));
325 }
326 else {
327 size[0] = int(ibuf->x * (float(size[1]) / ibuf->y));
328 }
329 }
330
331 if (ibuf->ftype == IMB_FTYPE_DDS) {
332 eGPUTextureFormat compressed_format;
333 if (!IMB_gpu_get_compressed_format(ibuf, &compressed_format)) {
334 fprintf(stderr, "Unable to find a suitable DXT compression,");
335 }
336 else if (do_rescale) {
337 fprintf(stderr, "Unable to load DXT image resolution,");
338 }
339 else if (!is_power_of_2_i(ibuf->x) || !is_power_of_2_i(ibuf->y)) {
340 /* We require POT DXT/S3TC texture sizes not because something in there
341 * intrinsically needs it, but because we flip them upside down at
342 * load time, and that (when mipmaps are involved) is only possible
343 * with POT height. */
344 fprintf(stderr, "Unable to load non-power-of-two DXT image resolution,");
345 }
346 else {
348 ibuf->x,
349 ibuf->y,
350 ibuf->dds_data.nummipmaps,
351 compressed_format,
353 ibuf->dds_data.data);
354
355 if (tex != nullptr) {
356 return tex;
357 }
358
359 fprintf(stderr, "ST3C support not found,");
360 }
361 /* Fall back to uncompressed texture. */
362 fprintf(stderr, " falling back to uncompressed (%s, %ix%i).\n", name, ibuf->x, ibuf->y);
363 }
364
365 eGPUTextureFormat tex_format;
366 imb_gpu_get_format(ibuf, use_high_bitdepth, true, &tex_format);
367
368 bool freebuf = false;
369
370 /* Create Texture. Specify read usage to allow both shader and host reads, the latter is needed
371 * by the GPU compositor. */
373 tex = GPU_texture_create_2d(name, UNPACK2(size), 9999, tex_format, usage, nullptr);
374 if (tex == nullptr) {
375 size[0] = max_ii(1, size[0] / 2);
376 size[1] = max_ii(1, size[1] / 2);
377 tex = GPU_texture_create_2d(name, UNPACK2(size), 9999, tex_format, usage, nullptr);
378 do_rescale = true;
379 }
380 BLI_assert(tex != nullptr);
381 eGPUDataFormat data_format;
382 void *data = imb_gpu_get_data(ibuf, do_rescale, size, use_premult, true, &freebuf, &data_format);
383 GPU_texture_update(tex, data_format, data);
384
387
388 if (freebuf) {
390 }
391
392 return tex;
393}
394
396 bool high_bitdepth,
397 bool use_grayscale)
398{
399 eGPUTextureFormat gpu_texture_format;
400 imb_gpu_get_format(ibuf, high_bitdepth, use_grayscale, &gpu_texture_format);
401 return gpu_texture_format;
402}
403
405{
406 const float half_min = -65504;
407 const float half_max = 65504;
408 if (!image_buffer->float_buffer.data) {
409 return;
410 }
411
412 float *rect_float = image_buffer->float_buffer.data;
413
414 int rect_float_len = image_buffer->x * image_buffer->y *
415 (image_buffer->channels == 0 ? 4 : image_buffer->channels);
416
417 for (int i = 0; i < rect_float_len; i++) {
418 rect_float[i] = clamp_f(rect_float[i], half_min, half_max);
419 }
420}
#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)
int GPU_texture_size_with_limit(int res)
GPUTexture * GPU_texture_create_compressed_2d(const char *name, int width, int height, int mip_len, eGPUTextureFormat format, eGPUTextureUsage usage, const void *data)
GPUTexture * GPU_texture_create_2d(const char *name, int width, int height, int mip_len, eGPUTextureFormat format, eGPUTextureUsage usage, const float *data)
void GPU_texture_anisotropic_filter(GPUTexture *texture, bool use_aniso)
eGPUDataFormat
@ GPU_DATA_UBYTE
@ GPU_DATA_FLOAT
eGPUTextureUsage
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_TEXTURE_USAGE_HOST_READ
@ GPU_TEXTURE_USAGE_GENERAL
void GPU_texture_update_sub(GPUTexture *texture, eGPUDataFormat data_format, const void *pixels, int offset_x, int offset_y, int offset_z, int width, int height, int depth)
GPUTexture * GPU_texture_create_2d_array(const char *name, int width, int height, int layer_len, int mip_len, eGPUTextureFormat format, eGPUTextureUsage usage, const float *data)
eGPUTextureFormat
@ GPU_R32F
@ GPU_SRGB8_A8
@ GPU_R16F
@ GPU_SRGB8_A8_DXT5
@ GPU_RGBA32F
@ GPU_RGBA16F
@ GPU_SRGB8_A8_DXT1
@ GPU_RGBA8_DXT1
@ GPU_SRGB8_A8_DXT3
@ GPU_RGBA8_DXT3
@ GPU_R8
@ GPU_RGBA8_DXT5
@ GPU_RGBA8
void GPU_texture_update(GPUTexture *texture, eGPUDataFormat data_format, const void *data)
void GPU_texture_swizzle_set(GPUTexture *texture, const char swizzle[4])
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:777
@ 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
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
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 bool imb_is_grayscale_texture_format_compatible(const ImBuf *ibuf)
Definition util_gpu.cc:21
static bool IMB_gpu_get_compressed_format(const ImBuf *ibuf, eGPUTextureFormat *r_texture_format)
Definition util_gpu.cc:90
GPUTexture * 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:255
static const char * imb_gpu_get_swizzle(const ImBuf *ibuf)
Definition util_gpu.cc:84
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:116
eGPUTextureFormat IMB_gpu_get_texture_format(const ImBuf *ibuf, bool high_bitdepth, bool use_grayscale)
Definition util_gpu.cc:395
void IMB_update_gpu_texture_sub(GPUTexture *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:281
static void imb_gpu_get_format(const ImBuf *ibuf, bool high_bitdepth, bool use_grayscale, eGPUTextureFormat *r_texture_format)
Definition util_gpu.cc:52
GPUTexture * IMB_create_gpu_texture(const char *name, ImBuf *ibuf, bool use_high_bitdepth, bool use_premult)
Definition util_gpu.cc:312
void IMB_gpu_clamp_half_float(ImBuf *image_buffer)
Definition util_gpu.cc:404