diff options
10 files changed, 117 insertions, 47 deletions
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 90aa09430ae..451ab43367b 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -1400,7 +1400,7 @@ char BKE_imtype_valid_depths(const char imtype) case R_IMF_IMTYPE_OPENEXR: return R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_32; case R_IMF_IMTYPE_MULTILAYER: - return R_IMF_CHAN_DEPTH_32; + return R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_32; /* eeh, cineon does some strange 10bits per channel */ case R_IMF_IMTYPE_DPX: return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_10 | R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16; diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cpp b/source/blender/compositor/nodes/COM_OutputFileNode.cpp index acd2602e216..42805d1ff37 100644 --- a/source/blender/compositor/nodes/COM_OutputFileNode.cpp +++ b/source/blender/compositor/nodes/COM_OutputFileNode.cpp @@ -47,18 +47,21 @@ void OutputFileNode::convertToOperations(NodeConverter &converter, const Composi */ return; } - + if (storage->format.imtype == R_IMF_IMTYPE_MULTILAYER) { + const bool use_half_float = (storage->format.depth == R_IMF_CHAN_DEPTH_16); /* single output operation for the multilayer file */ OutputOpenExrMultiLayerOperation *outputOperation; if (is_multiview && storage->format.views_format == R_IMF_VIEWS_MULTIVIEW) { outputOperation = new OutputOpenExrMultiLayerMultiViewOperation( - context.getRenderData(), context.getbNodeTree(), storage->base_path, storage->format.exr_codec, context.getViewName()); + context.getRenderData(), context.getbNodeTree(), storage->base_path, + storage->format.exr_codec, use_half_float, context.getViewName()); } else { outputOperation = new OutputOpenExrMultiLayerOperation( - context.getRenderData(), context.getbNodeTree(), storage->base_path, storage->format.exr_codec, context.getViewName()); + context.getRenderData(), context.getbNodeTree(), storage->base_path, + storage->format.exr_codec, use_half_float, context.getViewName()); } converter.addOperation(outputOperation); diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp index e89af9cc15b..7786359c06a 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp @@ -74,7 +74,7 @@ void *OutputOpenExrSingleLayerMultiViewOperation::get_handle(const char *filenam continue; IMB_exr_add_view(exrhandle, srv->name); - add_exr_channels(exrhandle, NULL, this->m_datatype, srv->name, width, NULL); + add_exr_channels(exrhandle, NULL, this->m_datatype, srv->name, width, false, NULL); } BLI_make_existing_file(filename); @@ -108,7 +108,8 @@ void OutputOpenExrSingleLayerMultiViewOperation::deinitExecution() (this->m_rd->scemode & R_EXTENSION) != 0, true, NULL); exrhandle = this->get_handle(filename); - add_exr_channels(exrhandle, NULL, this->m_datatype, this->m_viewName, width, this->m_outputBuffer); + add_exr_channels(exrhandle, NULL, this->m_datatype, this->m_viewName, width, + this->m_format->depth == R_IMF_CHAN_DEPTH_16, this->m_outputBuffer); /* memory can only be freed after we write all views to the file */ this->m_outputBuffer = NULL; @@ -130,8 +131,9 @@ void OutputOpenExrSingleLayerMultiViewOperation::deinitExecution() /************************************ OpenEXR Multilayer Multiview *****************************************/ OutputOpenExrMultiLayerMultiViewOperation::OutputOpenExrMultiLayerMultiViewOperation( - const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, const char *viewName) - : OutputOpenExrMultiLayerOperation(rd, tree, path, exr_codec, viewName) + const RenderData *rd, const bNodeTree *tree, const char *path, + char exr_codec, bool exr_half_float, const char *viewName) + : OutputOpenExrMultiLayerOperation(rd, tree, path, exr_codec, exr_half_float, viewName) { } @@ -162,7 +164,8 @@ void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename IMB_exr_add_view(exrhandle, srv->name); for (unsigned int i = 0; i < this->m_layers.size(); ++i) - add_exr_channels(exrhandle, this->m_layers[i].name, this->m_layers[i].datatype, srv->name, width, NULL); + add_exr_channels(exrhandle, this->m_layers[i].name, this->m_layers[i].datatype, + srv->name, width, this->m_exr_half_float, NULL); } BLI_make_existing_file(filename); @@ -197,7 +200,8 @@ void OutputOpenExrMultiLayerMultiViewOperation::deinitExecution() exrhandle = this->get_handle(filename); for (unsigned int i = 0; i < this->m_layers.size(); ++i) - add_exr_channels(exrhandle, this->m_layers[i].name, this->m_layers[i].datatype, this->m_viewName, width, this->m_layers[i].outputBuffer); + add_exr_channels(exrhandle, this->m_layers[i].name, this->m_layers[i].datatype, this->m_viewName, + width, this->m_exr_half_float, this->m_layers[i].outputBuffer); for (unsigned int i = 0; i < this->m_layers.size(); ++i) { /* memory can only be freed after we write all views to the file */ @@ -269,7 +273,8 @@ void OutputStereoOperation::deinitExecution() float *buf = this->m_outputBuffer; /* populate single EXR channel with view data */ - IMB_exr_add_channel(exrhandle, NULL, this->m_name, this->m_viewName, 1, this->m_channels * width * height, buf); + IMB_exr_add_channel(exrhandle, NULL, this->m_name, this->m_viewName, 1, this->m_channels * width * height, buf, + this->m_format->depth == R_IMF_CHAN_DEPTH_16); this->m_imageInput = NULL; this->m_outputBuffer = NULL; diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h index 25716fdb9e1..0e3cde66897 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h @@ -51,7 +51,8 @@ public: class OutputOpenExrMultiLayerMultiViewOperation : public OutputOpenExrMultiLayerOperation { private: public: - OutputOpenExrMultiLayerMultiViewOperation(const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, const char *viewName); + OutputOpenExrMultiLayerMultiViewOperation(const RenderData *rd, const bNodeTree *tree, const char *path, + char exr_codec, bool exr_half_float, const char *viewName); void *get_handle(const char *filename); void deinitExecution(); diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cpp b/source/blender/compositor/operations/COM_OutputFileOperation.cpp index b99b0d5b875..fdf5a0b48cf 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cpp +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cpp @@ -40,23 +40,24 @@ extern "C" { # include "IMB_imbuf_types.h" } -void add_exr_channels(void *exrhandle, const char *layerName, const DataType datatype, const char *viewName, const size_t width, float *buf) +void add_exr_channels(void *exrhandle, const char *layerName, const DataType datatype, + const char *viewName, const size_t width, bool use_half_float, float *buf) { /* create channels */ switch (datatype) { case COM_DT_VALUE: - IMB_exr_add_channel(exrhandle, layerName, "V", viewName, 1, width, buf ? buf : NULL); + IMB_exr_add_channel(exrhandle, layerName, "V", viewName, 1, width, buf ? buf : NULL, use_half_float); break; case COM_DT_VECTOR: - IMB_exr_add_channel(exrhandle, layerName, "X", viewName, 3, 3 * width, buf ? buf : NULL); - IMB_exr_add_channel(exrhandle, layerName, "Y", viewName, 3, 3 * width, buf ? buf + 1 : NULL); - IMB_exr_add_channel(exrhandle, layerName, "Z", viewName, 3, 3 * width, buf ? buf + 2 : NULL); + IMB_exr_add_channel(exrhandle, layerName, "X", viewName, 3, 3 * width, buf ? buf : NULL, use_half_float); + IMB_exr_add_channel(exrhandle, layerName, "Y", viewName, 3, 3 * width, buf ? buf + 1 : NULL, use_half_float); + IMB_exr_add_channel(exrhandle, layerName, "Z", viewName, 3, 3 * width, buf ? buf + 2 : NULL, use_half_float); break; case COM_DT_COLOR: - IMB_exr_add_channel(exrhandle, layerName, "R", viewName, 4, 4 * width, buf ? buf : NULL); - IMB_exr_add_channel(exrhandle, layerName, "G", viewName, 4, 4 * width, buf ? buf + 1 : NULL); - IMB_exr_add_channel(exrhandle, layerName, "B", viewName, 4, 4 * width, buf ? buf + 2 : NULL); - IMB_exr_add_channel(exrhandle, layerName, "A", viewName, 4, 4 * width, buf ? buf + 3 : NULL); + IMB_exr_add_channel(exrhandle, layerName, "R", viewName, 4, 4 * width, buf ? buf : NULL, use_half_float); + IMB_exr_add_channel(exrhandle, layerName, "G", viewName, 4, 4 * width, buf ? buf + 1 : NULL, use_half_float); + IMB_exr_add_channel(exrhandle, layerName, "B", viewName, 4, 4 * width, buf ? buf + 2 : NULL, use_half_float); + IMB_exr_add_channel(exrhandle, layerName, "A", viewName, 4, 4 * width, buf ? buf + 3 : NULL, use_half_float); break; default: break; @@ -227,13 +228,15 @@ OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_, bo } OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation( - const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, const char *viewName) + const RenderData *rd, const bNodeTree *tree, const char *path, + char exr_codec, bool exr_half_float, const char *viewName) { this->m_rd = rd; this->m_tree = tree; BLI_strncpy(this->m_path, path, sizeof(this->m_path)); this->m_exr_codec = exr_codec; + this->m_exr_half_float = exr_half_float; this->m_viewName = viewName; } @@ -284,7 +287,8 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() if (!layer.imageInput) continue; /* skip unconnected sockets */ - add_exr_channels(exrhandle, this->m_layers[i].name, this->m_layers[i].datatype, "", width, this->m_layers[i].outputBuffer); + add_exr_channels(exrhandle, this->m_layers[i].name, this->m_layers[i].datatype, "", width, + this->m_exr_half_float, this->m_layers[i].outputBuffer); } /* when the filename has no permissions, this can fail */ diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h index 58ebf055583..93468977a66 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileOperation.h @@ -85,11 +85,13 @@ protected: char m_path[FILE_MAX]; char m_exr_codec; + bool m_exr_half_float; LayerList m_layers; const char *m_viewName; public: - OutputOpenExrMultiLayerOperation(const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, const char *viewName); + OutputOpenExrMultiLayerOperation(const RenderData *rd, const bNodeTree *tree, const char *path, + char exr_codec, bool exr_half_float, const char *viewName); void add_layer(const char *name, DataType datatype, bool use_layer); @@ -102,7 +104,8 @@ public: bool isFileOutputOperation() const { return true; } }; -void add_exr_channels(void *exrhandle, const char *layerName, const DataType datatype, const char *viewName, const size_t width, float *buf); +void add_exr_channels(void *exrhandle, const char *layerName, const DataType datatype, const char *viewName, + const size_t width, bool use_half_float, float *buf); void free_exr_channels(void *exrhandle, const RenderData *rd, const char *layerName, const DataType datatype); int get_datatype_size(DataType datatype); diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index e805d3dc25c..72288149846 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -654,6 +654,8 @@ typedef struct ExrHandle { ListBase channels; /* flattened out, ExrChannel */ ListBase layers; /* hierarchical, pointing in end to ExrChannel */ + + int num_half_channels; /* used during filr save, allows faster temporary buffers allocation */ } ExrHandle; /* flattened out channel */ @@ -666,6 +668,7 @@ typedef struct ExrChannel { float *rect; /* first pointer to write in */ char chan_id; /* quick lookup of channel char */ int view_id; /* quick lookup of channel view */ + bool use_half_float; /* when saving use half float for file storage */ } ExrChannel; @@ -783,7 +786,10 @@ static const char *imb_exr_insert_view_name(const char *passname, const char *vi /* adds flattened ExrChannels */ /* xstride, ystride and rect can be done in set_channel too, for tile writing */ /* passname does not include view */ -void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, const char *viewname, int xstride, int ystride, float *rect) +void IMB_exr_add_channel(void *handle, + const char *layname, const char *passname, const char *viewname, + int xstride, int ystride, float *rect, + bool use_half_float) { ExrHandle *data = (ExrHandle *)handle; ExrChannel *echan; @@ -823,6 +829,11 @@ void IMB_exr_add_channel(void *handle, const char *layname, const char *passname echan->xstride = xstride; echan->ystride = ystride; echan->rect = rect; + echan->use_half_float = use_half_float; + + if (echan->use_half_float) { + data->num_half_channels++; + } exr_printf("added channel %s\n", echan->name); BLI_addtail(&data->channels, echan); @@ -840,8 +851,10 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh bool is_singlelayer, is_multilayer, is_multiview; - for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) - header.channels().insert(echan->name, Channel(Imf::FLOAT)); + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + header.channels().insert(echan->name, + Channel(echan->use_half_float ? Imf::HALF : Imf::FLOAT)); + } openexr_header_compression(&header, compress); BKE_stamp_info_callback(&header, stamp, openexr_header_metadata_callback); @@ -911,6 +924,8 @@ void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int /* assign channels */ for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + /* Tiles are expected to be saved with full float currently. */ + BLI_assert(echan->use_half_float == 0); echan->m->internal_name = echan->m->name; echan->m->part_number = echan->view_id; @@ -965,7 +980,7 @@ int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *heig GetChannelsInMultiPartFile(*data->ifile, channels); for (size_t i = 0; i < channels.size(); i++) { - IMB_exr_add_channel(data, NULL, channels[i].name.c_str(), channels[i].view.c_str(), 0, 0, NULL); + IMB_exr_add_channel(data, NULL, channels[i].name.c_str(), channels[i].view.c_str(), 0, 0, NULL, false); echan = (ExrChannel *)data->channels.last; echan->m->name = channels[i].name; @@ -1062,12 +1077,33 @@ void IMB_exr_write_channels(void *handle) ExrChannel *echan; if (data->channels.first) { - for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { - /* last scanline, stride negative */ - float *rect = echan->rect + echan->xstride * (data->height - 1) * data->width; + const size_t num_pixels = ((size_t)data->width) * data->height; + half *rect_half = NULL, *current_rect_half; + + /* We allocate teporary storage for half pixels for all the channels at once. */ + if (data->num_half_channels != 0) { + rect_half = (half*)MEM_mallocN(sizeof(half) * data->num_half_channels * num_pixels, __func__); + current_rect_half = rect_half; + } - frameBuffer.insert(echan->name, Slice(Imf::FLOAT, (char *)rect, - echan->xstride * sizeof(float), -echan->ystride * sizeof(float))); + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + /* Writting starts from last scanline, stride negative. */ + if (echan->use_half_float) { + float *rect = echan->rect; + half *cur = current_rect_half; + for (size_t i = 0; i < num_pixels; ++i, ++cur) { + *cur = rect[i * echan->xstride]; + } + half *rect_to_write = current_rect_half + (data->height - 1) * data->width; + frameBuffer.insert(echan->name, Slice(Imf::HALF, (char *)rect_to_write, + sizeof(half), -data->width * sizeof(half))); + current_rect_half += num_pixels; + } + else { + float *rect = echan->rect + echan->xstride * (data->height - 1) * data->width; + frameBuffer.insert(echan->name, Slice(Imf::FLOAT, (char *)rect, + echan->xstride * sizeof(float), -echan->ystride * sizeof(float))); + } } data->ofile->setFrameBuffer(frameBuffer); @@ -1077,6 +1113,10 @@ void IMB_exr_write_channels(void *handle) catch (const std::exception& exc) { std::cerr << "OpenEXR-writePixels: ERROR: " << exc.what() << std::endl; } + /* Free temporary buffers. */ + if (rect_half != NULL) { + MEM_freeN(rect_half); + } } else { printf("Error: attempt to save MultiLayer without layers.\n"); @@ -1518,7 +1558,7 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream, MultiPartInputFil imb_exr_get_views(*data->ifile, *data->multiView); for (size_t i = 0; i < channels.size(); i++) { - IMB_exr_add_channel(data, NULL, channels[i].name.c_str(), channels[i].view.c_str(), 0, 0, NULL); + IMB_exr_add_channel(data, NULL, channels[i].name.c_str(), channels[i].view.c_str(), 0, 0, NULL, false); echan = (ExrChannel *)data->channels.last; echan->m->name = channels[i].name; diff --git a/source/blender/imbuf/intern/openexr/openexr_multi.h b/source/blender/imbuf/intern/openexr/openexr_multi.h index 77fa420322e..97e8042caa6 100644 --- a/source/blender/imbuf/intern/openexr/openexr_multi.h +++ b/source/blender/imbuf/intern/openexr/openexr_multi.h @@ -52,7 +52,11 @@ struct StampData; void *IMB_exr_get_handle(void); void *IMB_exr_get_handle_name(const char *name); -void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, const char *view, int xstride, int ystride, float *rect); +void IMB_exr_add_channel(void *handle, + const char *layname, const char *passname, const char *view, + int xstride, int ystride, + float *rect, + bool use_half_float); int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height); int IMB_exr_begin_write(void *handle, const char *filename, int width, int height, int compress, const struct StampData *stamp); diff --git a/source/blender/imbuf/intern/openexr/openexr_stub.cpp b/source/blender/imbuf/intern/openexr/openexr_stub.cpp index 1a2ae7a97e1..3ad23456b9f 100644 --- a/source/blender/imbuf/intern/openexr/openexr_stub.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_stub.cpp @@ -35,7 +35,8 @@ void *IMB_exr_get_handle (void) {return NULL;} void *IMB_exr_get_handle_name (const char * /*name*/) { return NULL;} void IMB_exr_add_channel (void * /*handle*/, const char * /*layname*/, const char * /*passname*/, const char * /*view*/, - int /*xstride*/, int /*ystride*/, float * /*rect*/) { } + int /*xstride*/, int /*ystride*/, float * /*rect*/, + bool /*use_half_float*/) { } int IMB_exr_begin_read (void * /*handle*/, const char * /*filename*/, int * /*width*/, int * /*height*/) { return 0;} int IMB_exr_begin_write (void * /*handle*/, const char * /*filename*/, int /*width*/, int /*height*/, int /*compress*/, const struct StampData * /*stamp*/) { return 0;} diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c index ddd9859ce54..2af6a383145 100644 --- a/source/blender/render/intern/source/render_result.c +++ b/source/blender/render/intern/source/render_result.c @@ -502,7 +502,7 @@ static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int if (rl->exrhandle) { int a; for (a = 0; a < channels; a++) - IMB_exr_add_channel(rl->exrhandle, rl->name, name_from_passtype(passtype, a), viewname, 0, 0, NULL); + IMB_exr_add_channel(rl->exrhandle, rl->name, name_from_passtype(passtype, a), viewname, 0, 0, NULL, false); } else { float *rect; @@ -1038,6 +1038,7 @@ bool RE_WriteRenderResult(ReportList *reports, RenderResult *rr, const char *fil size_t width, height; const bool is_mono = view && !multiview; + const bool use_half_float = (imf->depth == R_IMF_CHAN_DEPTH_16); width = rr->rectx; height = rr->recty; @@ -1049,12 +1050,17 @@ bool RE_WriteRenderResult(ReportList *reports, RenderResult *rr, const char *fil IMB_exr_add_view(exrhandle, rview->name); if (rview->rectf) { - for (a = 0; a < 4; a++) + for (a = 0; a < 4; a++) { IMB_exr_add_channel(exrhandle, "", RGBAZ[a], - rview->name, 4, 4 * width, rview->rectf + a); - if (rview->rectz) + rview->name, 4, 4 * width, rview->rectf + a, + use_half_float); + } + if (rview->rectz) { + /* Z pass is always stored as float. */ IMB_exr_add_channel(exrhandle, "", RGBAZ[4], - rview->name, 1, width, rview->rectz); + rview->name, 1, width, rview->rectz, + false); + } } } } @@ -1074,9 +1080,11 @@ bool RE_WriteRenderResult(ReportList *reports, RenderResult *rr, const char *fil IMB_exr_add_view(exrhandle, rview->name); if (rview->rectf) { - for (a = 0; a < 4; a++) + for (a = 0; a < 4; a++) { IMB_exr_add_channel(exrhandle, "Composite", name_from_passtype(SCE_PASS_COMBINED, a), - chan_view, 4, 4 * width, rview->rectf + a); + chan_view, 4, 4 * width, rview->rectf + a, + use_half_float); + } } } @@ -1099,14 +1107,15 @@ bool RE_WriteRenderResult(ReportList *reports, RenderResult *rr, const char *fil } for (a = 0; a < xstride; a++) { - if (rpass->passtype) { IMB_exr_add_channel(exrhandle, rl->name, name_from_passtype(rpass->passtype, a), chan_view, - xstride, xstride * width, rpass->rect + a); + xstride, xstride * width, rpass->rect + a, + rpass->passtype == SCE_PASS_Z ? false : use_half_float); } else { IMB_exr_add_channel(exrhandle, rl->name, make_pass_name(rpass, a), chan_view, - xstride, xstride * width, rpass->rect + a); + xstride, xstride * width, rpass->rect + a, + use_half_float); } } } |