Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brecht@blender.org>2022-06-03 15:13:10 +0300
committerBrecht Van Lommel <brecht@blender.org>2022-06-03 16:25:23 +0300
commit9d8fb80f218f05f743943a289e2aad579e709058 (patch)
treeb16d985f28ddb0fd3d396e9713e624b96dae2b4f /source/blender/imbuf
parent4c4056579b4bcbae3af745692f3fe802e3b79fac (diff)
Eevee/Workbench: store 8 bit image textures as half float for some color spaces
Same as in Cycles, this is needed for some color space conversions that don't compress well to byte sRGB, like for example Filmic sRGB. Ref T68926
Diffstat (limited to 'source/blender/imbuf')
-rw-r--r--source/blender/imbuf/IMB_colormanagement.h1
-rw-r--r--source/blender/imbuf/intern/colormanagement.c133
-rw-r--r--source/blender/imbuf/intern/util_gpu.c65
3 files changed, 122 insertions, 77 deletions
diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h
index 75159e186e9..2f0d2f9b449 100644
--- a/source/blender/imbuf/IMB_colormanagement.h
+++ b/source/blender/imbuf/IMB_colormanagement.h
@@ -181,7 +181,6 @@ void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer,
int width,
int height,
const struct ImBuf *ibuf,
- bool compress_as_srgb,
bool store_premultiplied);
void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer,
int offset_x,
diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c
index 0a85b31ef7b..33873b5daa7 100644
--- a/source/blender/imbuf/intern/colormanagement.c
+++ b/source/blender/imbuf/intern/colormanagement.c
@@ -2211,21 +2211,14 @@ void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer,
const int width,
const int height,
const struct ImBuf *ibuf,
- const bool compress_as_srgb,
const bool store_premultiplied)
{
- /* Convert byte buffer for texture storage on the GPU. These have builtin
- * support for converting sRGB to linear, which allows us to store textures
- * without precision or performance loss at minimal memory usage. */
+ /* Byte buffer storage, only for sRGB and data texture since other
+ * color space conversions can't be done on the GPU. */
BLI_assert(ibuf->rect && ibuf->rect_float == NULL);
+ BLI_assert(IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace) ||
+ IMB_colormanagement_space_is_data(ibuf->rect_colorspace));
- OCIO_ConstCPUProcessorRcPtr *processor = NULL;
- if (compress_as_srgb && ibuf->rect_colorspace &&
- !IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace)) {
- processor = colorspace_to_scene_linear_cpu_processor(ibuf->rect_colorspace);
- }
-
- /* TODO(brecht): make this multi-threaded, or at least process in batches. */
const unsigned char *in_buffer = (unsigned char *)ibuf->rect;
const bool use_premultiply = IMB_alpha_affects_rgb(ibuf) && store_premultiplied;
@@ -2235,20 +2228,7 @@ void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer,
const unsigned char *in = in_buffer + in_offset * 4;
unsigned char *out = out_buffer + out_offset * 4;
- if (processor != NULL) {
- /* Convert to scene linear, to sRGB and premultiply. */
- for (int x = 0; x < width; x++, in += 4, out += 4) {
- float pixel[4];
- rgba_uchar_to_float(pixel, in);
- OCIO_cpuProcessorApplyRGB(processor, pixel);
- linearrgb_to_srgb_v3_v3(pixel, pixel);
- if (use_premultiply) {
- mul_v3_fl(pixel, pixel[3]);
- }
- rgba_float_to_uchar(out, pixel);
- }
- }
- else if (use_premultiply) {
+ if (use_premultiply) {
/* Premultiply only. */
for (int x = 0; x < width; x++, in += 4, out += 4) {
out[0] = (in[0] * in[3]) >> 8;
@@ -2279,43 +2259,80 @@ void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer,
{
/* Float texture are stored in scene linear color space, with premultiplied
* alpha depending on the image alpha mode. */
- const float *in_buffer = ibuf->rect_float;
- const int in_channels = ibuf->channels;
- const bool use_unpremultiply = IMB_alpha_affects_rgb(ibuf) && !store_premultiplied;
-
- for (int y = 0; y < height; y++) {
- const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
- const size_t out_offset = y * width;
- const float *in = in_buffer + in_offset * in_channels;
- float *out = out_buffer + out_offset * 4;
-
- if (in_channels == 1) {
- /* Copy single channel. */
- for (int x = 0; x < width; x++, in += 1, out += 4) {
- out[0] = in[0];
- out[1] = in[0];
- out[2] = in[0];
- out[3] = in[0];
+ if (ibuf->rect_float) {
+ /* Float source buffer. */
+ const float *in_buffer = ibuf->rect_float;
+ const int in_channels = ibuf->channels;
+ const bool use_unpremultiply = IMB_alpha_affects_rgb(ibuf) && !store_premultiplied;
+
+ for (int y = 0; y < height; y++) {
+ const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
+ const size_t out_offset = y * width;
+ const float *in = in_buffer + in_offset * in_channels;
+ float *out = out_buffer + out_offset * 4;
+
+ if (in_channels == 1) {
+ /* Copy single channel. */
+ for (int x = 0; x < width; x++, in += 1, out += 4) {
+ out[0] = in[0];
+ out[1] = in[0];
+ out[2] = in[0];
+ out[3] = in[0];
+ }
}
- }
- else if (in_channels == 3) {
- /* Copy RGB. */
- for (int x = 0; x < width; x++, in += 3, out += 4) {
- out[0] = in[0];
- out[1] = in[1];
- out[2] = in[2];
- out[3] = 1.0f;
+ else if (in_channels == 3) {
+ /* Copy RGB. */
+ for (int x = 0; x < width; x++, in += 3, out += 4) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = 1.0f;
+ }
}
- }
- else if (in_channels == 4) {
- /* Copy or convert RGBA. */
- if (use_unpremultiply) {
- for (int x = 0; x < width; x++, in += 4, out += 4) {
- premul_to_straight_v4_v4(out, in);
+ else if (in_channels == 4) {
+ /* Copy or convert RGBA. */
+ if (use_unpremultiply) {
+ for (int x = 0; x < width; x++, in += 4, out += 4) {
+ premul_to_straight_v4_v4(out, in);
+ }
+ }
+ else {
+ memcpy(out, in, sizeof(float[4]) * width);
}
}
- else {
- memcpy(out, in, sizeof(float[4]) * width);
+ }
+ }
+ else {
+ /* Byte source buffer. */
+ const unsigned char *in_buffer = (unsigned char *)ibuf->rect;
+ const bool use_premultiply = IMB_alpha_affects_rgb(ibuf) && store_premultiplied;
+
+ /* TODO(brecht): make this multi-threaded, or at least process in batches. */
+ OCIO_ConstCPUProcessorRcPtr *processor = (ibuf->rect_colorspace) ?
+ colorspace_to_scene_linear_cpu_processor(
+ ibuf->rect_colorspace) :
+ NULL;
+
+ for (int y = 0; y < height; y++) {
+ const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
+ const size_t out_offset = y * width;
+ const unsigned char *in = in_buffer + in_offset * 4;
+ float *out = out_buffer + out_offset * 4;
+
+ /* Convert to scene linear, to sRGB and premultiply. */
+ for (int x = 0; x < width; x++, in += 4, out += 4) {
+ float pixel[4];
+ rgba_uchar_to_float(pixel, in);
+ if (processor) {
+ OCIO_cpuProcessorApplyRGB(processor, pixel);
+ }
+ else {
+ srgb_to_linearrgb_v3_v3(pixel, pixel);
+ }
+ if (use_premultiply) {
+ mul_v3_fl(pixel, pixel[3]);
+ }
+ copy_v4_v4(out, pixel);
}
}
}
diff --git a/source/blender/imbuf/intern/util_gpu.c b/source/blender/imbuf/intern/util_gpu.c
index 8e004938a89..5feb0ceb515 100644
--- a/source/blender/imbuf/intern/util_gpu.c
+++ b/source/blender/imbuf/intern/util_gpu.c
@@ -28,17 +28,30 @@ static void imb_gpu_get_format(const ImBuf *ibuf,
eGPUTextureFormat *r_texture_format)
{
const bool float_rect = (ibuf->rect_float != NULL);
- const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace) &&
- !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace));
- high_bitdepth = (!(ibuf->flags & IB_halffloat) && high_bitdepth);
-
- *r_data_format = (float_rect) ? GPU_DATA_FLOAT : GPU_DATA_UBYTE;
if (float_rect) {
- *r_texture_format = high_bitdepth ? GPU_RGBA32F : GPU_RGBA16F;
+ /* Float. */
+ const bool use_high_bitdepth = (!(ibuf->flags & IB_halffloat) && high_bitdepth);
+ *r_data_format = GPU_DATA_FLOAT;
+ *r_texture_format = use_high_bitdepth ? GPU_RGBA32F : GPU_RGBA16F;
}
else {
- *r_texture_format = use_srgb ? GPU_SRGB8_A8 : GPU_RGBA8;
+ if (IMB_colormanagement_space_is_data(ibuf->rect_colorspace) ||
+ IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)) {
+ /* Non-color data or scene linear, just store buffer as is. */
+ *r_data_format = GPU_DATA_UBYTE;
+ *r_texture_format = GPU_RGBA8;
+ }
+ else if (IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace)) {
+ /* sRGB, store as byte texture that the GPU can decode directly. */
+ *r_data_format = GPU_DATA_UBYTE;
+ *r_texture_format = GPU_SRGB8_A8;
+ }
+ else {
+ /* Other colorspace, store as half float texture to avoid precision loss. */
+ *r_data_format = GPU_DATA_FLOAT;
+ *r_texture_format = GPU_RGBA16F;
+ }
}
}
@@ -74,7 +87,6 @@ static bool IMB_gpu_get_compressed_format(const ImBuf *ibuf, eGPUTextureFormat *
static void *imb_gpu_get_data(const ImBuf *ibuf,
const bool do_rescale,
const int rescale_size[2],
- const bool compress_as_srgb,
const bool store_premultiplied,
bool *r_freedata)
{
@@ -99,14 +111,16 @@ static void *imb_gpu_get_data(const ImBuf *ibuf,
}
}
else {
- /* Byte image is in original colorspace from the file. If the file is sRGB
- * scene linear, or non-color data no conversion is needed. Otherwise we
- * compress as scene linear + sRGB transfer function to avoid precision loss
- * in common cases.
+ /* Byte image is in original colorspace from the file, and may need conversion.
*
* We must also convert to premultiplied for correct texture interpolation
* and consistency with float images. */
- if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) {
+ if (IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) {
+ /* Non-color data, just store buffer as is. */
+ }
+ else if (IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace) ||
+ IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)) {
+ /* sRGB or scene linear, store as byte texture that the GPU can decode directly. */
data_rect = MEM_mallocN(sizeof(uchar[4]) * ibuf->x * ibuf->y, __func__);
*r_freedata = freedata = true;
@@ -120,7 +134,24 @@ static void *imb_gpu_get_data(const ImBuf *ibuf,
* zero alpha areas, and appears generally closer to what game engines that we
* want to be compatible with do. */
IMB_colormanagement_imbuf_to_byte_texture(
- (uchar *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, compress_as_srgb, store_premultiplied);
+ (uchar *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
+ }
+ else {
+ /* Other colorspace, store as float texture to avoid precision loss. */
+ data_rect = MEM_mallocN(sizeof(float[4]) * ibuf->x * ibuf->y, __func__);
+ *r_freedata = freedata = true;
+
+ if (data_rect == NULL) {
+ return NULL;
+ }
+
+ /* Texture storage of images is defined by the alpha mode of the image. The
+ * downside of this is that there can be artifacts near alpha edges. However,
+ * this allows us to use sRGB texture formats and preserves color values in
+ * zero alpha areas, and appears generally closer to what game engines that we
+ * want to be compatible with do. */
+ IMB_colormanagement_imbuf_to_float_texture(
+ (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
}
}
@@ -181,10 +212,9 @@ void IMB_update_gpu_texture_sub(GPUTexture *tex,
eGPUTextureFormat tex_format;
imb_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format);
- const bool compress_as_srgb = (tex_format == GPU_SRGB8_A8);
bool freebuf = false;
- void *data = imb_gpu_get_data(ibuf, do_rescale, size, compress_as_srgb, use_premult, &freebuf);
+ void *data = imb_gpu_get_data(ibuf, do_rescale, size, use_premult, &freebuf);
/* Update Texture. */
GPU_texture_update_sub(tex, data_format, data, x, y, z, w, h, 1);
@@ -238,7 +268,6 @@ GPUTexture *IMB_create_gpu_texture(const char *name,
eGPUTextureFormat tex_format;
imb_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format);
- const bool compress_as_srgb = (tex_format == GPU_SRGB8_A8);
bool freebuf = false;
/* Create Texture. */
@@ -250,7 +279,7 @@ GPUTexture *IMB_create_gpu_texture(const char *name,
do_rescale = true;
}
BLI_assert(tex != NULL);
- void *data = imb_gpu_get_data(ibuf, do_rescale, size, compress_as_srgb, use_premult, &freebuf);
+ void *data = imb_gpu_get_data(ibuf, do_rescale, size, use_premult, &freebuf);
GPU_texture_update(tex, data_format, data);
GPU_texture_anisotropic_filter(tex, true);