forked from blender/blender
realize-depth #5
@ -15,6 +15,7 @@
|
|||||||
#include "GPU_shader.hh"
|
#include "GPU_shader.hh"
|
||||||
#include "GPU_texture.hh"
|
#include "GPU_texture.hh"
|
||||||
|
|
||||||
|
#include "IMB_colormanagement.hh"
|
||||||
#include "IMB_imbuf.hh"
|
#include "IMB_imbuf.hh"
|
||||||
#include "IMB_imbuf_types.hh"
|
#include "IMB_imbuf_types.hh"
|
||||||
|
|
||||||
@ -56,74 +57,6 @@ bool operator==(const CachedImageKey &a, const CachedImageKey &b)
|
|||||||
* Cached Image.
|
* Cached Image.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Returns a new texture of the given format and precision preprocessed using the given shader. The
|
|
||||||
* input texture is freed. */
|
|
||||||
static GPUTexture *preprocess_texture(Context &context,
|
|
||||||
GPUTexture *input_texture,
|
|
||||||
eGPUTextureFormat target_format,
|
|
||||||
ResultPrecision precision,
|
|
||||||
const char *shader_name)
|
|
||||||
{
|
|
||||||
const int2 size = int2(GPU_texture_width(input_texture), GPU_texture_height(input_texture));
|
|
||||||
|
|
||||||
GPUTexture *preprocessed_texture = GPU_texture_create_2d(
|
|
||||||
"Cached Image", size.x, size.y, 1, target_format, GPU_TEXTURE_USAGE_GENERAL, nullptr);
|
|
||||||
|
|
||||||
GPUShader *shader = context.get_shader(shader_name, precision);
|
|
||||||
GPU_shader_bind(shader);
|
|
||||||
|
|
||||||
const int input_unit = GPU_shader_get_sampler_binding(shader, "input_tx");
|
|
||||||
GPU_texture_bind(input_texture, input_unit);
|
|
||||||
|
|
||||||
const int image_unit = GPU_shader_get_sampler_binding(shader, "output_img");
|
|
||||||
GPU_texture_image_bind(preprocessed_texture, image_unit);
|
|
||||||
|
|
||||||
compute_dispatch_threads_at_least(shader, size);
|
|
||||||
|
|
||||||
GPU_shader_unbind();
|
|
||||||
GPU_texture_unbind(input_texture);
|
|
||||||
GPU_texture_image_unbind(preprocessed_texture);
|
|
||||||
GPU_texture_free(input_texture);
|
|
||||||
|
|
||||||
return preprocessed_texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compositor images are expected to be always pre-multiplied, so identify if the GPU texture
|
|
||||||
* returned by the IMB module is straight and needs to be pre-multiplied. An exception is when
|
|
||||||
* the image has an alpha mode of channel packed or alpha ignore, in which case, we always ignore
|
|
||||||
* pre-multiplication. */
|
|
||||||
static bool should_premultiply_alpha(Image *image, ImBuf *image_buffer)
|
|
||||||
{
|
|
||||||
if (ELEM(image->alpha_mode, IMA_ALPHA_CHANNEL_PACKED, IMA_ALPHA_IGNORE)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !BKE_image_has_gpu_texture_premultiplied_alpha(image, image_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get a suitable texture format supported by the compositor given the format of the texture
|
|
||||||
* returned by the IMB module. See imb_gpu_get_format for the formats that needs to be handled. */
|
|
||||||
static eGPUTextureFormat get_compatible_texture_format(eGPUTextureFormat original_format)
|
|
||||||
{
|
|
||||||
switch (original_format) {
|
|
||||||
case GPU_R16F:
|
|
||||||
case GPU_R32F:
|
|
||||||
case GPU_RGBA16F:
|
|
||||||
case GPU_RGBA32F:
|
|
||||||
return original_format;
|
|
||||||
case GPU_R8:
|
|
||||||
return GPU_R16F;
|
|
||||||
case GPU_RGBA8:
|
|
||||||
case GPU_SRGB8_A8:
|
|
||||||
return GPU_RGBA16F;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
BLI_assert_unreachable();
|
|
||||||
return original_format;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the selected render layer selected assuming the image is a multilayer image. */
|
/* Get the selected render layer selected assuming the image is a multilayer image. */
|
||||||
static RenderLayer *get_render_layer(Image *image, ImageUser &image_user)
|
static RenderLayer *get_render_layer(Image *image, ImageUser &image_user)
|
||||||
{
|
{
|
||||||
@ -205,6 +138,56 @@ static ImageUser compute_image_user_for_pass(Context &context,
|
|||||||
return image_user_for_pass;
|
return image_user_for_pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The image buffer might be stored as an sRGB 8-bit image, while the compositor expects linear
|
||||||
|
* float images, so compute a linear float buffer for the image buffer. This will also do linear
|
||||||
|
* space conversion and alpha pre-multiplication as needed. We could store those images in sRGB GPU
|
||||||
|
* textures and let the GPU do the linear space conversion, but the issues is that we don't control
|
||||||
|
* how the GPU does the conversion and so we get tiny differences across CPU and GPU compositing,
|
||||||
|
* and potentially even across GPUs/Drivers. Further, if alpha pre-multiplication is needed, we
|
||||||
|
* would need to do it ourself, which means alpha pre-multiplication will happen before linear
|
||||||
|
* space conversion, which would produce yet another difference. So we just do everything on the
|
||||||
|
* CPU, since this is already a cached resource.
|
||||||
|
*
|
||||||
|
* To avoid conflicts with other threads, create a new image buffer and assign all the necessary
|
||||||
|
* information to it, with IB_DO_NOT_TAKE_OWNERSHIP for buffers since a deep copy is not needed.
|
||||||
|
*
|
||||||
|
* The caller should free the returned image buffer. */
|
||||||
|
static ImBuf *compute_linear_buffer(ImBuf *image_buffer)
|
||||||
|
{
|
||||||
|
/* Do not pass the flags to the allocation function to avoid buffer allocation, but assign them
|
||||||
|
* after to retain important information like precision and alpha mode. */
|
||||||
|
ImBuf *linear_image_buffer = IMB_allocImBuf(
|
||||||
|
image_buffer->x, image_buffer->y, image_buffer->planes, 0);
|
||||||
|
linear_image_buffer->flags = image_buffer->flags;
|
||||||
|
|
||||||
|
/* Assign the float buffer if it exists, as well as its number of channels. */
|
||||||
|
IMB_assign_float_buffer(
|
||||||
|
linear_image_buffer, image_buffer->float_buffer, IB_DO_NOT_TAKE_OWNERSHIP);
|
||||||
|
linear_image_buffer->channels = image_buffer->channels;
|
||||||
|
|
||||||
|
/* If no float buffer exists, assign it then compute a float buffer from it. This is the main
|
||||||
|
* call of this function. */
|
||||||
|
if (!linear_image_buffer->float_buffer.data) {
|
||||||
|
IMB_assign_byte_buffer(
|
||||||
|
linear_image_buffer, image_buffer->byte_buffer, IB_DO_NOT_TAKE_OWNERSHIP);
|
||||||
|
IMB_float_from_rect(linear_image_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the image buffer contained compressed data, assign them as well, but only if the color
|
||||||
|
* space of the buffer is linear or data, since we need linear data and can't preprocess the
|
||||||
|
* compressed buffer. If not, we fallback to the float buffer already assigned, which is
|
||||||
|
* guaranteed to exist as a fallback for compressed textures. */
|
||||||
|
const bool is_suitable_compressed_color_space =
|
||||||
|
IMB_colormanagement_space_is_data(image_buffer->byte_buffer.colorspace) ||
|
||||||
|
IMB_colormanagement_space_is_scene_linear(image_buffer->byte_buffer.colorspace);
|
||||||
|
if (image_buffer->ftype == IMB_FTYPE_DDS && is_suitable_compressed_color_space) {
|
||||||
|
linear_image_buffer->ftype = IMB_FTYPE_DDS;
|
||||||
|
IMB_assign_dds_data(linear_image_buffer, image_buffer->dds_data, IB_DO_NOT_TAKE_OWNERSHIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
return linear_image_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
CachedImage::CachedImage(Context &context,
|
CachedImage::CachedImage(Context &context,
|
||||||
Image *image,
|
Image *image,
|
||||||
ImageUser *image_user,
|
ImageUser *image_user,
|
||||||
@ -227,34 +210,12 @@ CachedImage::CachedImage(Context &context,
|
|||||||
context, image, image_user, pass_name);
|
context, image, image_user, pass_name);
|
||||||
|
|
||||||
ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &image_user_for_pass, nullptr);
|
ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &image_user_for_pass, nullptr);
|
||||||
const bool is_premultiplied = BKE_image_has_gpu_texture_premultiplied_alpha(image, image_buffer);
|
ImBuf *linear_image_buffer = compute_linear_buffer(image_buffer);
|
||||||
texture_ = IMB_create_gpu_texture("Image Texture", image_buffer, true, is_premultiplied);
|
|
||||||
|
texture_ = IMB_create_gpu_texture("Image Texture", linear_image_buffer, true, true);
|
||||||
GPU_texture_update_mipmap_chain(texture_);
|
GPU_texture_update_mipmap_chain(texture_);
|
||||||
|
|
||||||
const eGPUTextureFormat original_format = GPU_texture_format(texture_);
|
IMB_freeImBuf(linear_image_buffer);
|
||||||
const eGPUTextureFormat target_format = get_compatible_texture_format(original_format);
|
|
||||||
const ResultType result_type = Result::type(target_format);
|
|
||||||
const ResultPrecision precision = Result::precision(target_format);
|
|
||||||
|
|
||||||
/* The GPU image returned by the IMB module can be in a format not supported by the compositor,
|
|
||||||
* or it might need pre-multiplication, so preprocess them first. */
|
|
||||||
if (result_type == ResultType::Color && should_premultiply_alpha(image, image_buffer)) {
|
|
||||||
texture_ = preprocess_texture(
|
|
||||||
context, texture_, target_format, precision, "compositor_premultiply_alpha");
|
|
||||||
}
|
|
||||||
else if (original_format != target_format) {
|
|
||||||
const char *conversion_shader_name = result_type == ResultType::Float ?
|
|
||||||
"compositor_convert_float_to_float" :
|
|
||||||
"compositor_convert_color_to_color";
|
|
||||||
texture_ = preprocess_texture(
|
|
||||||
context, texture_, target_format, precision, conversion_shader_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the alpha to 1 using swizzling if alpha is ignored. */
|
|
||||||
if (result_type == ResultType::Color && image->alpha_mode == IMA_ALPHA_IGNORE) {
|
|
||||||
GPU_texture_swizzle_set(texture_, "rgb1");
|
|
||||||
}
|
|
||||||
|
|
||||||
BKE_image_release_ibuf(image, image_buffer, nullptr);
|
BKE_image_release_ibuf(image, image_buffer, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +118,21 @@ ImBuf *IMB_allocFromBuffer(const uint8_t *byte_buffer,
|
|||||||
void IMB_assign_byte_buffer(ImBuf *ibuf, uint8_t *buffer_data, ImBufOwnership ownership);
|
void IMB_assign_byte_buffer(ImBuf *ibuf, uint8_t *buffer_data, ImBufOwnership ownership);
|
||||||
void IMB_assign_float_buffer(ImBuf *ibuf, float *buffer_data, ImBufOwnership ownership);
|
void IMB_assign_float_buffer(ImBuf *ibuf, float *buffer_data, ImBufOwnership ownership);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign the content and the color space of the corresponding buffer the data from the given
|
||||||
|
* buffer.
|
||||||
|
*
|
||||||
|
* \note Does not modify the topology (width, height, number of channels)
|
||||||
|
* or the mipmaps in any way.
|
||||||
|
*
|
||||||
|
* \note The ownership of the data in the source buffer is ignored.
|
||||||
|
*/
|
||||||
|
void IMB_assign_byte_buffer(ImBuf *ibuf, const ImBufByteBuffer &buffer, ImBufOwnership ownership);
|
||||||
|
void IMB_assign_float_buffer(ImBuf *ibuf,
|
||||||
|
const ImBufFloatBuffer &buffer,
|
||||||
|
ImBufOwnership ownership);
|
||||||
|
void IMB_assign_dds_data(ImBuf *ibuf, const DDSData &data, ImBufOwnership ownership);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make corresponding buffers available for modification.
|
* Make corresponding buffers available for modification.
|
||||||
* Is achieved by ensuring that the given ImBuf is the only owner of the underlying buffer data.
|
* Is achieved by ensuring that the given ImBuf is the only owner of the underlying buffer data.
|
||||||
|
@ -34,17 +34,6 @@ struct IDProperty;
|
|||||||
#define IMB_MIPMAP_LEVELS 20
|
#define IMB_MIPMAP_LEVELS 20
|
||||||
#define IMB_FILEPATH_SIZE 1024
|
#define IMB_FILEPATH_SIZE 1024
|
||||||
|
|
||||||
struct DDSData {
|
|
||||||
/** DDS fourcc info */
|
|
||||||
unsigned int fourcc;
|
|
||||||
/** The number of mipmaps in the dds file */
|
|
||||||
unsigned int nummipmaps;
|
|
||||||
/** The compressed image data */
|
|
||||||
unsigned char *data;
|
|
||||||
/** The size of the compressed data */
|
|
||||||
unsigned int size;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \ingroup imbuf
|
* \ingroup imbuf
|
||||||
* This is the abstraction of an image. ImBuf is the basic type used for all imbuf operations.
|
* This is the abstraction of an image. ImBuf is the basic type used for all imbuf operations.
|
||||||
@ -143,6 +132,19 @@ enum ImBufOwnership {
|
|||||||
IB_TAKE_OWNERSHIP = 1,
|
IB_TAKE_OWNERSHIP = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DDSData {
|
||||||
|
/** DDS fourcc info */
|
||||||
|
unsigned int fourcc;
|
||||||
|
/** The number of mipmaps in the dds file */
|
||||||
|
unsigned int nummipmaps;
|
||||||
|
/** The compressed image data */
|
||||||
|
unsigned char *data;
|
||||||
|
/** The size of the compressed data */
|
||||||
|
unsigned int size;
|
||||||
|
/** Who owns the data buffer. */
|
||||||
|
ImBufOwnership ownership;
|
||||||
|
};
|
||||||
|
|
||||||
/* Different storage specialization.
|
/* Different storage specialization.
|
||||||
*
|
*
|
||||||
* NOTE: Avoid direct assignments and allocations, use the buffer utilities from the IMB_imbuf.hh
|
* NOTE: Avoid direct assignments and allocations, use the buffer utilities from the IMB_imbuf.hh
|
||||||
|
@ -84,6 +84,27 @@ template<class BufferType> static void imb_free_buffer(BufferType &buffer)
|
|||||||
buffer.ownership = IB_DO_NOT_TAKE_OWNERSHIP;
|
buffer.ownership = IB_DO_NOT_TAKE_OWNERSHIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Free the specified DDS buffer storage, freeing memory when needed and restoring the state of the
|
||||||
|
* buffer to its defaults. */
|
||||||
|
static void imb_free_dds_buffer(DDSData &dds_data)
|
||||||
|
{
|
||||||
|
if (dds_data.data) {
|
||||||
|
switch (dds_data.ownership) {
|
||||||
|
case IB_DO_NOT_TAKE_OWNERSHIP:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IB_TAKE_OWNERSHIP:
|
||||||
|
/* dds_data.data is allocated by DirectDrawSurface::readData(), so don't use MEM_freeN! */
|
||||||
|
free(dds_data.data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset buffer to defaults. */
|
||||||
|
dds_data.data = nullptr;
|
||||||
|
dds_data.ownership = IB_DO_NOT_TAKE_OWNERSHIP;
|
||||||
|
}
|
||||||
|
|
||||||
/* Allocate pixel storage of the given buffer. The buffer owns the allocated memory.
|
/* Allocate pixel storage of the given buffer. The buffer owns the allocated memory.
|
||||||
* Returns true of allocation succeeded, false otherwise. */
|
* Returns true of allocation succeeded, false otherwise. */
|
||||||
template<class BufferType>
|
template<class BufferType>
|
||||||
@ -249,11 +270,7 @@ void IMB_freeImBuf(ImBuf *ibuf)
|
|||||||
IMB_free_gpu_textures(ibuf);
|
IMB_free_gpu_textures(ibuf);
|
||||||
IMB_metadata_free(ibuf->metadata);
|
IMB_metadata_free(ibuf->metadata);
|
||||||
colormanage_cache_free(ibuf);
|
colormanage_cache_free(ibuf);
|
||||||
|
imb_free_dds_buffer(ibuf->dds_data);
|
||||||
if (ibuf->dds_data.data != nullptr) {
|
|
||||||
/* dds_data.data is allocated by DirectDrawSurface::readData(), so don't use MEM_freeN! */
|
|
||||||
free(ibuf->dds_data.data);
|
|
||||||
}
|
|
||||||
MEM_freeN(ibuf);
|
MEM_freeN(ibuf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -472,6 +489,32 @@ void IMB_assign_float_buffer(ImBuf *ibuf, float *buffer_data, const ImBufOwnersh
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IMB_assign_byte_buffer(ImBuf *ibuf,
|
||||||
|
const ImBufByteBuffer &buffer,
|
||||||
|
const ImBufOwnership ownership)
|
||||||
|
{
|
||||||
|
IMB_assign_byte_buffer(ibuf, buffer.data, ownership);
|
||||||
|
ibuf->byte_buffer.colorspace = buffer.colorspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IMB_assign_float_buffer(ImBuf *ibuf,
|
||||||
|
const ImBufFloatBuffer &buffer,
|
||||||
|
const ImBufOwnership ownership)
|
||||||
|
{
|
||||||
|
IMB_assign_float_buffer(ibuf, buffer.data, ownership);
|
||||||
|
ibuf->float_buffer.colorspace = buffer.colorspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IMB_assign_dds_data(ImBuf *ibuf, const DDSData &data, const ImBufOwnership ownership)
|
||||||
|
{
|
||||||
|
BLI_assert(ibuf->ftype == IMB_FTYPE_DDS);
|
||||||
|
|
||||||
|
imb_free_dds_buffer(ibuf->dds_data);
|
||||||
|
|
||||||
|
ibuf->dds_data = data;
|
||||||
|
ibuf->dds_data.ownership = ownership;
|
||||||
|
}
|
||||||
|
|
||||||
ImBuf *IMB_allocFromBufferOwn(
|
ImBuf *IMB_allocFromBufferOwn(
|
||||||
uint8_t *byte_buffer, float *float_buffer, uint w, uint h, uint channels)
|
uint8_t *byte_buffer, float *float_buffer, uint w, uint h, uint channels)
|
||||||
{
|
{
|
||||||
|
@ -330,6 +330,7 @@ static void LoadDXTCImage(ImBuf *ibuf, Filesystem::IOMemReader &mem_reader)
|
|||||||
ibuf->dds_data.size = mem_reader.size() - dds_header_size;
|
ibuf->dds_data.size = mem_reader.size() - dds_header_size;
|
||||||
ibuf->dds_data.data = (uchar *)malloc(ibuf->dds_data.size);
|
ibuf->dds_data.data = (uchar *)malloc(ibuf->dds_data.size);
|
||||||
mem_reader.pread(ibuf->dds_data.data, ibuf->dds_data.size, dds_header_size);
|
mem_reader.pread(ibuf->dds_data.data, ibuf->dds_data.size, dds_header_size);
|
||||||
|
ibuf->dds_data.ownership = IB_TAKE_OWNERSHIP;
|
||||||
|
|
||||||
/* Flip compressed image data to match OpenGL convention. */
|
/* Flip compressed image data to match OpenGL convention. */
|
||||||
FlipDXTCImage(ibuf);
|
FlipDXTCImage(ibuf);
|
||||||
|
Loading…
Reference in New Issue
Block a user