Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/cycles/render/tile.cpp')
-rw-r--r--intern/cycles/render/tile.cpp629
1 files changed, 0 insertions, 629 deletions
diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp
deleted file mode 100644
index 3070e93eff3..00000000000
--- a/intern/cycles/render/tile.cpp
+++ /dev/null
@@ -1,629 +0,0 @@
-/*
- * 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 "render/tile.h"
-
-#include <atomic>
-
-#include "graph/node.h"
-#include "render/background.h"
-#include "render/film.h"
-#include "render/integrator.h"
-#include "render/scene.h"
-#include "util/util_algorithm.h"
-#include "util/util_foreach.h"
-#include "util/util_logging.h"
-#include "util/util_path.h"
-#include "util/util_string.h"
-#include "util/util_system.h"
-#include "util/util_types.h"
-
-CCL_NAMESPACE_BEGIN
-
-/* --------------------------------------------------------------------
- * Internal functions.
- */
-
-static const char *ATTR_PASSES_COUNT = "cycles.passes.count";
-static const char *ATTR_PASS_SOCKET_PREFIX_FORMAT = "cycles.passes.%d.";
-static const char *ATTR_BUFFER_SOCKET_PREFIX = "cycles.buffer.";
-static const char *ATTR_DENOISE_SOCKET_PREFIX = "cycles.denoise.";
-
-/* Global counter of ToleManager object instances. */
-static std::atomic<uint64_t> g_instance_index = 0;
-
-/* Construct names of EXR channels which will ensure order of all channels to match exact offsets
- * in render buffers corresponding to the given passes.
- *
- * Returns `std` datatypes so that it can be assigned directly to the OIIO's `ImageSpec`. */
-static std::vector<std::string> exr_channel_names_for_passes(const BufferParams &buffer_params)
-{
- static const char *component_suffixes[] = {"R", "G", "B", "A"};
-
- int pass_index = 0;
- int num_channels = 0;
- std::vector<std::string> channel_names;
- for (const BufferPass &pass : buffer_params.passes) {
- if (pass.offset == PASS_UNUSED) {
- continue;
- }
-
- const PassInfo pass_info = pass.get_info();
- num_channels += pass_info.num_components;
-
- /* EXR canonically expects first part of channel names to be sorted alphabetically, which is
- * not guaranteed to be the case with passes names. Assign a prefix based on the pass index
- * with a fixed width to ensure ordering. This makes it possible to dump existing render
- * buffers memory to disk and read it back without doing extra mapping. */
- const string prefix = string_printf("%08d", pass_index);
-
- const string channel_name_prefix = prefix + string(pass.name) + ".";
-
- for (int i = 0; i < pass_info.num_components; ++i) {
- channel_names.push_back(channel_name_prefix + component_suffixes[i]);
- }
-
- ++pass_index;
- }
-
- return channel_names;
-}
-
-inline string node_socket_attribute_name(const SocketType &socket, const string &attr_name_prefix)
-{
- return attr_name_prefix + string(socket.name);
-}
-
-template<typename ValidateValueFunc, typename GetValueFunc>
-static bool node_socket_generic_to_image_spec_atttributes(
- ImageSpec *image_spec,
- const Node *node,
- const SocketType &socket,
- const string &attr_name_prefix,
- const ValidateValueFunc &validate_value_func,
- const GetValueFunc &get_value_func)
-{
- if (!validate_value_func(node, socket)) {
- return false;
- }
-
- image_spec->attribute(node_socket_attribute_name(socket, attr_name_prefix),
- get_value_func(node, socket));
-
- return true;
-}
-
-static bool node_socket_to_image_spec_atttributes(ImageSpec *image_spec,
- const Node *node,
- const SocketType &socket,
- const string &attr_name_prefix)
-{
- const string attr_name = node_socket_attribute_name(socket, attr_name_prefix);
-
- switch (socket.type) {
- case SocketType::ENUM: {
- const ustring value = node->get_string(socket);
-
- /* Validate that the node is consistent with the node type definition. */
- const NodeEnum &enum_values = *socket.enum_values;
- if (!enum_values.exists(value)) {
- LOG(DFATAL) << "Node enum contains invalid value " << value;
- return false;
- }
-
- image_spec->attribute(attr_name, value);
-
- return true;
- }
-
- case SocketType::STRING:
- image_spec->attribute(attr_name, node->get_string(socket));
- return true;
-
- case SocketType::INT:
- image_spec->attribute(attr_name, node->get_int(socket));
- return true;
-
- case SocketType::FLOAT:
- image_spec->attribute(attr_name, node->get_float(socket));
- return true;
-
- case SocketType::BOOLEAN:
- image_spec->attribute(attr_name, node->get_bool(socket));
- return true;
-
- default:
- LOG(DFATAL) << "Unhandled socket type " << socket.type << ", should never happen.";
- return false;
- }
-}
-
-static bool node_socket_from_image_spec_atttributes(Node *node,
- const SocketType &socket,
- const ImageSpec &image_spec,
- const string &attr_name_prefix)
-{
- const string attr_name = node_socket_attribute_name(socket, attr_name_prefix);
-
- switch (socket.type) {
- case SocketType::ENUM: {
- /* TODO(sergey): Avoid construction of `ustring` by using `string_view` in the Node API. */
- const ustring value(image_spec.get_string_attribute(attr_name, ""));
-
- /* Validate that the node is consistent with the node type definition. */
- const NodeEnum &enum_values = *socket.enum_values;
- if (!enum_values.exists(value)) {
- LOG(ERROR) << "Invalid enumerator value " << value;
- return false;
- }
-
- node->set(socket, enum_values[value]);
-
- return true;
- }
-
- case SocketType::STRING:
- /* TODO(sergey): Avoid construction of `ustring` by using `string_view` in the Node API. */
- node->set(socket, ustring(image_spec.get_string_attribute(attr_name, "")));
- return true;
-
- case SocketType::INT:
- node->set(socket, image_spec.get_int_attribute(attr_name, 0));
- return true;
-
- case SocketType::FLOAT:
- node->set(socket, image_spec.get_float_attribute(attr_name, 0));
- return true;
-
- case SocketType::BOOLEAN:
- node->set(socket, static_cast<bool>(image_spec.get_int_attribute(attr_name, 0)));
- return true;
-
- default:
- LOG(DFATAL) << "Unhandled socket type " << socket.type << ", should never happen.";
- return false;
- }
-}
-
-static bool node_to_image_spec_atttributes(ImageSpec *image_spec,
- const Node *node,
- const string &attr_name_prefix)
-{
- for (const SocketType &socket : node->type->inputs) {
- if (!node_socket_to_image_spec_atttributes(image_spec, node, socket, attr_name_prefix)) {
- return false;
- }
- }
-
- return true;
-}
-
-static bool node_from_image_spec_atttributes(Node *node,
- const ImageSpec &image_spec,
- const string &attr_name_prefix)
-{
- for (const SocketType &socket : node->type->inputs) {
- if (!node_socket_from_image_spec_atttributes(node, socket, image_spec, attr_name_prefix)) {
- return false;
- }
- }
-
- return true;
-}
-
-static bool buffer_params_to_image_spec_atttributes(ImageSpec *image_spec,
- const BufferParams &buffer_params)
-{
- if (!node_to_image_spec_atttributes(image_spec, &buffer_params, ATTR_BUFFER_SOCKET_PREFIX)) {
- return false;
- }
-
- /* Passes storage is not covered by the node socket. so "expand" the loop manually. */
-
- const int num_passes = buffer_params.passes.size();
- image_spec->attribute(ATTR_PASSES_COUNT, num_passes);
-
- for (int pass_index = 0; pass_index < num_passes; ++pass_index) {
- const string attr_name_prefix = string_printf(ATTR_PASS_SOCKET_PREFIX_FORMAT, pass_index);
-
- const BufferPass *pass = &buffer_params.passes[pass_index];
- if (!node_to_image_spec_atttributes(image_spec, pass, attr_name_prefix)) {
- return false;
- }
- }
-
- return true;
-}
-
-static bool buffer_params_from_image_spec_atttributes(BufferParams *buffer_params,
- const ImageSpec &image_spec)
-{
- if (!node_from_image_spec_atttributes(buffer_params, image_spec, ATTR_BUFFER_SOCKET_PREFIX)) {
- return false;
- }
-
- /* Passes storage is not covered by the node socket. so "expand" the loop manually. */
-
- const int num_passes = image_spec.get_int_attribute(ATTR_PASSES_COUNT, 0);
- if (num_passes == 0) {
- LOG(ERROR) << "Missing passes count attribute.";
- return false;
- }
-
- for (int pass_index = 0; pass_index < num_passes; ++pass_index) {
- const string attr_name_prefix = string_printf(ATTR_PASS_SOCKET_PREFIX_FORMAT, pass_index);
-
- BufferPass pass;
-
- if (!node_from_image_spec_atttributes(&pass, image_spec, attr_name_prefix)) {
- return false;
- }
-
- buffer_params->passes.emplace_back(std::move(pass));
- }
-
- buffer_params->update_passes();
-
- return true;
-}
-
-/* Configure image specification for the given buffer parameters and passes.
- *
- * Image channels will be strictly ordered to match content of corresponding buffer, and the
- * metadata will be set so that the render buffers and passes can be reconstructed from it.
- *
- * If the tile size different from (0, 0) the image specification will be configured to use the
- * given tile size for tiled IO. */
-static bool configure_image_spec_from_buffer(ImageSpec *image_spec,
- const BufferParams &buffer_params,
- const int2 tile_size = make_int2(0, 0))
-{
- const std::vector<std::string> channel_names = exr_channel_names_for_passes(buffer_params);
- const int num_channels = channel_names.size();
-
- *image_spec = ImageSpec(
- buffer_params.width, buffer_params.height, num_channels, TypeDesc::FLOAT);
-
- image_spec->channelnames = move(channel_names);
-
- if (!buffer_params_to_image_spec_atttributes(image_spec, buffer_params)) {
- return false;
- }
-
- if (tile_size.x != 0 || tile_size.y != 0) {
- DCHECK_GT(tile_size.x, 0);
- DCHECK_GT(tile_size.y, 0);
-
- image_spec->tile_width = min(TileManager::IMAGE_TILE_SIZE, tile_size.x);
- image_spec->tile_height = min(TileManager::IMAGE_TILE_SIZE, tile_size.y);
- }
-
- return true;
-}
-
-/* --------------------------------------------------------------------
- * Tile Manager.
- */
-
-TileManager::TileManager()
-{
- /* Use process ID to separate different processes.
- * To ensure uniqueness from within a process use combination of object address and instance
- * index. This solves problem of possible object re-allocation at the same time, and solves
- * possible conflict when the counter overflows while there are still active instances of the
- * class. */
- const int tile_manager_id = g_instance_index.fetch_add(1, std::memory_order_relaxed);
- tile_file_unique_part_ = to_string(system_self_process_id()) + "-" +
- to_string(reinterpret_cast<uintptr_t>(this)) + "-" +
- to_string(tile_manager_id);
-}
-
-TileManager::~TileManager()
-{
-}
-
-int TileManager::compute_render_tile_size(const int suggested_tile_size) const
-{
- /* Must be a multiple of IMAGE_TILE_SIZE so that we can write render tiles into the image file
- * aligned on image tile boundaries. We can't set IMAGE_TILE_SIZE equal to the render tile size
- * because too big tile size leads to integer overflow inside OpenEXR. */
- return (suggested_tile_size <= IMAGE_TILE_SIZE) ? suggested_tile_size :
- align_up(suggested_tile_size, IMAGE_TILE_SIZE);
-}
-
-void TileManager::reset_scheduling(const BufferParams &params, int2 tile_size)
-{
- VLOG(3) << "Using tile size of " << tile_size;
-
- close_tile_output();
-
- tile_size_ = tile_size;
-
- tile_state_.num_tiles_x = divide_up(params.width, tile_size_.x);
- tile_state_.num_tiles_y = divide_up(params.height, tile_size_.y);
- tile_state_.num_tiles = tile_state_.num_tiles_x * tile_state_.num_tiles_y;
-
- tile_state_.next_tile_index = 0;
-
- tile_state_.current_tile = Tile();
-}
-
-void TileManager::update(const BufferParams &params, const Scene *scene)
-{
- DCHECK_NE(params.pass_stride, -1);
-
- buffer_params_ = params;
-
- /* TODO(sergey): Proper Error handling, so that if configuration has failed we don't attempt to
- * write to a partially configured file. */
- configure_image_spec_from_buffer(&write_state_.image_spec, buffer_params_, tile_size_);
-
- const DenoiseParams denoise_params = scene->integrator->get_denoise_params();
- const AdaptiveSampling adaptive_sampling = scene->integrator->get_adaptive_sampling();
-
- node_to_image_spec_atttributes(
- &write_state_.image_spec, &denoise_params, ATTR_DENOISE_SOCKET_PREFIX);
-
- if (adaptive_sampling.use) {
- overscan_ = 4;
- }
- else {
- overscan_ = 0;
- }
-}
-
-bool TileManager::done()
-{
- return tile_state_.next_tile_index == tile_state_.num_tiles;
-}
-
-bool TileManager::next()
-{
- if (done()) {
- return false;
- }
-
- tile_state_.current_tile = get_tile_for_index(tile_state_.next_tile_index);
-
- ++tile_state_.next_tile_index;
-
- return true;
-}
-
-Tile TileManager::get_tile_for_index(int index) const
-{
- /* TODO(sergey): Consider using hilbert spiral, or. maybe, even configurable. Not sure this
- * brings a lot of value since this is only applicable to BIG tiles. */
-
- const int tile_index_y = index / tile_state_.num_tiles_x;
- const int tile_index_x = index - tile_index_y * tile_state_.num_tiles_x;
-
- const int tile_window_x = tile_index_x * tile_size_.x;
- const int tile_window_y = tile_index_y * tile_size_.y;
-
- Tile tile;
-
- tile.x = max(0, tile_window_x - overscan_);
- tile.y = max(0, tile_window_y - overscan_);
-
- tile.window_x = tile_window_x - tile.x;
- tile.window_y = tile_window_y - tile.y;
- tile.window_width = min(tile_size_.x, buffer_params_.width - tile_window_x);
- tile.window_height = min(tile_size_.y, buffer_params_.height - tile_window_y);
-
- tile.width = min(buffer_params_.width - tile.x, tile.window_x + tile.window_width + overscan_);
- tile.height = min(buffer_params_.height - tile.y,
- tile.window_y + tile.window_height + overscan_);
-
- return tile;
-}
-
-const Tile &TileManager::get_current_tile() const
-{
- return tile_state_.current_tile;
-}
-
-const int2 TileManager::get_size() const
-{
- return make_int2(buffer_params_.width, buffer_params_.height);
-}
-
-bool TileManager::open_tile_output()
-{
- write_state_.filename = path_temp_get("cycles-tile-buffer-" + tile_file_unique_part_ + "-" +
- to_string(write_state_.tile_file_index) + ".exr");
-
- write_state_.tile_out = ImageOutput::create(write_state_.filename);
- if (!write_state_.tile_out) {
- LOG(ERROR) << "Error creating image output for " << write_state_.filename;
- return false;
- }
-
- if (!write_state_.tile_out->supports("tiles")) {
- LOG(ERROR) << "Progress tile file format does not support tiling.";
- return false;
- }
-
- if (!write_state_.tile_out->open(write_state_.filename, write_state_.image_spec)) {
- LOG(ERROR) << "Error opening tile file: " << write_state_.tile_out->geterror();
- write_state_.tile_out = nullptr;
- return false;
- }
-
- write_state_.num_tiles_written = 0;
-
- VLOG(3) << "Opened tile file " << write_state_.filename;
-
- return true;
-}
-
-bool TileManager::close_tile_output()
-{
- if (!write_state_.tile_out) {
- return true;
- }
-
- const bool success = write_state_.tile_out->close();
- write_state_.tile_out = nullptr;
-
- if (!success) {
- LOG(ERROR) << "Error closing tile file.";
- return false;
- }
-
- VLOG(3) << "Tile output is closed.";
-
- return true;
-}
-
-bool TileManager::write_tile(const RenderBuffers &tile_buffers)
-{
- if (!write_state_.tile_out) {
- if (!open_tile_output()) {
- return false;
- }
- }
-
- DCHECK_EQ(tile_buffers.params.pass_stride, buffer_params_.pass_stride);
-
- vector<float> pixel_storage;
-
- const BufferParams &tile_params = tile_buffers.params;
-
- const int tile_x = tile_params.full_x - buffer_params_.full_x + tile_params.window_x;
- const int tile_y = tile_params.full_y - buffer_params_.full_y + tile_params.window_y;
-
- const int64_t pass_stride = tile_params.pass_stride;
- const int64_t tile_row_stride = tile_params.width * pass_stride;
-
- const int64_t xstride = pass_stride * sizeof(float);
- const int64_t ystride = xstride * tile_params.width;
- const int64_t zstride = ystride * tile_params.height;
-
- const float *pixels = tile_buffers.buffer.data() + tile_params.window_x * pass_stride +
- tile_params.window_y * tile_row_stride;
-
- VLOG(3) << "Write tile at " << tile_x << ", " << tile_y;
-
- /* The image tile sizes in the OpenEXR file are different from the size of our big tiles. The
- * write_tiles() method expects a contiguous image region that will be split into tiles
- * internally. OpenEXR expects the size of this region to be a multiple of the tile size,
- * however OpenImageIO automatically adds the required padding.
- *
- * The only thing we have to ensure is that the tile_x and tile_y are a multiple of the
- * image tile size, which happens in compute_render_tile_size. */
- if (!write_state_.tile_out->write_tiles(tile_x,
- tile_x + tile_params.window_width,
- tile_y,
- tile_y + tile_params.window_height,
- 0,
- 1,
- TypeDesc::FLOAT,
- pixels,
- xstride,
- ystride,
- zstride)) {
- LOG(ERROR) << "Error writing tile " << write_state_.tile_out->geterror();
- return false;
- }
-
- ++write_state_.num_tiles_written;
-
- return true;
-}
-
-void TileManager::finish_write_tiles()
-{
- if (!write_state_.tile_out) {
- /* None of the tiles were written hence the file was not created.
- * Avoid creation of fully empty file since it is redundant. */
- return;
- }
-
- /* EXR expects all tiles to present in file. So explicitly write missing tiles as all-zero. */
- if (write_state_.num_tiles_written < tile_state_.num_tiles) {
- vector<float> pixel_storage(tile_size_.x * tile_size_.y * buffer_params_.pass_stride);
-
- for (int tile_index = write_state_.num_tiles_written; tile_index < tile_state_.num_tiles;
- ++tile_index) {
- const Tile tile = get_tile_for_index(tile_index);
-
- const int tile_x = tile.x + tile.window_x;
- const int tile_y = tile.y + tile.window_y;
-
- VLOG(3) << "Write dummy tile at " << tile_x << ", " << tile_y;
-
- write_state_.tile_out->write_tiles(tile_x,
- tile_x + tile.window_width,
- tile_y,
- tile_y + tile.window_height,
- 0,
- 1,
- TypeDesc::FLOAT,
- pixel_storage.data());
- }
- }
-
- close_tile_output();
-
- if (full_buffer_written_cb) {
- full_buffer_written_cb(write_state_.filename);
- }
-
- /* Advance the counter upon explicit finish of the file.
- * Makes it possible to re-use tile manager for another scene, and avoids unnecessary increments
- * of the tile-file-within-session index. */
- ++write_state_.tile_file_index;
-
- write_state_.filename = "";
-}
-
-bool TileManager::read_full_buffer_from_disk(const string_view filename,
- RenderBuffers *buffers,
- DenoiseParams *denoise_params)
-{
- unique_ptr<ImageInput> in(ImageInput::open(filename));
- if (!in) {
- LOG(ERROR) << "Error opening tile file " << filename;
- return false;
- }
-
- const ImageSpec &image_spec = in->spec();
-
- BufferParams buffer_params;
- if (!buffer_params_from_image_spec_atttributes(&buffer_params, image_spec)) {
- return false;
- }
- buffers->reset(buffer_params);
-
- if (!node_from_image_spec_atttributes(denoise_params, image_spec, ATTR_DENOISE_SOCKET_PREFIX)) {
- return false;
- }
-
- if (!in->read_image(TypeDesc::FLOAT, buffers->buffer.data())) {
- LOG(ERROR) << "Error reading pixels from the tile file " << in->geterror();
- return false;
- }
-
- if (!in->close()) {
- LOG(ERROR) << "Error closing tile file " << in->geterror();
- return false;
- }
-
- return true;
-}
-
-CCL_NAMESPACE_END