Blender V4.3
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
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 else {
37 /* TODO: Support gray-scale byte buffers.
38 * The challenge is that Blender always stores byte images as RGBA. */
39 return false;
40 }
41 }
42
43 /* Only #IMBuf's with color-space that do not modify the chrominance of the texture data relative
44 * to the scene color space can be uploaded as single channel textures. */
48 {
49 return true;
50 }
51 return false;
52}
53
54static void imb_gpu_get_format(const ImBuf *ibuf,
55 bool high_bitdepth,
56 bool use_grayscale,
57 eGPUTextureFormat *r_texture_format)
58{
59 const bool float_rect = (ibuf->float_buffer.data != nullptr);
60 const bool is_grayscale = use_grayscale && imb_is_grayscale_texture_format_compatible(ibuf);
61
62 if (float_rect) {
63 /* Float. */
64 const bool use_high_bitdepth = (!(ibuf->flags & IB_halffloat) && high_bitdepth);
65 *r_texture_format = is_grayscale ? (use_high_bitdepth ? GPU_R32F : GPU_R16F) :
66 (use_high_bitdepth ? GPU_RGBA32F : GPU_RGBA16F);
67 }
68 else {
71 {
72 /* Non-color data or scene linear, just store buffer as is. */
73 *r_texture_format = (is_grayscale) ? GPU_R8 : GPU_RGBA8;
74 }
76 /* sRGB, store as byte texture that the GPU can decode directly. */
77 *r_texture_format = (is_grayscale) ? GPU_R16F : GPU_SRGB8_A8;
78 }
79 else {
80 /* Other colorspace, store as half float texture to avoid precision loss. */
81 *r_texture_format = (is_grayscale) ? GPU_R16F : GPU_RGBA16F;
82 }
83 }
84}
85
86static const char *imb_gpu_get_swizzle(const ImBuf *ibuf)
87{
88 return imb_is_grayscale_texture_format_compatible(ibuf) ? "rrra" : "rgba";
89}
90
91/* Return false if no suitable format was found. */
92static bool IMB_gpu_get_compressed_format(const ImBuf *ibuf, eGPUTextureFormat *r_texture_format)
93{
94 /* For DDS we only support data, scene linear and sRGB. Converting to
95 * different colorspace would break the compression. */
96 const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->byte_buffer.colorspace) &&
98
99 if (ibuf->dds_data.fourcc == FOURCC_DXT1) {
100 *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT1 : GPU_RGBA8_DXT1;
101 }
102 else if (ibuf->dds_data.fourcc == FOURCC_DXT3) {
103 *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT3 : GPU_RGBA8_DXT3;
104 }
105 else if (ibuf->dds_data.fourcc == FOURCC_DXT5) {
106 *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT5 : GPU_RGBA8_DXT5;
107 }
108 else {
109 return false;
110 }
111 return true;
112}
113
118static void *imb_gpu_get_data(const ImBuf *ibuf,
119 const bool do_rescale,
120 const int rescale_size[2],
121 const bool store_premultiplied,
122 const bool allow_grayscale,
123 bool *r_freedata,
124 eGPUDataFormat *r_data_format)
125{
126 bool is_float_rect = (ibuf->float_buffer.data != nullptr);
127 const bool is_grayscale = allow_grayscale && imb_is_grayscale_texture_format_compatible(ibuf);
128 void *data_rect = (is_float_rect) ? (void *)ibuf->float_buffer.data :
129 (void *)ibuf->byte_buffer.data;
130 bool freedata = false;
131
132 if (is_float_rect) {
133 /* Float image is already in scene linear colorspace or non-color data by
134 * convention, no colorspace conversion needed. But we do require 4 channels
135 * currently. */
136 if (ibuf->channels != 4 || !store_premultiplied) {
137 data_rect = MEM_mallocN(sizeof(float[4]) * ibuf->x * ibuf->y, __func__);
138 *r_freedata = freedata = true;
139
140 if (data_rect == nullptr) {
141 return nullptr;
142 }
143
145 (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
146 }
147 }
148 else {
149 /* Byte image is in original colorspace from the file, and may need conversion.
150 *
151 * We must also convert to premultiplied for correct texture interpolation
152 * and consistency with float images. */
154 /* Non-color data, just store buffer as is. */
155 }
158 {
159 /* sRGB or scene linear, store as byte texture that the GPU can decode directly. */
160 data_rect = MEM_mallocN(
161 (is_grayscale ? sizeof(float[4]) : sizeof(uchar[4])) * ibuf->x * ibuf->y, __func__);
162 *r_freedata = freedata = true;
163
164 if (data_rect == nullptr) {
165 return nullptr;
166 }
167
168 /* Texture storage of images is defined by the alpha mode of the image. The
169 * downside of this is that there can be artifacts near alpha edges. However,
170 * this allows us to use sRGB texture formats and preserves color values in
171 * zero alpha areas, and appears generally closer to what game engines that we
172 * want to be compatible with do. */
173 if (is_grayscale) {
174 /* Convert to byte buffer to then pack as half floats reducing the buffer size by half. */
176 (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
177 is_float_rect = true;
178 }
179 else {
181 (uchar *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
182 }
183 }
184 else {
185 /* Other colorspace, store as float texture to avoid precision loss. */
186 data_rect = MEM_mallocN(sizeof(float[4]) * ibuf->x * ibuf->y, __func__);
187 *r_freedata = freedata = true;
188 is_float_rect = true;
189
190 if (data_rect == nullptr) {
191 return nullptr;
192 }
193
194 /* Texture storage of images is defined by the alpha mode of the image. The
195 * downside of this is that there can be artifacts near alpha edges. However,
196 * this allows us to use sRGB texture formats and preserves color values in
197 * zero alpha areas, and appears generally closer to what game engines that we
198 * want to be compatible with do. */
200 (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
201 }
202 }
203
204 if (do_rescale) {
205 const uint8_t *rect = (is_float_rect) ? nullptr : (uint8_t *)data_rect;
206 const float *rect_float = (is_float_rect) ? (float *)data_rect : nullptr;
207
208 ImBuf *scale_ibuf = IMB_allocFromBuffer(rect, rect_float, ibuf->x, ibuf->y, 4);
209 IMB_scale(scale_ibuf, UNPACK2(rescale_size), IMBScaleFilter::Box, false);
210
211 if (freedata) {
212 MEM_freeN(data_rect);
213 }
214
215 data_rect = (is_float_rect) ? (void *)scale_ibuf->float_buffer.data :
216 (void *)scale_ibuf->byte_buffer.data;
217 *r_freedata = freedata = true;
218 /* Steal the rescaled buffer to avoid double free. */
219 (void)IMB_steal_byte_buffer(scale_ibuf);
220 (void)IMB_steal_float_buffer(scale_ibuf);
221 IMB_freeImBuf(scale_ibuf);
222 }
223
224 /* Pack first channel data manually at the start of the buffer. */
225 if (is_grayscale) {
226 void *src_rect = data_rect;
227
228 if (freedata == false) {
229 data_rect = MEM_mallocN((is_float_rect ? sizeof(float) : sizeof(uchar)) * ibuf->x * ibuf->y,
230 __func__);
231 *r_freedata = freedata = true;
232 }
233
234 if (data_rect == nullptr) {
235 return nullptr;
236 }
237
238 int buffer_size = do_rescale ? rescale_size[0] * rescale_size[1] : ibuf->x * ibuf->y;
239 if (is_float_rect) {
240 for (uint64_t i = 0; i < buffer_size; i++) {
241 ((float *)data_rect)[i] = ((float *)src_rect)[i * 4];
242 }
243 }
244 else {
245 for (uint64_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
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) {
308 MEM_freeN(data);
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;
318 int size[2] = {GPU_texture_size_with_limit(ibuf->x), GPU_texture_size_with_limit(ibuf->y)};
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 /* Fallback 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) {
389 MEM_freeN(data);
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:50
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_SRGB8_A8
@ GPU_SRGB8_A8_DXT5
@ GPU_SRGB8_A8_DXT1
@ GPU_RGBA8_DXT1
@ GPU_SRGB8_A8_DXT3
@ GPU_RGBA8_DXT3
@ GPU_R8
@ GPU_RGBA8_DXT5
void GPU_texture_update(GPUTexture *texture, eGPUDataFormat data_format, const void *data)
void GPU_texture_swizzle_set(GPUTexture *texture, const char swizzle[4])
bool IMB_colormanagement_space_is_data(ColorSpace *colorspace)
bool IMB_colormanagement_space_is_srgb(ColorSpace *colorspace)
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(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)
float * IMB_steal_float_buffer(ImBuf *ibuf)
uint8_t * IMB_steal_byte_buffer(ImBuf *ibuf)
bool IMB_scale(ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded=true)
Definition scaling.cc:779
@ IMB_FTYPE_DDS
Contains defines and structs used throughout the imbuf module.
#define FOURCC_DXT5
#define FOURCC_DXT1
@ IB_halffloat
#define FOURCC_DXT3
Read Guarded memory(de)allocation.
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
additional_info("compositor_sum_float_shared") .push_constant(Type additional_info("compositor_sum_float_shared") .push_constant(Type GPU_RGBA32F
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
RAYTRACE_GROUP_SIZE additional_info("eevee_shared", "eevee_gbuffer_data", "eevee_global_ubo", "eevee_sampling_data", "eevee_utility_texture", "eevee_hiz_data", "draw_view") .specialization_constant(Type RAYTRACE_GROUP_SIZE in_sh_0_tx in_sh_2_tx screen_normal_tx GPU_RGBA8
struct ImBuf * IMB_allocFromBuffer(const uint8_t *, const float *, unsigned int, unsigned int, unsigned int)
void IMB_freeImBuf(ImBuf *)
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
unsigned char uint8_t
Definition stdint.h:78
unsigned __int64 uint64_t
Definition stdint.h:90
unsigned int nummipmaps
unsigned char * data
unsigned int fourcc
ColorSpace * colorspace
ColorSpace * colorspace
DDSData dds_data
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
unsigned char planes
enum eImbFileType ftype
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:92
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:86
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:118
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:54
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