forked from blender/blender
realize-depth #5
@ -15,6 +15,7 @@
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
#include "IMB_imbuf.hh"
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
@ -56,74 +57,6 @@ bool operator==(const CachedImageKey &a, const CachedImageKey &b)
|
||||
* 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. */
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
Image *image,
|
||||
ImageUser *image_user,
|
||||
@ -227,34 +210,12 @@ CachedImage::CachedImage(Context &context,
|
||||
context, image, image_user, pass_name);
|
||||
|
||||
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);
|
||||
texture_ = IMB_create_gpu_texture("Image Texture", image_buffer, true, is_premultiplied);
|
||||
ImBuf *linear_image_buffer = compute_linear_buffer(image_buffer);
|
||||
|
||||
texture_ = IMB_create_gpu_texture("Image Texture", linear_image_buffer, true, true);
|
||||
GPU_texture_update_mipmap_chain(texture_);
|
||||
|
||||
const eGPUTextureFormat original_format = GPU_texture_format(texture_);
|
||||
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");
|
||||
}
|
||||
|
||||
IMB_freeImBuf(linear_image_buffer);
|
||||
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_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.
|
||||
* 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_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
|
||||
* 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,
|
||||
};
|
||||
|
||||
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.
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
* Returns true of allocation succeeded, false otherwise. */
|
||||
template<class BufferType>
|
||||
@ -249,11 +270,7 @@ void IMB_freeImBuf(ImBuf *ibuf)
|
||||
IMB_free_gpu_textures(ibuf);
|
||||
IMB_metadata_free(ibuf->metadata);
|
||||
colormanage_cache_free(ibuf);
|
||||
|
||||
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);
|
||||
}
|
||||
imb_free_dds_buffer(ibuf->dds_data);
|
||||
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(
|
||||
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.data = (uchar *)malloc(ibuf->dds_data.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. */
|
||||
FlipDXTCImage(ibuf);
|
||||
|
Loading…
Reference in New Issue
Block a user