/* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ #include "integrator/pass_accessor.h" #include "session/buffers.h" #include "util/log.h" // clang-format off #include "kernel/device/cpu/compat.h" #include "kernel/types.h" // clang-format on CCL_NAMESPACE_BEGIN /* -------------------------------------------------------------------- * Pass input information. */ PassAccessor::PassAccessInfo::PassAccessInfo(const BufferPass &pass) : type(pass.type), mode(pass.mode), include_albedo(pass.include_albedo), is_lightgroup(!pass.lightgroup.empty()), offset(pass.offset) { } /* -------------------------------------------------------------------- * Pass destination. */ PassAccessor::Destination::Destination(float *pixels, int num_components) : pixels(pixels), num_components(num_components) { } PassAccessor::Destination::Destination(const PassType pass_type, half4 *pixels) : Destination(pass_type) { pixels_half_rgba = pixels; } PassAccessor::Destination::Destination(const PassType pass_type) { const PassInfo pass_info = Pass::get_info(pass_type); num_components = pass_info.num_components; } /* -------------------------------------------------------------------- * Pass source. */ PassAccessor::Source::Source(const float *pixels, int num_components) : pixels(pixels), num_components(num_components) { } /* -------------------------------------------------------------------- * Pass accessor. */ PassAccessor::PassAccessor(const PassAccessInfo &pass_access_info, float exposure, int num_samples) : pass_access_info_(pass_access_info), exposure_(exposure), num_samples_(num_samples) { } bool PassAccessor::get_render_tile_pixels(const RenderBuffers *render_buffers, const Destination &destination) const { if (render_buffers == nullptr || render_buffers->buffer.data() == nullptr) { return false; } return get_render_tile_pixels(render_buffers, render_buffers->params, destination); } static void pad_pixels(const BufferParams &buffer_params, const PassAccessor::Destination &destination, const int src_num_components) { /* When requesting a single channel pass as RGBA, or RGB pass as RGBA, * fill in the additional components for convenience. */ const int dest_num_components = destination.num_components; if (src_num_components >= dest_num_components) { return; } const size_t size = static_cast(buffer_params.width) * buffer_params.height; if (destination.pixels) { const size_t pixel_stride = destination.pixel_stride ? destination.pixel_stride : destination.num_components; float *pixel = destination.pixels + pixel_stride * destination.offset; for (size_t i = 0; i < size; i++, pixel += dest_num_components) { if (dest_num_components >= 3 && src_num_components == 1) { pixel[1] = pixel[0]; pixel[2] = pixel[0]; } if (dest_num_components >= 4) { pixel[3] = 1.0f; } } } if (destination.pixels_half_rgba) { const half one = float_to_half_display(1.0f); half4 *pixel = destination.pixels_half_rgba + destination.offset; for (size_t i = 0; i < size; i++, pixel++) { if (dest_num_components >= 3 && src_num_components == 1) { pixel[0].y = pixel[0].x; pixel[0].z = pixel[0].x; } if (dest_num_components >= 4) { pixel[0].w = one; } } } } bool PassAccessor::get_render_tile_pixels(const RenderBuffers *render_buffers, const BufferParams &buffer_params, const Destination &destination) const { if (render_buffers == nullptr || render_buffers->buffer.data() == nullptr) { return false; } const PassType type = pass_access_info_.type; const PassMode mode = pass_access_info_.mode; const PassInfo pass_info = Pass::get_info( type, pass_access_info_.include_albedo, pass_access_info_.is_lightgroup); int num_written_components = pass_info.num_components; if (pass_info.num_components == 1) { /* Single channel passes. */ if (mode == PassMode::DENOISED) { /* Denoised passes store their final pixels, no need in special calculation. */ get_pass_float(render_buffers, buffer_params, destination); } else if (type == PASS_DEPTH) { get_pass_depth(render_buffers, buffer_params, destination); } else if (type == PASS_MIST) { get_pass_mist(render_buffers, buffer_params, destination); } else if (type == PASS_SAMPLE_COUNT) { get_pass_sample_count(render_buffers, buffer_params, destination); } else { get_pass_float(render_buffers, buffer_params, destination); } } else if (type == PASS_MOTION) { /* Motion pass. */ DCHECK_EQ(destination.num_components, 4) << "Motion pass must have 4 components"; get_pass_motion(render_buffers, buffer_params, destination); } else if (type == PASS_CRYPTOMATTE) { /* Cryptomatte pass. */ DCHECK_EQ(destination.num_components, 4) << "Cryptomatte pass must have 4 components"; get_pass_cryptomatte(render_buffers, buffer_params, destination); } else { /* RGB, RGBA and vector passes. */ DCHECK(destination.num_components == 3 || destination.num_components == 4) << pass_type_as_string(type) << " pass must have 3 or 4 components"; if (type == PASS_SHADOW_CATCHER_MATTE && pass_access_info_.use_approximate_shadow_catcher) { /* Denoised matte with shadow needs to do calculation (will use denoised shadow catcher pass * to approximate shadow with). */ get_pass_shadow_catcher_matte_with_shadow(render_buffers, buffer_params, destination); } else if (type == PASS_SHADOW_CATCHER && mode != PassMode::DENOISED) { /* Shadow catcher pass. */ get_pass_shadow_catcher(render_buffers, buffer_params, destination); } else if ((pass_info.divide_type != PASS_NONE || pass_info.direct_type != PASS_NONE || pass_info.indirect_type != PASS_NONE) && mode != PassMode::DENOISED) { /* RGB lighting passes that need to divide out color and/or sum direct and indirect. * These can also optionally write alpha like the combined pass. */ get_pass_light_path(render_buffers, buffer_params, destination); num_written_components = 4; } else { /* Passes that need no special computation, or denoised passes that already * had the computation done. */ if (pass_info.num_components == 3) { get_pass_float3(render_buffers, buffer_params, destination); } else if (pass_info.num_components == 4) { if (destination.num_components == 3) { /* Special case for denoiser access of RGBA passes ignoring alpha channel. */ get_pass_float3(render_buffers, buffer_params, destination); } else if (type == PASS_COMBINED || type == PASS_SHADOW_CATCHER || type == PASS_SHADOW_CATCHER_MATTE) { /* Passes with transparency as 4th component. */ get_pass_combined(render_buffers, buffer_params, destination); } else { /* Passes with alpha as 4th component. */ get_pass_float4(render_buffers, buffer_params, destination); } } } } pad_pixels(buffer_params, destination, num_written_components); return true; } void PassAccessor::init_kernel_film_convert(KernelFilmConvert *kfilm_convert, const BufferParams &buffer_params, const Destination &destination) const { const PassMode mode = pass_access_info_.mode; const PassInfo &pass_info = Pass::get_info( pass_access_info_.type, pass_access_info_.include_albedo, pass_access_info_.is_lightgroup); kfilm_convert->pass_offset = pass_access_info_.offset; kfilm_convert->pass_stride = buffer_params.pass_stride; kfilm_convert->pass_use_exposure = pass_info.use_exposure; kfilm_convert->pass_use_filter = pass_info.use_filter; /* TODO(sergey): Some of the passes needs to become denoised when denoised pass is accessed. */ if (pass_info.direct_type != PASS_NONE) { kfilm_convert->pass_offset = buffer_params.get_pass_offset(pass_info.direct_type); } kfilm_convert->pass_indirect = buffer_params.get_pass_offset(pass_info.indirect_type); kfilm_convert->pass_divide = buffer_params.get_pass_offset(pass_info.divide_type); kfilm_convert->pass_combined = buffer_params.get_pass_offset(PASS_COMBINED); kfilm_convert->pass_sample_count = buffer_params.get_pass_offset(PASS_SAMPLE_COUNT); kfilm_convert->pass_adaptive_aux_buffer = buffer_params.get_pass_offset( PASS_ADAPTIVE_AUX_BUFFER); kfilm_convert->pass_motion_weight = buffer_params.get_pass_offset(PASS_MOTION_WEIGHT); kfilm_convert->pass_shadow_catcher = buffer_params.get_pass_offset(PASS_SHADOW_CATCHER, mode); kfilm_convert->pass_shadow_catcher_sample_count = buffer_params.get_pass_offset( PASS_SHADOW_CATCHER_SAMPLE_COUNT); kfilm_convert->pass_shadow_catcher_matte = buffer_params.get_pass_offset( PASS_SHADOW_CATCHER_MATTE, mode); /* Background is not denoised, so always use noisy pass. */ kfilm_convert->pass_background = buffer_params.get_pass_offset(PASS_BACKGROUND); if (pass_info.use_filter) { kfilm_convert->scale = num_samples_ != 0 ? 1.0f / num_samples_ : 0.0f; } else { kfilm_convert->scale = 1.0f; } if (pass_info.use_exposure) { kfilm_convert->exposure = exposure_; } else { kfilm_convert->exposure = 1.0f; } kfilm_convert->scale_exposure = kfilm_convert->scale * kfilm_convert->exposure; kfilm_convert->use_approximate_shadow_catcher = pass_access_info_.use_approximate_shadow_catcher; kfilm_convert->use_approximate_shadow_catcher_background = pass_access_info_.use_approximate_shadow_catcher_background; kfilm_convert->show_active_pixels = pass_access_info_.show_active_pixels; kfilm_convert->num_components = destination.num_components; kfilm_convert->pixel_stride = destination.pixel_stride ? destination.pixel_stride : destination.num_components; kfilm_convert->is_denoised = (mode == PassMode::DENOISED); } bool PassAccessor::set_render_tile_pixels(RenderBuffers *render_buffers, const Source &source) { if (render_buffers == nullptr || render_buffers->buffer.data() == nullptr) { return false; } const PassInfo pass_info = Pass::get_info( pass_access_info_.type, pass_access_info_.include_albedo, pass_access_info_.is_lightgroup); const BufferParams &buffer_params = render_buffers->params; float *buffer_data = render_buffers->buffer.data(); const int size = buffer_params.width * buffer_params.height; const int out_stride = buffer_params.pass_stride; const int in_stride = source.num_components; const int num_components_to_copy = min(source.num_components, pass_info.num_components); float *out = buffer_data + pass_access_info_.offset; const float *in = source.pixels + source.offset * in_stride; for (int i = 0; i < size; i++, out += out_stride, in += in_stride) { memcpy(out, in, sizeof(float) * num_components_to_copy); } return true; } CCL_NAMESPACE_END