Refactor: Use ImBuf to store passes in RenderResult #109788

Merged
Sergey Sharybin merged 2 commits from Sergey/blender:render_result into main 2023-07-10 16:33:40 +02:00
25 changed files with 315 additions and 528 deletions

View File

@ -3987,12 +3987,9 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int e
if (ima->rr) {
RenderPass *rpass = BKE_image_multilayer_index(ima->rr, iuser);
if (rpass) {
// printf("load from pass %s\n", rpass->name);
ibuf = IMB_allocImBuf(ima->rr->rectx, ima->rr->recty, 32, 0);
ibuf->channels = rpass->channels;
IMB_assign_shared_float_buffer(ibuf, rpass->buffer.data, rpass->buffer.sharing_info);
if (rpass && rpass->ibuf) {
ibuf = rpass->ibuf;
IMB_refImBuf(ibuf);
BKE_imbuf_stamp_info(ima->rr, ibuf);
@ -4295,15 +4292,12 @@ static ImBuf *image_get_ibuf_multilayer(Image *ima, ImageUser *iuser)
if (ima->rr) {
RenderPass *rpass = BKE_image_multilayer_index(ima->rr, iuser);
if (rpass) {
ibuf = IMB_allocImBuf(ima->rr->rectx, ima->rr->recty, 32, 0);
if (rpass && rpass->ibuf) {
ibuf = rpass->ibuf;
IMB_refImBuf(ibuf);
image_init_after_load(ima, iuser, ibuf);
IMB_assign_shared_float_buffer(ibuf, rpass->buffer.data, rpass->buffer.sharing_info);
ibuf->channels = rpass->channels;
BKE_imbuf_stamp_info(ima->rr, ibuf);
image_assign_ibuf(ima, ibuf, iuser ? iuser->multi_index : IMA_NO_INDEX, 0);
@ -4318,15 +4312,10 @@ static ImBuf *image_get_ibuf_multilayer(Image *ima, ImageUser *iuser)
/* always returns a single ibuf, also during render progress */
static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_lock)
{
Render *re;
RenderView *rv;
RenderBuffer *combined_buffer;
RenderByteBuffer *byte_buffer;
ImBuf *pass_ibuf = nullptr;
float dither;
int channels, layer, pass;
ImBuf *ibuf;
int from_render = (ima->render_slot == ima->last_render_slot);
int actview;
const int from_render = (ima->render_slot == ima->last_render_slot);
if (!(iuser && iuser->scene)) {
return nullptr;
@ -4337,12 +4326,11 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_loc
return nullptr;
}
re = RE_GetSceneRender(iuser->scene);
Render *re = RE_GetSceneRender(iuser->scene);
channels = 4;
layer = iuser->layer;
pass = iuser->pass;
actview = iuser->view;
const int layer = iuser->layer;
const int pass = iuser->pass;
int actview = iuser->view;
if (BKE_image_is_stereo(ima) && (iuser->flag & IMA_SHOW_STEREO)) {
actview = iuser->multiview_eye;
@ -4355,7 +4343,7 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_loc
}
else if ((slot = BKE_image_get_renderslot(ima, ima->render_slot))->render) {
rres = *(slot->render);
rres.have_combined = ((RenderView *)rres.views.first)->combined_buffer.data != nullptr;
rres.have_combined = ((RenderView *)rres.views.first)->ibuf != nullptr;
}
if (!(rres.rectx > 0 && rres.recty > 0)) {
@ -4380,12 +4368,10 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_loc
/* this gives active layer, composite or sequence result */
if (rv == nullptr) {
byte_buffer = &rres.byte_buffer;
combined_buffer = &rres.combined_buffer;
pass_ibuf = rres.ibuf;
}
else {
byte_buffer = &rv->byte_buffer;
combined_buffer = &rv->combined_buffer;
pass_ibuf = rv->ibuf;
}
dither = iuser->scene->r.dither_intensity;
@ -4394,13 +4380,8 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_loc
if (rres.have_combined && layer == 0) {
/* pass */
}
else if (byte_buffer && byte_buffer->data && layer == 0) {
/* rect32 is set when there's a Sequence pass, this pass seems
* to have layer=0 (this is from image_buttons.c)
* in this case we ignore float buffer, because it could have
* hung from previous pass which was float
*/
combined_buffer = nullptr;
else if (pass_ibuf && pass_ibuf->byte_buffer.data && layer == 0) {
/* pass */
}
else if (rres.layers.first) {
RenderLayer *rl = static_cast<RenderLayer *>(
@ -4408,78 +4389,24 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_loc
if (rl) {
RenderPass *rpass = image_render_pass_get(rl, pass, actview, nullptr);
if (rpass) {
combined_buffer = &rpass->buffer;
pass_ibuf = rpass->ibuf;
if (pass != 0) {
channels = rpass->channels;
dither = 0.0f; /* don't dither passes */
}
}
}
}
ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, nullptr);
if (pass_ibuf) {
/* TODO(sergey): Perhaps its better to assign dither when ImBuf is allocated for the render
* result. It will avoid modification here, and allow comparing render results with different
* dither applied to them. */
pass_ibuf->dither = dither;
/* make ibuf if needed, and initialize it */
if (ibuf == nullptr) {
ibuf = IMB_allocImBuf(rres.rectx, rres.recty, 32, 0);
image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0);
IMB_refImBuf(pass_ibuf);
}
/* Set color space settings for a byte buffer.
*
* This is mainly to make it so color management treats byte buffer
* from render result with Save Buffers enabled as final display buffer
* and doesn't apply any color management on it.
*
* For other cases we need to be sure it stays to default byte buffer space.
*/
if (ibuf->byte_buffer.data != byte_buffer->data) {
const char *colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE);
IMB_colormanagement_assign_byte_colorspace(ibuf, colorspace);
}
/* invalidate color managed buffers if render result changed */
BLI_thread_lock(LOCK_COLORMANAGE);
if (combined_buffer && (ibuf->x != rres.rectx || ibuf->y != rres.recty ||
ibuf->float_buffer.data != combined_buffer->data))
{
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
}
ibuf->x = rres.rectx;
ibuf->y = rres.recty;
ibuf->channels = channels;
imb_freerectImBuf(ibuf);
if (byte_buffer) {
IMB_assign_shared_byte_buffer(ibuf, byte_buffer->data, byte_buffer->sharing_info);
}
else {
IMB_assign_byte_buffer(ibuf, nullptr, IB_DO_NOT_TAKE_OWNERSHIP);
}
if (combined_buffer) {
IMB_assign_shared_float_buffer(ibuf, combined_buffer->data, combined_buffer->sharing_info);
}
else {
IMB_assign_float_buffer(ibuf, nullptr, IB_DO_NOT_TAKE_OWNERSHIP);
}
/* TODO(sergey): Make this faster by either simply referencing the stamp
* or by changing both ImBuf and RenderResult to use same data type to
* store metadata. */
if (ibuf->metadata != nullptr) {
IMB_metadata_free(ibuf->metadata);
ibuf->metadata = nullptr;
}
BKE_imbuf_stamp_info(&rres, ibuf);
BLI_thread_unlock(LOCK_COLORMANAGE);
ibuf->dither = dither;
return ibuf;
return pass_ibuf;
}
static int image_get_multiview_index(Image *ima, ImageUser *iuser)

View File

@ -736,7 +736,7 @@ bool BKE_image_render_write_exr(ReportList *reports,
/* Compositing result. */
if (rr->have_combined) {
LISTBASE_FOREACH (RenderView *, rview, &rr->views) {
if (!rview->combined_buffer.data) {
if (!rview->ibuf || !rview->ibuf->float_buffer.data) {
continue;
}
@ -757,8 +757,8 @@ bool BKE_image_render_write_exr(ReportList *reports,
float *output_rect =
(save_as_render) ?
image_exr_from_scene_linear_to_output(
rview->combined_buffer.data, rr->rectx, rr->recty, 4, imf, tmp_output_rects) :
rview->combined_buffer.data;
rview->ibuf->float_buffer.data, rr->rectx, rr->recty, 4, imf, tmp_output_rects) :
rview->ibuf->float_buffer.data;
for (int a = 0; a < channels; a++) {
char passname[EXR_PASS_MAXNAME];
@ -814,11 +814,14 @@ bool BKE_image_render_write_exr(ReportList *reports,
const bool pass_half_float = half_float && pass_RGBA;
/* Color-space conversion only happens on RGBA passes. */
float *output_rect =
(save_as_render && pass_RGBA) ?
image_exr_from_scene_linear_to_output(
rp->buffer.data, rr->rectx, rr->recty, rp->channels, imf, tmp_output_rects) :
rp->buffer.data;
float *output_rect = (save_as_render && pass_RGBA) ?
image_exr_from_scene_linear_to_output(rp->ibuf->float_buffer.data,
rr->rectx,
rr->recty,
rp->channels,
imf,
tmp_output_rects) :
rp->ibuf->float_buffer.data;
for (int a = 0; a < std::min(channels, rp->channels); a++) {
/* Save Combined as RGBA or RGB if single layer save. */

View File

@ -59,8 +59,9 @@ void CompositorOperation::deinit_execution()
if (rr) {
RenderView *rv = RE_RenderViewGetByName(rr, view_name_);
ImBuf *ibuf = RE_RenderViewEnsureImBuf(rr, rv);
RE_RenderBuffer_assign_data(&rv->combined_buffer, output_buffer_);
IMB_assign_float_buffer(ibuf, output_buffer_, IB_TAKE_OWNERSHIP);
rr->have_combined = true;
}

View File

@ -46,6 +46,8 @@
#include "DNA_modifier_types.h"
#include "DNA_particle_types.h"
#include "IMB_imbuf_types.h"
#include "eevee_private.h"
/* -------------------------------------------------------------------- */
@ -582,7 +584,7 @@ static void eevee_cryptomatte_extract_render_passes(
const int pass_offset = pass * 2;
SNPRINTF_RLEN(cryptomatte_pass_name, render_pass_name_format, pass);
RenderPass *rp_object = RE_pass_find_by_name(rl, cryptomatte_pass_name, viewname);
float *rp_buffer_data = rp_object->buffer.data;
float *rp_buffer_data = rp_object->ibuf->float_buffer.data;
for (int y = 0; y < rect_height; y++) {
for (int x = 0; x < rect_width; x++) {
const int accum_buffer_offset = (rect_offset_x + x +

View File

@ -31,6 +31,8 @@
#include "RE_pipeline.h"
#include "IMB_imbuf_types.h"
#include "eevee_private.h"
bool EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, Depsgraph *depsgraph)
@ -267,7 +269,7 @@ static void eevee_render_color_result(RenderLayer *rl,
num_channels,
0,
GPU_DATA_FLOAT,
rp->buffer.data);
rp->ibuf->float_buffer.data);
}
static void eevee_render_result_combined(RenderLayer *rl,

View File

@ -17,6 +17,7 @@
#include "DNA_ID.h"
#include "DNA_lightprobe_types.h"
#include "DNA_modifier_types.h"
#include "IMB_imbuf_types.h"
#include "RE_pipeline.h"
#include "eevee_engine.h"
@ -362,7 +363,9 @@ void Instance::render_read_result(RenderLayer *render_layer, const char *view_na
RenderPass *vector_rp = RE_pass_find_by_name(
render_layer, vector_pass_name.c_str(), view_name);
if (vector_rp) {
memset(vector_rp->buffer.data, 0, sizeof(float) * 4 * vector_rp->rectx * vector_rp->recty);
memset(vector_rp->ibuf->float_buffer.data,
0,
sizeof(float) * 4 * vector_rp->rectx * vector_rp->recty);
}
}
}

View File

@ -17,6 +17,8 @@
#include "RE_pipeline.h"
#include "IMB_imbuf_types.h"
#include "gpencil_engine.h"
void GPENCIL_render_init(GPENCIL_Data *vedata,
@ -50,8 +52,8 @@ void GPENCIL_render_init(GPENCIL_Data *vedata,
RenderPass *rpass_z_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_Z, viewname);
RenderPass *rpass_col_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname);
float *pix_z = (rpass_z_src) ? rpass_z_src->buffer.data : NULL;
float *pix_col = (rpass_col_src) ? rpass_col_src->buffer.data : NULL;
float *pix_z = (rpass_z_src) ? rpass_z_src->ibuf->float_buffer.data : NULL;
float *pix_col = (rpass_col_src) ? rpass_col_src->ibuf->float_buffer.data : NULL;
if (!pix_z || !pix_col) {
RE_engine_set_error_message(engine,
@ -161,7 +163,7 @@ static void GPENCIL_render_result_z(RenderLayer *rl,
if ((view_layer->passflag & SCE_PASS_Z) != 0) {
RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_Z, viewname);
float *ro_buffer_data = rp->buffer.data;
float *ro_buffer_data = rp->ibuf->float_buffer.data;
GPU_framebuffer_read_depth(vedata->fbl->render_fb,
rect->xmin,
@ -223,7 +225,7 @@ static void GPENCIL_render_result_combined(RenderLayer *rl,
4,
0,
GPU_DATA_FLOAT,
rp->buffer.data);
rp->ibuf->float_buffer.data);
}
void GPENCIL_render_to_image(void *ved,

View File

@ -14,6 +14,7 @@
#include "ED_paint.h"
#include "ED_view3d.h"
#include "GPU_capabilities.h"
#include "IMB_imbuf_types.h"
#include "draw_common.hh"
#include "draw_sculpt.hh"
@ -656,7 +657,7 @@ static void write_render_color_output(RenderLayer *layer,
4,
0,
GPU_DATA_FLOAT,
rp->buffer.data);
rp->ibuf->float_buffer.data);
}
}
@ -675,13 +676,13 @@ static void write_render_z_output(RenderLayer *layer,
BLI_rcti_size_x(rect),
BLI_rcti_size_y(rect),
GPU_DATA_FLOAT,
rp->buffer.data);
rp->ibuf->float_buffer.data);
int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect);
/* Convert GPU depth [0..1] to view Z [near..far] */
if (DRW_view_is_persp_get(nullptr)) {
for (float &z : MutableSpan(rp->buffer.data, pix_num)) {
for (float &z : MutableSpan(rp->ibuf->float_buffer.data, pix_num)) {
if (z == 1.0f) {
z = 1e10f; /* Background */
}
@ -697,7 +698,7 @@ static void write_render_z_output(RenderLayer *layer,
float far = DRW_view_far_distance_get(nullptr);
float range = fabsf(far - near);
for (float &z : MutableSpan(rp->buffer.data, pix_num)) {
for (float &z : MutableSpan(rp->ibuf->float_buffer.data, pix_num)) {
if (z == 1.0f) {
z = 1e10f; /* Background */
}

View File

@ -24,6 +24,8 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "IMB_imbuf_types.h"
#include "RE_pipeline.h"
#include "workbench_private.h"
@ -104,7 +106,7 @@ static void workbench_render_result_z(RenderLayer *rl, const char *viewname, con
if ((view_layer->passflag & SCE_PASS_Z) != 0) {
RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_Z, viewname);
float *rp_buffer_data = rp->buffer.data;
float *rp_buffer_data = rp->ibuf->float_buffer.data;
GPU_framebuffer_bind(dfbl->default_fb);
GPU_framebuffer_read_depth(dfbl->default_fb,
@ -208,7 +210,7 @@ void workbench_render(void *ved, RenderEngine *engine, RenderLayer *render_layer
4,
0,
GPU_DATA_FLOAT,
rp->buffer.data);
rp->ibuf->float_buffer.data);
workbench_render_result_z(render_layer, viewname, rect);
}

View File

@ -182,7 +182,7 @@ static bool eyedropper_cryptomatte_sample_renderlayer_fl(RenderLayer *render_lay
const int y = int(fpos[1] * render_pass->recty);
const int offset = 4 * (y * render_pass->rectx + x);
zero_v3(r_col);
r_col[0] = render_pass->buffer.data[offset];
r_col[0] = render_pass->ibuf->float_buffer.data[offset];
return true;
}
}

View File

@ -204,16 +204,19 @@ static void image_buffer_rect_update(RenderJob *rj,
*/
/* TODO(sergey): Need to check has_combined here? */
if (iuser->pass == 0) {
RenderView *rv;
const int view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname);
rv = RE_RenderViewGetById(rr, view_id);
const RenderView *rv = RE_RenderViewGetById(rr, view_id);
if (rv->ibuf == nullptr) {
return;
}
/* find current float rect for display, first case is after composite... still weak */
if (rv->combined_buffer.data) {
rectf = rv->combined_buffer.data;
if (rv->ibuf->float_buffer.data) {
rectf = rv->ibuf->float_buffer.data;
}
else {
if (rv->byte_buffer.data) {
if (rv->ibuf->byte_buffer.data) {
/* special case, currently only happens with sequencer rendering,
* which updates the whole frame, so we can only mark display buffer
* as invalid here (sergey)

View File

@ -194,8 +194,7 @@ static void screen_opengl_views_setup(OGLRender *oglrender)
RenderView *rv_del = rv->next;
BLI_remlink(&rr->views, rv_del);
RE_RenderBuffer_data_free(&rv_del->combined_buffer);
RE_RenderByteBuffer_data_free(&rv_del->byte_buffer);
IMB_freeImBuf(rv_del->ibuf);
MEM_freeN(rv_del);
}
@ -219,8 +218,7 @@ static void screen_opengl_views_setup(OGLRender *oglrender)
BLI_remlink(&rr->views, rv_del);
RE_RenderBuffer_data_free(&rv_del->combined_buffer);
RE_RenderByteBuffer_data_free(&rv_del->byte_buffer);
IMB_freeImBuf(rv_del->ibuf);
MEM_freeN(rv_del);
}

View File

@ -668,7 +668,7 @@ static bool ed_preview_draw_rect(
rv = nullptr;
}
if (rv && rv->combined_buffer.data) {
if (rv && rv->ibuf) {
if (abs(rres.rectx - newx) < 2 && abs(rres.recty - newy) < 2) {
newrect->xmax = max_ii(newrect->xmax, rect->xmin + rres.rectx + offx);
newrect->ymax = max_ii(newrect->ymax, rect->ymin + rres.recty);
@ -677,13 +677,8 @@ static bool ed_preview_draw_rect(
float fx = rect->xmin + offx;
float fy = rect->ymin;
ImBuf *ibuf = IMB_allocImBuf(rres.rectx, rres.recty, 0, 0);
IMB_assign_float_buffer(ibuf, rv->combined_buffer.data, IB_DO_NOT_TAKE_OWNERSHIP);
ED_draw_imbuf(
ibuf, fx, fy, false, &scene->view_settings, &scene->display_settings, 1.0f, 1.0f);
IMB_freeImBuf(ibuf);
rv->ibuf, fx, fy, false, &scene->view_settings, &scene->display_settings, 1.0f, 1.0f);
ok = true;
}
@ -1062,9 +1057,11 @@ static void shader_preview_texture(ShaderPreview *sp, Tex *tex, Scene *sce, Rend
/* Create buffer in empty RenderView created in the init step. */
RenderResult *rr = RE_AcquireResultWrite(re);
RenderView *rv = (RenderView *)rr->views.first;
RE_RenderBuffer_assign_data(&rv->combined_buffer,
static_cast<float *>(MEM_callocN(sizeof(float[4]) * width * height,
"texture render result")));
ImBuf *rv_ibuf = RE_RenderViewEnsureImBuf(rr, rv);
IMB_assign_float_buffer(rv_ibuf,
static_cast<float *>(MEM_callocN(sizeof(float[4]) * width * height,
"texture render result")),
IB_TAKE_OWNERSHIP);
RE_ReleaseResult(re);
/* Get texture image pool (if any) */
@ -1072,7 +1069,7 @@ static void shader_preview_texture(ShaderPreview *sp, Tex *tex, Scene *sce, Rend
BKE_texture_fetch_images_for_pool(tex, img_pool);
/* Fill in image buffer. */
float *rect_float = rv->combined_buffer.data;
float *rect_float = rv_ibuf->float_buffer.data;
float tex_coord[3] = {0.0f, 0.0f, 0.0f};
bool color_manage = BKE_scene_check_color_management_enabled(sce);

View File

@ -128,10 +128,14 @@ static bool ui_imageuser_slot_menu_step(bContext *C, int direction, void *image_
static const char *ui_imageuser_layer_fake_name(RenderResult *rr)
{
RenderView *rv = RE_RenderViewGetById(rr, 0);
if (rv->combined_buffer.data) {
ImBuf *ibuf = rv->ibuf;
if (!ibuf) {
return NULL;
}
if (ibuf->float_buffer.data) {
return IFACE_("Composite");
}
if (rv->byte_buffer.data) {
if (ibuf->byte_buffer.data) {
return IFACE_("Sequence");
}
return NULL;

View File

@ -47,6 +47,8 @@ using namespace Freestyle;
#include "DEG_depsgraph_query.h"
#include "IMB_imbuf.h"
#include "pipeline.hh"
#include "FRS_freestyle.h"
@ -447,7 +449,7 @@ static void prepare(Render *re, ViewLayer *view_layer, Depsgraph *depsgraph)
RenderLayer *rl = RE_GetRenderLayer(re->result, view_layer->name);
bool diffuse = false, z = false;
for (RenderPass *rpass = (RenderPass *)rl->passes.first; rpass; rpass = rpass->next) {
float *rpass_buffer_data = rpass->buffer.data;
float *rpass_buffer_data = rpass->ibuf->float_buffer.data;
if (STREQ(rpass->name, RE_PASSNAME_DIFFUSE_COLOR)) {
controller->setPassDiffuse(rpass_buffer_data, rpass->rectx, rpass->recty);
diffuse = true;

View File

@ -44,7 +44,6 @@
#include "../blenlib/BLI_sys_types.h"
#include "../gpu/GPU_texture.h"
#include "BLI_implicit_sharing.h"
#include "BLI_utildefines.h"
#include "IMB_imbuf_types.h"
@ -152,19 +151,6 @@ struct ImBuf *IMB_allocFromBuffer(const uint8_t *byte_buffer,
unsigned int h,
unsigned int channels);
/**
* Assign the content of the corresponding buffer using an implicitly shareable data pointer.
*
* \note Does not modify the topology (width, height, number of channels)
* or the mipmaps in any way.
*/
void IMB_assign_shared_byte_buffer(struct ImBuf *ibuf,
uint8_t *buffer_data,
const ImplicitSharingInfoHandle *implicit_sharing);
void IMB_assign_shared_float_buffer(struct ImBuf *ibuf,
float *buffer_data,
const ImplicitSharingInfoHandle *implicit_sharing);
/**
* Assign the content of the corresponding buffer with the given data and ownership.
* The current content of the buffer is released corresponding to its ownership configuration.
@ -847,9 +833,13 @@ bool imb_addrectfloatImBuf(struct ImBuf *ibuf, const unsigned int channels);
void imb_freerectfloatImBuf(struct ImBuf *ibuf);
void imb_freemipmapImBuf(struct ImBuf *ibuf);
/** Free all pixel data (associated with image size). */
/** Free all CPU pixel data (associated with image size). */
void imb_freerectImbuf_all(struct ImBuf *ibuf);
/* Free the GPU textures of the given image buffer, leaving the CPU buffers unchanged.
* The ibuf can be nullptr, in which case the function does nothing. */
void IMB_free_gpu_textures(struct ImBuf *ibuf);
/**
* Threaded processors.
*/

View File

@ -4,12 +4,12 @@
#pragma once
#include "BLI_implicit_sharing.h"
#include "DNA_vec_types.h" /* for rcti */
#include "BLI_sys_types.h"
struct GPUTexture;
#ifdef __cplusplus
extern "C" {
#endif
@ -172,17 +172,14 @@ typedef enum ImBufOwnership {
/* Different storage specialization.
*
* Note on the implicit sharing
* ----------------------------
* NOTE: Avoid direct assignments and allocations, use the buffer utilities from the IMB_imbuf.h
* instead.
*
* The buffer allows implicitly sharing data with other users of such data. In this case the
* ownership is set to IB_DO_NOT_TAKE_OWNERSHIP. */
/* TODO(sergey): Once everything is C++ replace with a template. */
* Accessing the data pointer directly is fine and is an expected way of accessing it. */
typedef struct ImBufByteBuffer {
uint8_t *data;
ImBufOwnership ownership;
const ImplicitSharingInfoHandle *implicit_sharing;
struct ColorSpace *colorspace;
} ImBufByteBuffer;
@ -190,11 +187,20 @@ typedef struct ImBufByteBuffer {
typedef struct ImBufFloatBuffer {
float *data;
ImBufOwnership ownership;
const ImplicitSharingInfoHandle *implicit_sharing;
struct ColorSpace *colorspace;
} ImBufFloatBuffer;
typedef struct ImBufGPU {
/* Texture which corresponds to the state of the ImBug on the GPU.
*
* Allocation is supposed to happen outside of the ImBug module from a proper GPU context.
* De-referencing the ImBuf or its GPU texture can happen from any state. */
/* TODO(sergey): This should become a list of textures, to support having high-res ImBuf on GPU
* without hitting hardware limitations. */
struct GPUTexture *texture;
} ImBufGPU;
/** \} */
/* -------------------------------------------------------------------- */
@ -236,6 +242,9 @@ typedef struct ImBuf {
*/
ImBufFloatBuffer float_buffer;
/* Image buffer on the GPU. */
ImBufGPU gpu;
/** Resolution in pixels per meter. Multiply by `0.0254` for DPI. */
double ppm[2];

View File

@ -23,10 +23,11 @@
#include "MEM_guardedalloc.h"
#include "BLI_implicit_sharing.hh"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
#include "GPU_texture.h"
static SpinLock refcounter_spin;
void imb_refcounter_lock_init()
@ -67,16 +68,12 @@ void imb_mmap_unlock()
* buffer to its defaults. */
template<class BufferType> static void imb_free_buffer(BufferType &buffer)
{
if (buffer.implicit_sharing) {
blender::implicit_sharing::free_shared_data(&buffer.data, &buffer.implicit_sharing);
}
else if (buffer.data) {
if (buffer.data) {
switch (buffer.ownership) {
case IB_DO_NOT_TAKE_OWNERSHIP:
break;
case IB_TAKE_OWNERSHIP:
BLI_assert(buffer.implicit_sharing == nullptr);
MEM_freeN(buffer.data);
break;
}
@ -85,7 +82,6 @@ template<class BufferType> static void imb_free_buffer(BufferType &buffer)
/* Reset buffer to defaults. */
buffer.data = nullptr;
buffer.ownership = IB_DO_NOT_TAKE_OWNERSHIP;
buffer.implicit_sharing = nullptr;
}
/* Allocate pixel storage of the given buffer. The buffer owns the allocated memory.
@ -101,7 +97,6 @@ bool imb_alloc_buffer(
}
buffer.ownership = IB_TAKE_OWNERSHIP;
buffer.implicit_sharing = nullptr;
return true;
}
@ -119,12 +114,6 @@ template<class BufferType> void imb_make_writeable_buffer(BufferType &buffer)
buffer.data = static_cast<decltype(BufferType::data)>(MEM_dupallocN(buffer.data));
buffer.ownership = IB_TAKE_OWNERSHIP;
if (buffer.implicit_sharing) {
buffer.implicit_sharing->remove_user_and_delete_if_last();
buffer.implicit_sharing = nullptr;
}
break;
case IB_TAKE_OWNERSHIP:
break;
}
@ -157,30 +146,6 @@ auto imb_steal_buffer_data(BufferType &buffer) -> decltype(BufferType::data)
return nullptr;
}
/* Assign the new data of the buffer which is implicitly shared via the given handle.
* The old content of the buffer is freed using imb_free_buffer. */
template<class BufferType>
void imb_assign_shared_buffer(BufferType &buffer,
decltype(BufferType::data) buffer_data,
const ImplicitSharingInfoHandle *implicit_sharing)
{
imb_free_buffer(buffer);
if (implicit_sharing) {
BLI_assert(buffer_data != nullptr);
blender::implicit_sharing::copy_shared_pointer(
buffer_data, implicit_sharing, &buffer.data, &buffer.implicit_sharing);
}
else {
BLI_assert(buffer_data == nullptr);
buffer.data = nullptr;
buffer.implicit_sharing = nullptr;
}
buffer.ownership = IB_DO_NOT_TAKE_OWNERSHIP;
}
void imb_freemipmapImBuf(ImBuf *ibuf)
{
int a;
@ -244,6 +209,16 @@ void imb_freerectImbuf_all(ImBuf *ibuf)
freeencodedbufferImBuf(ibuf);
}
void IMB_free_gpu_textures(ImBuf *ibuf)
{
if (!ibuf || !ibuf->gpu.texture) {
return;
}
GPU_texture_free(ibuf->gpu.texture);
ibuf->gpu.texture = nullptr;
}
void IMB_freeImBuf(ImBuf *ibuf)
{
if (ibuf == nullptr) {
@ -267,6 +242,7 @@ void IMB_freeImBuf(ImBuf *ibuf)
"'.blend' relative \"//\" must not be used in ImBuf!");
imb_freerectImbuf_all(ibuf);
IMB_free_gpu_textures(ibuf);
IMB_metadata_free(ibuf->metadata);
colormanage_cache_free(ibuf);
@ -459,32 +435,6 @@ void IMB_make_writable_float_buffer(ImBuf *ibuf)
imb_make_writeable_buffer(ibuf->float_buffer);
}
void IMB_assign_shared_byte_buffer(ImBuf *ibuf,
uint8_t *buffer_data,
const ImplicitSharingInfoHandle *implicit_sharing)
{
imb_free_buffer(ibuf->byte_buffer);
ibuf->flags &= ~IB_rect;
if (buffer_data) {
imb_assign_shared_buffer(ibuf->byte_buffer, buffer_data, implicit_sharing);
ibuf->flags |= IB_rect;
}
}
void IMB_assign_shared_float_buffer(ImBuf *ibuf,
float *buffer_data,
const ImplicitSharingInfoHandle *implicit_sharing)
{
imb_free_buffer(ibuf->float_buffer);
ibuf->flags &= ~IB_rectfloat;
if (buffer_data) {
imb_assign_shared_buffer(ibuf->float_buffer, buffer_data, implicit_sharing);
ibuf->flags |= IB_rectfloat;
}
}
void IMB_assign_byte_buffer(ImBuf *ibuf, uint8_t *buffer_data, const ImBufOwnership ownership)
{
imb_free_buffer(ibuf->byte_buffer);
@ -626,8 +576,6 @@ ImBuf *IMB_dupImBuf(const ImBuf *ibuf1)
return nullptr;
}
/* TODO(sergey): Use implicit sharing. */
if (ibuf1->byte_buffer.data) {
flags |= IB_rect;
}

View File

@ -93,6 +93,7 @@ const EnumPropertyItem rna_enum_bake_pass_type_items[] = {
# include "GPU_capabilities.h"
# include "GPU_shader.h"
# include "IMB_colormanagement.h"
# include "IMB_imbuf_types.h"
# include "DEG_depsgraph_query.h"
@ -499,15 +500,30 @@ static int rna_RenderPass_rect_get_length(const PointerRNA *ptr,
static void rna_RenderPass_rect_get(PointerRNA *ptr, float *values)
{
RenderPass *rpass = (RenderPass *)ptr->data;
memcpy(
values, rpass->buffer.data, sizeof(float) * rpass->rectx * rpass->recty * rpass->channels);
const size_t size_in_bytes = sizeof(float) * rpass->rectx * rpass->recty * rpass->channels;
const float *buffer = rpass->ibuf ? rpass->ibuf->float_buffer.data : nullptr;
if (!buffer) {
/* No float buffer to read from, initialize to all zeroes. */
memset(values, 0, size_in_bytes);
return;
}
memcpy(values, buffer, size_in_bytes);
}
void rna_RenderPass_rect_set(PointerRNA *ptr, const float *values)
{
RenderPass *rpass = (RenderPass *)ptr->data;
memcpy(
rpass->buffer.data, values, sizeof(float) * rpass->rectx * rpass->recty * rpass->channels);
float *buffer = rpass->ibuf ? rpass->ibuf->float_buffer.data : nullptr;
if (!buffer) {
/* Only writing to an already existing buffer is supported. */
return;
}
const size_t size_in_bytes = sizeof(float) * rpass->rectx * rpass->recty * rpass->channels;
memcpy(buffer, values, size_in_bytes);
}
static RenderPass *rna_RenderPass_find_by_type(RenderLayer *rl, int passtype, const char *view)

View File

@ -42,43 +42,14 @@ extern "C" {
/* only used as handle */
typedef struct Render Render;
/* Buffer of a floating point values which uses implicit sharing.
*
* The buffer is allocated by render passes creation, and then is shared with the render result
* and image buffer.
*
* The GPU texture is an optional read-only copy of the render buffer in GPU memory. */
typedef struct RenderBuffer {
float *data;
const ImplicitSharingInfoHandle *sharing_info;
struct GPUTexture *gpu_texture;
} RenderBuffer;
/* Specialized render buffer to store 8bpp passes. */
typedef struct RenderByteBuffer {
uint8_t *data;
const ImplicitSharingInfoHandle *sharing_info;
struct GPUTexture *gpu_texture;
} RenderByteBuffer;
/* Render Result usage:
*
* - render engine allocates/frees and delivers raw floating point rects
* - right now it's full rects, but might become tiles or file
* - the display client has to allocate display rects, sort out what to display,
* and how it's converted
*/
typedef struct RenderView {
struct RenderView *next, *prev;
char name[64]; /* EXR_VIEW_MAXNAME */
/* if this exists, result of composited layers */
RenderBuffer combined_buffer;
/* optional, 32 bits version of picture, used for sequencer, OpenGL render and image curves */
RenderByteBuffer byte_buffer;
/* Image buffer of a composited layer or a sequencer output.
* The ibuf is only allocated if it has an actual data in one of its buffers (float, byte, or
* GPU). */
struct ImBuf *ibuf;
} RenderView;
typedef struct RenderPass {
@ -87,7 +58,14 @@ typedef struct RenderPass {
char name[64]; /* amount defined in IMB_openexr.h */
char chan_id[8]; /* amount defined in IMB_openexr.h */
RenderBuffer buffer;
/* Image buffer which contains data of this pass.
*
* The data can be either CPU side stored in ibuf->float_buffer, or a GPU-side stored in
* ibuf->gpu (during rendering, i.e.).
*
* The pass data storage is lazily allocated, and until data is actually provided (via either CPU
* buffer of GPU texture) the ibuf is not allocated. */
struct ImBuf *ibuf;
int rectx, recty;
@ -126,14 +104,10 @@ typedef struct RenderResult {
/* target image size */
int rectx, recty;
/* The following byte, combined, and z buffers are for temporary storage only,
* for RenderResult structs created in #RE_AcquireResultImage - which do not have RenderView */
/* Optional, 32 bits version of picture, used for OpenGL render and image curves. */
RenderByteBuffer byte_buffer;
/* if this exists, a copy of one of layers, or result of composited layers */
RenderBuffer combined_buffer;
/* The temporary storage to pass image data from #RE_AcquireResultImage.
* Is null pointer when the RenderResult is not coming from the #RE_AcquireResultImage, and is
* a pointer to an existing ibuf in either RenderView or a RenderPass otherwise. */
struct ImBuf *ibuf;
/* coordinates within final image (after cropping) */
rcti tilerect;
@ -285,9 +259,9 @@ void RE_render_result_rect_from_ibuf(struct RenderResult *rr,
struct RenderLayer *RE_GetRenderLayer(struct RenderResult *rr, const char *name);
float *RE_RenderLayerGetPass(struct RenderLayer *rl, const char *name, const char *viewname);
RenderBuffer *RE_RenderLayerGetPassBuffer(struct RenderLayer *rl,
const char *name,
const char *viewname);
struct ImBuf *RE_RenderLayerGetPassImBuf(struct RenderLayer *rl,
const char *name,
const char *viewname);
bool RE_HasSingleLayer(struct Render *re);
@ -510,42 +484,8 @@ struct RenderView *RE_RenderViewGetByName(struct RenderResult *rr, const char *v
RenderResult *RE_DuplicateRenderResult(RenderResult *rr);
/**
* Create new render buffer which takes ownership of the given data.
* Creates an implicit sharing handle for the data as well. */
RenderBuffer RE_RenderBuffer_new(float *data);
/**
* Assign the buffer data.
*
* The current buffer data is freed and the new one is assigned, and the implicit sharing for it.
*/
void RE_RenderBuffer_assign_data(RenderBuffer *render_buffer, float *data);
/**
* Effectively `lhs = rhs`. The lhs will share the same buffer as the rhs (with an increased user
* counter).
*
* The current content of the lhs is freed.
* The rhs and its data is allowed to be nullptr, in which case the lhs's data will be nullptr
* after this call.
*/
void RE_RenderBuffer_assign_shared(RenderBuffer *lhs, const RenderBuffer *rhs);
/**
* Free data of the given buffer.
*
* The data and implicit sharing information of the buffer is set to nullptr after this call.
* The buffer itself is not freed.
*/
void RE_RenderBuffer_data_free(RenderBuffer *render_buffer);
/* Implementation of above, but for byte buffer. */
/* TODO(sergey): Once everything is C++ we can remove the duplicated API. */
RenderByteBuffer RE_RenderByteBuffer_new(uint8_t *data);
void RE_RenderByteBuffer_assign_data(RenderByteBuffer *render_buffer, uint8_t *data);
void RE_RenderByteBuffer_assign_shared(RenderByteBuffer *lhs, const RenderByteBuffer *rhs);
void RE_RenderByteBuffer_data_free(RenderByteBuffer *render_buffer);
struct ImBuf *RE_RenderPassEnsureImBuf(RenderPass *render_pass);
struct ImBuf *RE_RenderViewEnsureImBuf(const RenderResult *render_result, RenderView *render_view);
#ifdef __cplusplus
}

View File

@ -226,7 +226,7 @@ class Context : public realtime_compositor::Context {
RenderPass *rpass = (RenderPass *)BLI_findstring(
&rl->passes, pass_name, offsetof(RenderPass, name));
if (rpass && rpass->buffer.data) {
if (rpass && rpass->ibuf && rpass->ibuf->float_buffer.data) {
input_texture = RE_pass_ensure_gpu_texture_cache(re, rpass);
if (input_texture) {
@ -283,7 +283,8 @@ class Context : public realtime_compositor::Context {
float *output_buffer = (float *)GPU_texture_read(output_texture_, GPU_DATA_FLOAT, 0);
if (output_buffer) {
RE_RenderBuffer_assign_data(&rv->combined_buffer, output_buffer);
ImBuf *ibuf = RE_RenderViewEnsureImBuf(rr, rv);
IMB_assign_float_buffer(ibuf, output_buffer, IB_TAKE_OWNERSHIP);
}
/* TODO: z-buffer output. */

View File

@ -43,6 +43,8 @@
# include "BPY_extern.h"
#endif
#include "IMB_imbuf_types.h"
#include "RE_bake.h"
#include "RE_engine.h"
#include "RE_pipeline.h"
@ -216,8 +218,8 @@ static RenderResult *render_result_from_bake(
/* Fill render passes from bake pixel array, to be read by the render engine. */
for (int ty = 0; ty < h; ty++) {
size_t offset = ty * w * 4;
float *primitive = primitive_pass->buffer.data + offset;
float *differential = differential_pass->buffer.data + offset;
float *primitive = primitive_pass->ibuf->float_buffer.data + offset;
float *differential = differential_pass->ibuf->float_buffer.data + offset;
size_t bake_offset = (y + ty) * image->width + x;
const BakePixel *bake_pixel = pixels + bake_offset;
@ -284,7 +286,7 @@ static void render_result_to_bake(RenderEngine *engine, RenderResult *rr)
const size_t offset = ty * w;
const size_t bake_offset = (y + ty) * image->width + x;
const float *pass_rect = rpass->buffer.data + offset * channels_num;
const float *pass_rect = rpass->ibuf->float_buffer.data + offset * channels_num;
const BakePixel *bake_pixel = pixels + bake_offset;
float *bake_result = result + bake_offset * channels_num;

View File

@ -228,16 +228,16 @@ void RE_FreeRenderResult(RenderResult *rr)
render_result_free(rr);
}
RenderBuffer *RE_RenderLayerGetPassBuffer(RenderLayer *rl, const char *name, const char *viewname)
ImBuf *RE_RenderLayerGetPassImBuf(RenderLayer *rl, const char *name, const char *viewname)
{
RenderPass *rpass = RE_pass_find_by_name(rl, name, viewname);
return rpass ? &rpass->buffer : nullptr;
return rpass ? rpass->ibuf : nullptr;
}
float *RE_RenderLayerGetPass(RenderLayer *rl, const char *name, const char *viewname)
{
RenderPass *rpass = RE_pass_find_by_name(rl, name, viewname);
return rpass ? rpass->buffer.data : nullptr;
const ImBuf *ibuf = RE_RenderLayerGetPassImBuf(rl, name, viewname);
return ibuf ? ibuf->float_buffer.data : nullptr;
}
RenderLayer *RE_GetRenderLayer(RenderResult *rr, const char *name)
@ -381,7 +381,7 @@ void RE_AcquireResultImageViews(Render *re, RenderResult *rr)
render_result_views_shallowcopy(rr, re->result);
RenderView *rv = static_cast<RenderView *>(rr->views.first);
rr->have_combined = (rv->combined_buffer.data != nullptr);
rr->have_combined = (rv->ibuf != nullptr);
/* single layer */
RenderLayer *rl = render_get_single_layer(re, re->result);
@ -390,13 +390,9 @@ void RE_AcquireResultImageViews(Render *re, RenderResult *rr)
* explicitly free it. So simply assign the buffers as a shallow copy here as well. */
if (rl) {
if (rv->combined_buffer.data == nullptr) {
if (rv->ibuf == nullptr) {
LISTBASE_FOREACH (RenderView *, rview, &rr->views) {
RenderBuffer *buffer = RE_RenderLayerGetPassBuffer(
rl, RE_PASSNAME_COMBINED, rview->name);
if (buffer) {
rview->combined_buffer = *buffer;
}
rview->ibuf = RE_RenderLayerGetPassImBuf(rl, RE_PASSNAME_COMBINED, rview->name);
}
}
}
@ -435,24 +431,20 @@ void RE_AcquireResultImage(Render *re, RenderResult *rr, const int view_id)
/* `scene.rd.actview` view. */
rv = RE_RenderViewGetById(re->result, view_id);
rr->have_combined = (rv->combined_buffer.data != nullptr);
rr->have_combined = (rv->ibuf != nullptr);
/* The render result uses shallow initialization, and the caller is not expected to
* explicitly free it. So simply assign the buffers as a shallow copy here as well.
*
* The thread safety is ensured via the re->resultmutex. */
rr->combined_buffer = rv->combined_buffer;
rr->byte_buffer = rv->byte_buffer;
rr->ibuf = rv->ibuf;
/* active layer */
rl = render_get_single_layer(re, re->result);
if (rl) {
if (rv->combined_buffer.data == nullptr) {
RenderBuffer *buffer = RE_RenderLayerGetPassBuffer(rl, RE_PASSNAME_COMBINED, rv->name);
if (buffer) {
rr->combined_buffer = *buffer;
}
if (rv->ibuf == nullptr) {
rr->ibuf = RE_RenderLayerGetPassImBuf(rl, RE_PASSNAME_COMBINED, rv->name);
}
}
@ -1311,8 +1303,8 @@ static void renderresult_stampinfo(Render *re)
BKE_image_stamp_buf(re->scene,
ob_camera_eval,
(re->r.stamp & R_STAMP_STRIPMETA) ? rres.stamp_data : nullptr,
rres.byte_buffer.data,
rres.combined_buffer.data,
rres.ibuf->byte_buffer.data,
rres.ibuf->float_buffer.data,
rres.rectx,
rres.recty,
4);
@ -2594,7 +2586,7 @@ void RE_layer_load_from_file(
IMB_float_from_rect(ibuf);
}
memcpy(rpass->buffer.data,
memcpy(rpass->ibuf->float_buffer.data,
ibuf->float_buffer.data,
sizeof(float[4]) * layer->rectx * layer->recty);
}
@ -2610,7 +2602,7 @@ void RE_layer_load_from_file(
if (ibuf_clip) {
IMB_rectcpy(ibuf_clip, ibuf, 0, 0, x, y, layer->rectx, layer->recty);
memcpy(rpass->buffer.data,
memcpy(rpass->ibuf->float_buffer.data,
ibuf_clip->float_buffer.data,
sizeof(float[4]) * layer->rectx * layer->recty);
IMB_freeImBuf(ibuf_clip);
@ -2737,9 +2729,7 @@ RenderPass *RE_create_gp_pass(RenderResult *rr, const char *layername, const cha
/* Clear previous pass if exist or the new image will be over previous one. */
RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_COMBINED, viewname);
if (rp) {
rp->buffer.sharing_info->remove_user_and_delete_if_last();
rp->buffer.sharing_info = nullptr;
IMB_freeImBuf(rp->ibuf);
BLI_freelinkN(&rl->passes, rp);
}
/* create a totally new pass */

View File

@ -54,8 +54,7 @@ static void render_result_views_free(RenderResult *rr)
RenderView *rv = static_cast<RenderView *>(rr->views.first);
BLI_remlink(&rr->views, rv);
RE_RenderByteBuffer_data_free(&rv->byte_buffer);
RE_RenderBuffer_data_free(&rv->combined_buffer);
IMB_freeImBuf(rv->ibuf);
MEM_freeN(rv);
}
@ -75,7 +74,7 @@ void render_result_free(RenderResult *rr)
while (rl->passes.first) {
RenderPass *rpass = static_cast<RenderPass *>(rl->passes.first);
RE_RenderBuffer_data_free(&rpass->buffer);
IMB_freeImBuf(rpass->ibuf);
BLI_freelinkN(&rl->passes, rpass);
}
@ -85,8 +84,7 @@ void render_result_free(RenderResult *rr)
render_result_views_free(rr);
RE_RenderByteBuffer_data_free(&rr->byte_buffer);
RE_RenderBuffer_data_free(&rr->combined_buffer);
IMB_freeImBuf(rr->ibuf);
if (rr->text) {
MEM_freeN(rr->text);
@ -119,10 +117,7 @@ void render_result_free_gpu_texture_caches(RenderResult *rr)
{
LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) {
LISTBASE_FOREACH (RenderPass *, rpass, &rl->passes) {
if (rpass->buffer.gpu_texture) {
GPU_texture_free(rpass->buffer.gpu_texture);
rpass->buffer.gpu_texture = nullptr;
}
IMB_free_gpu_textures(rpass->ibuf);
}
}
}
@ -143,8 +138,7 @@ void render_result_views_shallowcopy(RenderResult *dst, RenderResult *src)
STRNCPY(rv->name, rview->name);
rv->combined_buffer = rview->combined_buffer;
rv->byte_buffer = rview->byte_buffer;
rv->ibuf = rview->ibuf;
}
}
@ -165,14 +159,19 @@ void render_result_views_shallowdelete(RenderResult *rr)
static void render_layer_allocate_pass(RenderResult *rr, RenderPass *rp)
{
if (rp->buffer.data != nullptr) {
if (rp->ibuf && rp->ibuf->float_buffer.data) {
return;
}
/* NOTE: In-lined manual allocation to support floating point buffers of an arbitrary number of
* channels. */
const size_t rectsize = size_t(rr->rectx) * rr->recty * rp->channels;
float *buffer_data = MEM_cnew_array<float>(rectsize, rp->name);
rp->buffer = RE_RenderBuffer_new(buffer_data);
rp->ibuf = IMB_allocImBuf(rr->rectx, rr->recty, 32, 0);
rp->ibuf->channels = rp->channels;
IMB_assign_float_buffer(rp->ibuf, buffer_data, IB_TAKE_OWNERSHIP);
if (STREQ(rp->name, RE_PASSNAME_VECTOR)) {
/* initialize to max speed */
@ -415,15 +414,27 @@ void RE_create_render_pass(RenderResult *rr,
void RE_pass_set_buffer_data(RenderPass *pass, float *data)
{
RE_RenderBuffer_assign_data(&pass->buffer, data);
ImBuf *ibuf = RE_RenderPassEnsureImBuf(pass);
IMB_assign_float_buffer(ibuf, data, IB_TAKE_OWNERSHIP);
}
GPUTexture *RE_pass_ensure_gpu_texture_cache(Render *re, RenderPass *rpass)
{
if (rpass->buffer.gpu_texture) {
return rpass->buffer.gpu_texture;
ImBuf *ibuf = rpass->ibuf;
if (!ibuf) {
/* No existing GPU texture, but also no CPU side data to create it from. */
return nullptr;
}
if (rpass->buffer.data == nullptr) {
if (ibuf->gpu.texture) {
/* Return existing GPU texture, regardless whether it also exists on CPU or not. */
return ibuf->gpu.texture;
}
if (ibuf->float_buffer.data == nullptr) {
/* No CPU side data to create the texture from. */
return nullptr;
}
@ -431,20 +442,21 @@ GPUTexture *RE_pass_ensure_gpu_texture_cache(Render *re, RenderPass *rpass)
(rpass->channels == 3) ? GPU_RGB16F :
GPU_RGBA16F;
rpass->buffer.gpu_texture = GPU_texture_create_2d("RenderBuffer.gpu_texture",
rpass->rectx,
rpass->recty,
1,
format,
GPU_TEXTURE_USAGE_GENERAL,
nullptr);
/* TODO(sergey): Use utility to assign the texture. */
ibuf->gpu.texture = GPU_texture_create_2d("RenderBuffer.gpu_texture",
rpass->rectx,
rpass->recty,
1,
format,
GPU_TEXTURE_USAGE_GENERAL,
nullptr);
if (rpass->buffer.gpu_texture) {
GPU_texture_update(rpass->buffer.gpu_texture, GPU_DATA_FLOAT, rpass->buffer.data);
if (ibuf->gpu.texture) {
GPU_texture_update(ibuf->gpu.texture, GPU_DATA_FLOAT, ibuf->float_buffer.data);
re->result_has_gpu_texture_caches = true;
}
return rpass->buffer.gpu_texture;
return ibuf->gpu.texture;
}
void RE_render_result_full_channel_name(char *fullname,
@ -543,6 +555,8 @@ static void ml_addpass_cb(void *base,
RenderPass *rpass = MEM_cnew<RenderPass>("loaded pass");
BLI_addtail(&rl->passes, rpass);
rpass->rectx = rr->rectx;
rpass->recty = rr->recty;
rpass->channels = totchan;
rl->passflag |= passtype_from_name(name);
@ -673,7 +687,7 @@ RenderResult *render_result_new_from_exr(
rpass->recty = recty;
if (rpass->channels >= 3) {
IMB_colormanagement_transform(rpass->buffer.data,
IMB_colormanagement_transform(rpass->ibuf->float_buffer.data,
rpass->rectx,
rpass->recty,
rpass->channels,
@ -753,7 +767,11 @@ void render_result_merge(RenderResult *rr, RenderResult *rrpart)
rpass = rpass->next)
{
/* For save buffers, skip any passes that are only saved to disk. */
if (rpass->buffer.data == nullptr || rpassp->buffer.data == nullptr) {
if (rpass->ibuf == nullptr || rpassp->ibuf == nullptr) {
continue;
}
if (rpass->ibuf->float_buffer.data == nullptr ||
rpassp->ibuf->float_buffer.data == nullptr) {
continue;
}
/* Render-result have all passes, render-part only the active view's passes. */
@ -761,7 +779,11 @@ void render_result_merge(RenderResult *rr, RenderResult *rrpart)
continue;
}
do_merge_tile(rr, rrpart, rpass->buffer.data, rpassp->buffer.data, rpass->channels);
do_merge_tile(rr,
rrpart,
rpass->ibuf->float_buffer.data,
rpassp->ibuf->float_buffer.data,
rpass->channels);
/* manually get next render pass */
rpassp = rpassp->next;
@ -865,13 +887,23 @@ bool render_result_exr_file_read_path(RenderResult *rr,
RE_render_result_full_channel_name(
fullname, nullptr, rpass->name, rpass->view, rpass->chan_id, a);
if (IMB_exr_set_channel(
exrhandle, rl->name, fullname, xstride, ystride, rpass->buffer.data + a)) {
if (IMB_exr_set_channel(exrhandle,
rl->name,
fullname,
xstride,
ystride,
rpass->ibuf->float_buffer.data + a))
{
found_channels = true;
}
else if (rl_single) {
if (IMB_exr_set_channel(
exrhandle, nullptr, fullname, xstride, ystride, rpass->buffer.data + a)) {
if (IMB_exr_set_channel(exrhandle,
nullptr,
fullname,
xstride,
ystride,
rpass->ibuf->float_buffer.data + a))
{
found_channels = true;
}
else {
@ -998,8 +1030,10 @@ ImBuf *RE_render_result_rect_to_ibuf(RenderResult *rr,
RenderView *rv = RE_RenderViewGetById(rr, view_id);
/* if not exists, BKE_imbuf_write makes one */
IMB_assign_shared_byte_buffer(ibuf, rv->byte_buffer.data, rv->byte_buffer.sharing_info);
IMB_assign_shared_float_buffer(ibuf, rv->combined_buffer.data, rv->combined_buffer.sharing_info);
if (rv->ibuf) {
IMB_assign_byte_buffer(ibuf, rv->ibuf->byte_buffer.data, IB_DO_NOT_TAKE_OWNERSHIP);
IMB_assign_float_buffer(ibuf, rv->ibuf->float_buffer.data, IB_DO_NOT_TAKE_OWNERSHIP);
}
/* float factor for random dither, imbuf takes care of it */
ibuf->dither = dither;
@ -1041,34 +1075,36 @@ void RE_render_result_rect_from_ibuf(RenderResult *rr, const ImBuf *ibuf, const
{
RenderView *rv = RE_RenderViewGetById(rr, view_id);
ImBuf *rv_ibuf = RE_RenderViewEnsureImBuf(rr, rv);
if (ibuf->float_buffer.data) {
rr->have_combined = true;
if (!rv->combined_buffer.data) {
if (!rv_ibuf->float_buffer.data) {
float *data = MEM_cnew_array<float>(4 * rr->rectx * rr->recty, "render_seq rectf");
RE_RenderBuffer_assign_data(&rv->combined_buffer, data);
IMB_assign_float_buffer(rv_ibuf, data, IB_TAKE_OWNERSHIP);
}
memcpy(rv->combined_buffer.data,
memcpy(rv_ibuf->float_buffer.data,
ibuf->float_buffer.data,
sizeof(float[4]) * rr->rectx * rr->recty);
/* TSK! Since sequence render doesn't free the *rr render result, the old rect32
* can hang around when sequence render has rendered a 32 bits one before */
RE_RenderByteBuffer_data_free(&rv->byte_buffer);
imb_freerectImBuf(rv_ibuf);
}
else if (ibuf->byte_buffer.data) {
rr->have_combined = true;
if (!rv->byte_buffer.data) {
if (!rv_ibuf->byte_buffer.data) {
uint8_t *data = MEM_cnew_array<uint8_t>(4 * rr->rectx * rr->recty, "render_seq rect");
RE_RenderByteBuffer_assign_data(&rv->byte_buffer, data);
IMB_assign_byte_buffer(rv_ibuf, data, IB_TAKE_OWNERSHIP);
}
memcpy(rv->byte_buffer.data, ibuf->byte_buffer.data, sizeof(int) * rr->rectx * rr->recty);
memcpy(rv_ibuf->byte_buffer.data, ibuf->byte_buffer.data, sizeof(int) * rr->rectx * rr->recty);
/* Same things as above, old rectf can hang around from previous render. */
RE_RenderBuffer_data_free(&rv->combined_buffer);
imb_freerectfloatImBuf(rv_ibuf);
}
}
@ -1076,15 +1112,20 @@ void render_result_rect_fill_zero(RenderResult *rr, const int view_id)
{
RenderView *rv = RE_RenderViewGetById(rr, view_id);
if (rv->combined_buffer.data) {
memset(rv->combined_buffer.data, 0, sizeof(float[4]) * rr->rectx * rr->recty);
}
else if (rv->byte_buffer.data) {
memset(rv->byte_buffer.data, 0, 4 * rr->rectx * rr->recty);
}
else {
ImBuf *ibuf = RE_RenderViewEnsureImBuf(rr, rv);
if (!ibuf->float_buffer.data && !ibuf->byte_buffer.data) {
uint8_t *data = MEM_cnew_array<uint8_t>(rr->rectx * rr->recty, "render_seq rect");
RE_RenderByteBuffer_assign_data(&rv->byte_buffer, data);
IMB_assign_byte_buffer(ibuf, data, IB_TAKE_OWNERSHIP);
return;
}
if (ibuf->float_buffer.data) {
memset(ibuf->float_buffer.data, 0, sizeof(float[4]) * rr->rectx * rr->recty);
}
if (ibuf->byte_buffer.data) {
memset(ibuf->byte_buffer.data, 0, 4 * rr->rectx * rr->recty);
}
}
@ -1097,13 +1138,14 @@ void render_result_rect_get_pixels(RenderResult *rr,
const int view_id)
{
RenderView *rv = RE_RenderViewGetById(rr, view_id);
ImBuf *ibuf = rv ? rv->ibuf : nullptr;
if (rv && rv->byte_buffer.data) {
memcpy(rect, rv->byte_buffer.data, sizeof(int) * rr->rectx * rr->recty);
if (ibuf->byte_buffer.data) {
Review

Could be a problem if ibuf is nullptr I think.

Could be a problem if ibuf is nullptr I think.
memcpy(rect, ibuf->byte_buffer.data, sizeof(int) * rr->rectx * rr->recty);
}
else if (rv && rv->combined_buffer.data) {
else if (ibuf->float_buffer.data) {
IMB_display_buffer_transform_apply((uchar *)rect,
rv->combined_buffer.data,
ibuf->float_buffer.data,
rr->rectx,
rr->recty,
4,
@ -1130,13 +1172,17 @@ bool RE_HasCombinedLayer(const RenderResult *result)
return false;
}
return (rv->byte_buffer.data || rv->combined_buffer.data);
return (rv->ibuf);
}
bool RE_HasFloatPixels(const RenderResult *result)
{
LISTBASE_FOREACH (const RenderView *, rview, &result->views) {
if (rview->byte_buffer.data && !rview->combined_buffer.data) {
ImBuf *ibuf = rview->ibuf;
if (!ibuf) {
continue;
}
if (ibuf->byte_buffer.data && !ibuf->float_buffer.data) {
return false;
}
}
@ -1177,9 +1223,7 @@ static RenderPass *duplicate_render_pass(RenderPass *rpass)
RenderPass *new_rpass = MEM_cnew<RenderPass>("new render pass", *rpass);
new_rpass->next = new_rpass->prev = nullptr;
if (new_rpass->buffer.sharing_info != nullptr) {
new_rpass->buffer.sharing_info->add_user();
}
new_rpass->ibuf = IMB_dupImBuf(rpass->ibuf);
return new_rpass;
}
@ -1201,19 +1245,7 @@ static RenderView *duplicate_render_view(RenderView *rview)
{
RenderView *new_rview = MEM_cnew<RenderView>("new render view", *rview);
/* Reset buffers, they are not supposed to be shallow-coped. */
new_rview->combined_buffer = {};
new_rview->byte_buffer = {};
if (rview->combined_buffer.data != nullptr) {
RE_RenderBuffer_assign_data(&new_rview->combined_buffer,
static_cast<float *>(MEM_dupallocN(rview->combined_buffer.data)));
}
if (rview->byte_buffer.data != nullptr) {
RE_RenderByteBuffer_assign_data(
&new_rview->byte_buffer, static_cast<uint8_t *>(MEM_dupallocN(rview->byte_buffer.data)));
}
new_rview->ibuf = IMB_dupImBuf(rview->ibuf);
return new_rview;
}
@ -1233,115 +1265,27 @@ RenderResult *RE_DuplicateRenderResult(RenderResult *rr)
BLI_addtail(&new_rr->views, new_rview);
}
/* Reset buffers, they are not supposed to be shallow-coped. */
new_rr->combined_buffer = {};
new_rr->byte_buffer = {};
if (rr->combined_buffer.data) {
RE_RenderBuffer_assign_data(&new_rr->combined_buffer,
static_cast<float *>(MEM_dupallocN(rr->combined_buffer.data)));
}
if (rr->byte_buffer.data) {
RE_RenderByteBuffer_assign_data(&new_rr->byte_buffer,
static_cast<uint8_t *>(MEM_dupallocN(rr->byte_buffer.data)));
}
new_rr->ibuf = IMB_dupImBuf(rr->ibuf);
new_rr->stamp_data = BKE_stamp_data_copy(new_rr->stamp_data);
return new_rr;
}
/* --------------------------------------------------------------------
* Render buffer.
*/
template<class BufferType> static BufferType render_buffer_new(decltype(BufferType::data) data)
ImBuf *RE_RenderPassEnsureImBuf(RenderPass *render_pass)
{
BufferType buffer;
buffer.data = data;
buffer.sharing_info = blender::implicit_sharing::info_for_mem_free(data);
buffer.gpu_texture = nullptr;
return buffer;
}
template<class BufferType> static void render_buffer_data_free(BufferType *render_buffer)
{
if (!render_buffer->sharing_info) {
MEM_SAFE_FREE(render_buffer->data);
return;
if (!render_pass->ibuf) {
render_pass->ibuf = IMB_allocImBuf(render_pass->rectx, render_pass->recty, 32, 0);
render_pass->ibuf->channels = render_pass->channels;
}
blender::implicit_sharing::free_shared_data(&render_buffer->data, &render_buffer->sharing_info);
if (render_buffer->gpu_texture) {
GPU_texture_free(render_buffer->gpu_texture);
render_buffer->gpu_texture = nullptr;
}
return render_pass->ibuf;
}
template<class BufferType>
static void render_buffer_assign_data(BufferType *render_buffer, decltype(BufferType::data) data)
ImBuf *RE_RenderViewEnsureImBuf(const RenderResult *render_result, RenderView *render_view)
{
render_buffer_data_free(render_buffer);
if (!data) {
render_buffer->data = nullptr;
render_buffer->sharing_info = nullptr;
return;
if (!render_view->ibuf) {
render_view->ibuf = IMB_allocImBuf(render_result->rectx, render_result->recty, 32, 0);
}
render_buffer->data = data;
render_buffer->sharing_info = blender::implicit_sharing::info_for_mem_free(data);
}
template<class BufferType>
static void render_buffer_assign_shared(BufferType *lhs, const BufferType *rhs)
{
render_buffer_data_free(lhs);
if (rhs) {
blender::implicit_sharing::copy_shared_pointer(
rhs->data, rhs->sharing_info, &lhs->data, &lhs->sharing_info);
}
}
RenderBuffer RE_RenderBuffer_new(float *data)
{
return render_buffer_new<RenderBuffer>(data);
}
void RE_RenderBuffer_assign_data(RenderBuffer *render_buffer, float *data)
{
return render_buffer_assign_data(render_buffer, data);
}
void RE_RenderBuffer_assign_shared(RenderBuffer *lhs, const RenderBuffer *rhs)
{
render_buffer_assign_shared(lhs, rhs);
}
void RE_RenderBuffer_data_free(RenderBuffer *render_buffer)
{
render_buffer_data_free(render_buffer);
}
RenderByteBuffer RE_RenderByteBuffer_new(uint8_t *data)
{
return render_buffer_new<RenderByteBuffer>(data);
}
void RE_RenderByteBuffer_assign_data(RenderByteBuffer *render_buffer, uint8_t *data)
{
return render_buffer_assign_data(render_buffer, data);
}
void RE_RenderByteBuffer_assign_shared(RenderByteBuffer *lhs, const RenderByteBuffer *rhs)
{
render_buffer_assign_shared(lhs, rhs);
}
void RE_RenderByteBuffer_data_free(RenderByteBuffer *render_buffer)
{
render_buffer_data_free(render_buffer);
}
return render_view->ibuf;
}

View File

@ -1577,18 +1577,18 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context,
RE_AcquireResultImage(re, &rres, view_id);
if (rres.combined_buffer.data) {
if (rres.ibuf && rres.ibuf->float_buffer.data) {
ibufs_arr[view_id] = IMB_allocImBuf(rres.rectx, rres.recty, 32, 0);
IMB_assign_shared_float_buffer(
ibufs_arr[view_id], rres.combined_buffer.data, rres.combined_buffer.sharing_info);
IMB_assign_float_buffer(
ibufs_arr[view_id], rres.ibuf->float_buffer.data, IB_DO_NOT_TAKE_OWNERSHIP);
/* float buffers in the sequencer are not linear */
seq_imbuf_to_sequencer_space(context->scene, ibufs_arr[view_id], false);
}
else if (rres.byte_buffer.data) {
else if (rres.ibuf->byte_buffer.data) {
ibufs_arr[view_id] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rect);
memcpy(ibufs_arr[view_id]->byte_buffer.data,
rres.byte_buffer.data,
rres.ibuf->byte_buffer.data,
4 * rres.rectx * rres.recty);
}