385 lines
9.9 KiB
C++
385 lines
9.9 KiB
C++
/*
|
|
* Copyright 2011-2013 Blender Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "device/device.h"
|
|
#include "session/buffers.h"
|
|
|
|
#include "util/foreach.h"
|
|
#include "util/hash.h"
|
|
#include "util/math.h"
|
|
#include "util/time.h"
|
|
#include "util/types.h"
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
/* --------------------------------------------------------------------
|
|
* Convert part information to an index of `BufferParams::pass_offset_`.
|
|
*/
|
|
|
|
static int pass_type_mode_to_index(PassType pass_type, PassMode mode)
|
|
{
|
|
int index = static_cast<int>(pass_type) * 2;
|
|
|
|
if (mode == PassMode::DENOISED) {
|
|
++index;
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
static int pass_to_index(const BufferPass &pass)
|
|
{
|
|
return pass_type_mode_to_index(pass.type, pass.mode);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------
|
|
* Buffer pass.
|
|
*/
|
|
|
|
NODE_DEFINE(BufferPass)
|
|
{
|
|
NodeType *type = NodeType::add("buffer_pass", create);
|
|
|
|
const NodeEnum *pass_type_enum = Pass::get_type_enum();
|
|
const NodeEnum *pass_mode_enum = Pass::get_mode_enum();
|
|
|
|
SOCKET_ENUM(type, "Type", *pass_type_enum, PASS_COMBINED);
|
|
SOCKET_ENUM(mode, "Mode", *pass_mode_enum, static_cast<int>(PassMode::DENOISED));
|
|
SOCKET_STRING(name, "Name", ustring());
|
|
SOCKET_BOOLEAN(include_albedo, "Include Albedo", false);
|
|
|
|
SOCKET_INT(offset, "Offset", -1);
|
|
|
|
return type;
|
|
}
|
|
|
|
BufferPass::BufferPass() : Node(get_node_type())
|
|
{
|
|
}
|
|
|
|
BufferPass::BufferPass(const Pass *scene_pass)
|
|
: Node(get_node_type()),
|
|
type(scene_pass->get_type()),
|
|
mode(scene_pass->get_mode()),
|
|
name(scene_pass->get_name()),
|
|
include_albedo(scene_pass->get_include_albedo())
|
|
{
|
|
}
|
|
|
|
PassInfo BufferPass::get_info() const
|
|
{
|
|
return Pass::get_info(type, include_albedo);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------
|
|
* Buffer Params.
|
|
*/
|
|
|
|
NODE_DEFINE(BufferParams)
|
|
{
|
|
NodeType *type = NodeType::add("buffer_params", create);
|
|
|
|
SOCKET_INT(width, "Width", 0);
|
|
SOCKET_INT(height, "Height", 0);
|
|
|
|
SOCKET_INT(window_x, "Window X", 0);
|
|
SOCKET_INT(window_y, "Window Y", 0);
|
|
SOCKET_INT(window_width, "Window Width", 0);
|
|
SOCKET_INT(window_height, "Window Height", 0);
|
|
|
|
SOCKET_INT(full_x, "Full X", 0);
|
|
SOCKET_INT(full_y, "Full Y", 0);
|
|
SOCKET_INT(full_width, "Full Width", 0);
|
|
SOCKET_INT(full_height, "Full Height", 0);
|
|
|
|
SOCKET_STRING(layer, "Layer", ustring());
|
|
SOCKET_STRING(view, "View", ustring());
|
|
SOCKET_INT(samples, "Samples", 0);
|
|
SOCKET_FLOAT(exposure, "Exposure", 1.0f);
|
|
SOCKET_BOOLEAN(use_approximate_shadow_catcher, "Use Approximate Shadow Catcher", false);
|
|
SOCKET_BOOLEAN(use_transparent_background, "Transparent Background", false);
|
|
|
|
/* Notes:
|
|
* - Skip passes since they do not follow typical container socket definition.
|
|
* Might look into covering those as a socket in the future.
|
|
*
|
|
* - Skip offset, stride, and pass stride since those can be delivered from the passes and
|
|
* rest of the sockets. */
|
|
|
|
return type;
|
|
}
|
|
|
|
BufferParams::BufferParams() : Node(get_node_type())
|
|
{
|
|
reset_pass_offset();
|
|
}
|
|
|
|
void BufferParams::update_passes()
|
|
{
|
|
update_offset_stride();
|
|
reset_pass_offset();
|
|
|
|
pass_stride = 0;
|
|
for (const BufferPass &pass : passes) {
|
|
if (pass.offset != PASS_UNUSED) {
|
|
const int index = pass_to_index(pass);
|
|
if (pass_offset_[index] == PASS_UNUSED) {
|
|
pass_offset_[index] = pass_stride;
|
|
}
|
|
|
|
pass_stride += pass.get_info().num_components;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BufferParams::update_passes(const vector<Pass *> &scene_passes)
|
|
{
|
|
passes.clear();
|
|
|
|
pass_stride = 0;
|
|
for (const Pass *scene_pass : scene_passes) {
|
|
BufferPass buffer_pass(scene_pass);
|
|
|
|
if (scene_pass->is_written()) {
|
|
buffer_pass.offset = pass_stride;
|
|
pass_stride += scene_pass->get_info().num_components;
|
|
}
|
|
else {
|
|
buffer_pass.offset = PASS_UNUSED;
|
|
}
|
|
|
|
passes.emplace_back(std::move(buffer_pass));
|
|
}
|
|
|
|
update_passes();
|
|
}
|
|
|
|
void BufferParams::reset_pass_offset()
|
|
{
|
|
for (int i = 0; i < kNumPassOffsets; ++i) {
|
|
pass_offset_[i] = PASS_UNUSED;
|
|
}
|
|
}
|
|
|
|
int BufferParams::get_pass_offset(PassType pass_type, PassMode mode) const
|
|
{
|
|
if (pass_type == PASS_NONE || pass_type == PASS_UNUSED) {
|
|
return PASS_UNUSED;
|
|
}
|
|
|
|
const int index = pass_type_mode_to_index(pass_type, mode);
|
|
return pass_offset_[index];
|
|
}
|
|
|
|
const BufferPass *BufferParams::find_pass(string_view name) const
|
|
{
|
|
for (const BufferPass &pass : passes) {
|
|
if (pass.name == name) {
|
|
return &pass;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
const BufferPass *BufferParams::find_pass(PassType type, PassMode mode) const
|
|
{
|
|
for (const BufferPass &pass : passes) {
|
|
if (pass.type == type && pass.mode == mode) {
|
|
return &pass;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
const BufferPass *BufferParams::get_actual_display_pass(PassType type, PassMode mode) const
|
|
{
|
|
const BufferPass *pass = find_pass(type, mode);
|
|
return get_actual_display_pass(pass);
|
|
}
|
|
|
|
const BufferPass *BufferParams::get_actual_display_pass(const BufferPass *pass) const
|
|
{
|
|
if (!pass) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (pass->type == PASS_COMBINED) {
|
|
const BufferPass *shadow_catcher_matte_pass = find_pass(PASS_SHADOW_CATCHER_MATTE, pass->mode);
|
|
if (shadow_catcher_matte_pass) {
|
|
pass = shadow_catcher_matte_pass;
|
|
}
|
|
}
|
|
|
|
return pass;
|
|
}
|
|
|
|
void BufferParams::update_offset_stride()
|
|
{
|
|
offset = -(full_x + full_y * width);
|
|
stride = width;
|
|
}
|
|
|
|
bool BufferParams::modified(const BufferParams &other) const
|
|
{
|
|
if (width != other.width || height != other.height) {
|
|
return true;
|
|
}
|
|
|
|
if (full_x != other.full_x || full_y != other.full_y || full_width != other.full_width ||
|
|
full_height != other.full_height) {
|
|
return true;
|
|
}
|
|
|
|
if (window_x != other.window_x || window_y != other.window_y ||
|
|
window_width != other.window_width || window_height != other.window_height) {
|
|
return true;
|
|
}
|
|
|
|
if (offset != other.offset || stride != other.stride || pass_stride != other.pass_stride) {
|
|
return true;
|
|
}
|
|
|
|
if (layer != other.layer || view != other.view) {
|
|
return true;
|
|
}
|
|
|
|
if (exposure != other.exposure ||
|
|
use_approximate_shadow_catcher != other.use_approximate_shadow_catcher ||
|
|
use_transparent_background != other.use_transparent_background) {
|
|
return true;
|
|
}
|
|
|
|
return !(passes == other.passes);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------
|
|
* Render Buffers.
|
|
*/
|
|
|
|
RenderBuffers::RenderBuffers(Device *device) : buffer(device, "RenderBuffers", MEM_READ_WRITE)
|
|
{
|
|
}
|
|
|
|
RenderBuffers::~RenderBuffers()
|
|
{
|
|
buffer.free();
|
|
}
|
|
|
|
void RenderBuffers::reset(const BufferParams ¶ms_)
|
|
{
|
|
DCHECK(params_.pass_stride != -1);
|
|
|
|
params = params_;
|
|
|
|
/* re-allocate buffer */
|
|
buffer.alloc(params.width * params.pass_stride, params.height);
|
|
}
|
|
|
|
void RenderBuffers::zero()
|
|
{
|
|
buffer.zero_to_device();
|
|
}
|
|
|
|
bool RenderBuffers::copy_from_device()
|
|
{
|
|
DCHECK(params.pass_stride != -1);
|
|
|
|
if (!buffer.device_pointer)
|
|
return false;
|
|
|
|
buffer.copy_from_device(0, params.width * params.pass_stride, params.height);
|
|
|
|
return true;
|
|
}
|
|
|
|
void RenderBuffers::copy_to_device()
|
|
{
|
|
buffer.copy_to_device();
|
|
}
|
|
|
|
void render_buffers_host_copy_denoised(RenderBuffers *dst,
|
|
const BufferParams &dst_params,
|
|
const RenderBuffers *src,
|
|
const BufferParams &src_params,
|
|
const size_t src_offset)
|
|
{
|
|
DCHECK_EQ(dst_params.width, src_params.width);
|
|
/* TODO(sergey): More sanity checks to avoid buffer overrun. */
|
|
|
|
/* Create a map of pass offsets to be copied.
|
|
* Assume offsets are different to allow copying passes between buffers with different set of
|
|
* passes. */
|
|
|
|
struct {
|
|
int dst_offset;
|
|
int src_offset;
|
|
} pass_offsets[PASS_NUM];
|
|
|
|
int num_passes = 0;
|
|
|
|
for (int i = 0; i < PASS_NUM; ++i) {
|
|
const PassType pass_type = static_cast<PassType>(i);
|
|
|
|
const int dst_pass_offset = dst_params.get_pass_offset(pass_type, PassMode::DENOISED);
|
|
if (dst_pass_offset == PASS_UNUSED) {
|
|
continue;
|
|
}
|
|
|
|
const int src_pass_offset = src_params.get_pass_offset(pass_type, PassMode::DENOISED);
|
|
if (src_pass_offset == PASS_UNUSED) {
|
|
continue;
|
|
}
|
|
|
|
pass_offsets[num_passes].dst_offset = dst_pass_offset;
|
|
pass_offsets[num_passes].src_offset = src_pass_offset;
|
|
++num_passes;
|
|
}
|
|
|
|
/* Copy passes. */
|
|
/* TODO(sergey): Make it more reusable, allowing implement copy of noisy passes. */
|
|
|
|
const int64_t dst_width = dst_params.width;
|
|
const int64_t dst_height = dst_params.height;
|
|
const int64_t dst_pass_stride = dst_params.pass_stride;
|
|
const int64_t dst_num_pixels = dst_width * dst_height;
|
|
|
|
const int64_t src_pass_stride = src_params.pass_stride;
|
|
const int64_t src_offset_in_floats = src_offset * src_pass_stride;
|
|
|
|
const float *src_pixel = src->buffer.data() + src_offset_in_floats;
|
|
float *dst_pixel = dst->buffer.data();
|
|
|
|
for (int i = 0; i < dst_num_pixels;
|
|
++i, src_pixel += src_pass_stride, dst_pixel += dst_pass_stride) {
|
|
for (int pass_offset_idx = 0; pass_offset_idx < num_passes; ++pass_offset_idx) {
|
|
const int dst_pass_offset = pass_offsets[pass_offset_idx].dst_offset;
|
|
const int src_pass_offset = pass_offsets[pass_offset_idx].src_offset;
|
|
|
|
/* TODO(sergey): Support non-RGBA passes. */
|
|
dst_pixel[dst_pass_offset + 0] = src_pixel[src_pass_offset + 0];
|
|
dst_pixel[dst_pass_offset + 1] = src_pixel[src_pass_offset + 1];
|
|
dst_pixel[dst_pass_offset + 2] = src_pixel[src_pass_offset + 2];
|
|
dst_pixel[dst_pass_offset + 3] = src_pixel[src_pass_offset + 3];
|
|
}
|
|
}
|
|
}
|
|
|
|
CCL_NAMESPACE_END
|