diff options
author | Sam Kottler <dev@samkottler.net> | 2020-08-13 17:58:33 +0300 |
---|---|---|
committer | Sam Kottler <dev@samkottler.net> | 2020-08-13 17:58:33 +0300 |
commit | 7c38d008de5ebdcb96ebdd67b8dc459d5551702f (patch) | |
tree | 8fb22f60bc31019e3fe8f5b2bf35159c2e32cda9 /intern/cycles | |
parent | cc86e03fd52cd68e66b817590aac80ec2a358d3e (diff) | |
parent | 4e103101f7a20eaa2a61306a1070a04b63a69958 (diff) |
Merge branch 'blender-v2.90-release' into soc-2020-production-ready-light-tree-2
Diffstat (limited to 'intern/cycles')
23 files changed, 564 insertions, 348 deletions
diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index 592a69585de..e1ab3b3fbc1 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -76,6 +76,9 @@ struct BlenderCamera { int full_width; int full_height; + int render_width; + int render_height; + BoundBox2D border; BoundBox2D pano_viewplane; BoundBox2D viewport_camera_border; @@ -126,8 +129,10 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings &b_rende bcam->matrix = transform_identity(); /* render resolution */ - bcam->full_width = render_resolution_x(b_render); - bcam->full_height = render_resolution_y(b_render); + bcam->render_width = render_resolution_x(b_render); + bcam->render_height = render_resolution_y(b_render); + bcam->full_width = bcam->render_width; + bcam->full_height = bcam->render_height; } static float blender_camera_focal_distance(BL::RenderEngine &b_engine, @@ -398,8 +403,8 @@ static void blender_camera_sync(Camera *cam, /* panorama sensor */ if (bcam->type == CAMERA_PANORAMA && bcam->panorama_type == PANORAMA_FISHEYE_EQUISOLID) { - float fit_xratio = (float)bcam->full_width * bcam->pixelaspect.x; - float fit_yratio = (float)bcam->full_height * bcam->pixelaspect.y; + float fit_xratio = (float)bcam->render_width * bcam->pixelaspect.x; + float fit_yratio = (float)bcam->render_height * bcam->pixelaspect.y; bool horizontal_fit; float sensor_size; diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 391a1b8f473..a06030c8b7d 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -59,6 +59,7 @@ BlenderSession::BlenderSession(BL::RenderEngine &b_engine, BL::BlendData &b_data, bool preview_osl) : session(NULL), + scene(NULL), sync(NULL), b_engine(b_engine), b_userpref(b_userpref), @@ -88,6 +89,7 @@ BlenderSession::BlenderSession(BL::RenderEngine &b_engine, int width, int height) : session(NULL), + scene(NULL), sync(NULL), b_engine(b_engine), b_userpref(b_userpref), @@ -492,27 +494,15 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) /* Update denoising parameters. */ session->set_denoising(session_params.denoising); - bool use_denoising = session_params.denoising.use; - bool store_denoising_passes = session_params.denoising.store_passes; - - buffer_params.denoising_data_pass = use_denoising || store_denoising_passes; - buffer_params.denoising_clean_pass = (scene->film->denoising_flags & DENOISING_CLEAN_ALL_PASSES); - buffer_params.denoising_prefiltered_pass = store_denoising_passes && - session_params.denoising.type == DENOISER_NLM; - - scene->film->denoising_data_pass = buffer_params.denoising_data_pass; - scene->film->denoising_clean_pass = buffer_params.denoising_clean_pass; - scene->film->denoising_prefiltered_pass = buffer_params.denoising_prefiltered_pass; - - /* Add passes */ + /* Compute render passes and film settings. */ vector<Pass> passes = sync->sync_render_passes( b_rlay, b_view_layer, session_params.adaptive_sampling, session_params.denoising); - buffer_params.passes = passes; - scene->film->pass_alpha_threshold = b_view_layer.pass_alpha_threshold(); - scene->film->tag_passes_update(scene, passes); - scene->film->tag_update(scene); - scene->integrator->tag_update(scene); + /* Set buffer params, using film settings from sync_render_passes. */ + buffer_params.passes = passes; + buffer_params.denoising_data_pass = scene->film->denoising_data_pass; + buffer_params.denoising_clean_pass = scene->film->denoising_clean_pass; + buffer_params.denoising_prefiltered_pass = scene->film->denoising_prefiltered_pass; BL::RenderResult::views_iterator b_view_iter; @@ -982,7 +972,8 @@ void BlenderSession::update_status_progress() remaining_time = (1.0 - (double)progress) * (render_time / (double)progress); if (background) { - scene_status += " | " + scene->name; + if (scene) + scene_status += " | " + scene->name; if (b_rlay_name != "") scene_status += ", " + b_rlay_name; diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 33e73b5a4b9..ae681432a43 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -678,7 +678,7 @@ static ShaderNode *add_node(Scene *scene, * builtin names for packed images and movies */ int scene_frame = b_scene.frame_current(); - int image_frame = image_user_frame_number(b_image_user, scene_frame); + int image_frame = image_user_frame_number(b_image_user, b_image, scene_frame); image->handle = scene->image_manager->add_image( new BlenderImageLoader(b_image, image_frame), image->image_params()); } @@ -713,7 +713,7 @@ static ShaderNode *add_node(Scene *scene, if (is_builtin) { int scene_frame = b_scene.frame_current(); - int image_frame = image_user_frame_number(b_image_user, scene_frame); + int image_frame = image_user_frame_number(b_image_user, b_image, scene_frame); env->handle = scene->image_manager->add_image(new BlenderImageLoader(b_image, image_frame), env->image_params()); } diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index ee90b4dfbfe..f806569c389 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -709,6 +709,16 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, } RNA_END; + scene->film->denoising_data_pass = denoising.use || denoising.store_passes; + scene->film->denoising_clean_pass = (scene->film->denoising_flags & DENOISING_CLEAN_ALL_PASSES); + scene->film->denoising_prefiltered_pass = denoising.store_passes && + denoising.type == DENOISER_NLM; + + scene->film->pass_alpha_threshold = b_view_layer.pass_alpha_threshold(); + scene->film->tag_passes_update(scene, passes); + scene->film->tag_update(scene); + scene->integrator->tag_update(scene); + return passes; } diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index ad90a5f8d52..1ea34b41aa2 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -238,7 +238,7 @@ static inline string image_user_file_path(BL::ImageUser &iuser, { char filepath[1024]; iuser.tile(0); - BKE_image_user_frame_calc(NULL, iuser.ptr.data, cfra); + BKE_image_user_frame_calc(ima.ptr.data, iuser.ptr.data, cfra); BKE_image_user_file_path(iuser.ptr.data, ima.ptr.data, filepath); string filepath_str = string(filepath); @@ -248,9 +248,9 @@ static inline string image_user_file_path(BL::ImageUser &iuser, return filepath_str; } -static inline int image_user_frame_number(BL::ImageUser &iuser, int cfra) +static inline int image_user_frame_number(BL::ImageUser &iuser, BL::Image &ima, int cfra) { - BKE_image_user_frame_calc(NULL, iuser.ptr.data, cfra); + BKE_image_user_frame_calc(ima.ptr.data, iuser.ptr.data, cfra); return iuser.frame_current(); } diff --git a/intern/cycles/blender/blender_volume.cpp b/intern/cycles/blender/blender_volume.cpp index 80591e0eec8..d0e1e4d6131 100644 --- a/intern/cycles/blender/blender_volume.cpp +++ b/intern/cycles/blender/blender_volume.cpp @@ -217,43 +217,29 @@ static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Mesh *mesh, float class BlenderVolumeLoader : public VDBImageLoader { public: BlenderVolumeLoader(BL::BlendData &b_data, BL::Volume &b_volume, const string &grid_name) - : VDBImageLoader(grid_name), b_data(b_data), b_volume(b_volume), unload(false) - { - } - - bool load_metadata(ImageMetaData &metadata) override + : VDBImageLoader(grid_name), b_volume(b_volume) { b_volume.grids.load(b_data.ptr.data); - BL::VolumeGrid b_volume_grid = find_grid(); - - if (!b_volume_grid) { - return false; - } - - unload = !b_volume_grid.is_loaded(); #ifdef WITH_OPENVDB - Volume *volume = (Volume *)b_volume.ptr.data; - VolumeGrid *volume_grid = (VolumeGrid *)b_volume_grid.ptr.data; - grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); -#endif + BL::Volume::grids_iterator b_grid_iter; + for (b_volume.grids.begin(b_grid_iter); b_grid_iter != b_volume.grids.end(); ++b_grid_iter) { + BL::VolumeGrid b_volume_grid(*b_grid_iter); + if (b_volume_grid.name() == grid_name) { + const bool unload = !b_volume_grid.is_loaded(); - return VDBImageLoader::load_metadata(metadata); - } + Volume *volume = (Volume *)b_volume.ptr.data; + VolumeGrid *volume_grid = (VolumeGrid *)b_volume_grid.ptr.data; + grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); - bool load_pixels(const ImageMetaData &metadata, - void *pixels, - const size_t pixel_size, - const bool associate_alpha) override - { - b_volume.grids.load(b_data.ptr.data); - BL::VolumeGrid b_volume_grid = find_grid(); + if (unload) { + b_volume_grid.unload(); + } - if (!b_volume_grid) { - return false; + break; + } } - - return VDBImageLoader::load_pixels(metadata, pixels, pixel_size, associate_alpha); +#endif } bool equals(const ImageLoader &other) const override @@ -263,36 +249,7 @@ class BlenderVolumeLoader : public VDBImageLoader { return b_volume == other_loader.b_volume && grid_name == other_loader.grid_name; } - void cleanup() override - { - VDBImageLoader::cleanup(); - - BL::VolumeGrid b_volume_grid = find_grid(); - if (b_volume_grid && unload) { - b_volume_grid.unload(); - } - } - - /* Find grid with matching name. Grid point not stored in the class since - * grids may be unloaded before we load the pixels, for example for motion - * blur where we move between frames. */ - BL::VolumeGrid find_grid() - { -#ifdef WITH_OPENVDB - BL::Volume::grids_iterator b_grid_iter; - for (b_volume.grids.begin(b_grid_iter); b_grid_iter != b_volume.grids.end(); ++b_grid_iter) { - if (b_grid_iter->name() == grid_name) { - return *b_grid_iter; - } - } -#endif - - return BL::VolumeGrid(PointerRNA_NULL); - } - - BL::BlendData b_data; BL::Volume b_volume; - bool unload; }; static void sync_volume_object(BL::BlendData &b_data, BL::Object &b_ob, Scene *scene, Mesh *mesh) @@ -342,7 +299,7 @@ static void sync_volume_object(BL::BlendData &b_data, BL::Object &b_ob, Scene *s ImageParams params; params.frame = b_volume.grids.frame(); - attr->data_voxel() = scene->image_manager->add_image(loader, params); + attr->data_voxel() = scene->image_manager->add_image(loader, params, false); } } } diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 5533eeb006d..db146226dc7 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -539,7 +539,7 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) ${SRC_UTIL_HEADERS} COMMAND ${CUBIN_CC_ENV} "$<TARGET_FILE:cycles_cubin_cc>" - -target 52 + -target 50 -ptx -i ${CMAKE_CURRENT_SOURCE_DIR}/${input} ${cuda_flags} @@ -563,7 +563,7 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) COMMAND ${CUDA_NVCC_EXECUTABLE} --ptx - -arch=sm_52 + -arch=sm_50 ${cuda_flags} ${input} WORKING_DIRECTORY diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h index 15cf2e02fdd..2ba3b1c4fdc 100644 --- a/intern/cycles/kernel/kernel_path.h +++ b/intern/cycles/kernel/kernel_path.h @@ -695,11 +695,9 @@ ccl_device void kernel_path_trace( kernel_path_trace_setup(kg, sample, x, y, &rng_hash, &ray); -# ifndef __KERNEL_OPTIX__ if (ray.t == 0.0f) { return; } -# endif /* Initialize state. */ float3 throughput = make_float3(1.0f, 1.0f, 1.0f); diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 3d1b6e1d865..145b1fa492c 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -1201,9 +1201,13 @@ void GeometryManager::device_update_volume_images(Device *device, Scene *scene, } ImageHandle &handle = attr.data_voxel(); - const int slot = handle.svm_slot(); - if (slot != -1) { - volume_images.insert(slot); + /* We can build directly from OpenVDB data structures, no need to + * load such images early. */ + if (!handle.vdb_loader()) { + const int slot = handle.svm_slot(); + if (slot != -1) { + volume_images.insert(slot); + } } } } diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp index 8d187814d64..a5dfcf60d61 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/render/image.cpp @@ -18,6 +18,7 @@ #include "device/device.h" #include "render/colorspace.h" #include "render/image_oiio.h" +#include "render/image_vdb.h" #include "render/scene.h" #include "render/stats.h" @@ -172,6 +173,31 @@ device_texture *ImageHandle::image_memory(const int tile_index) const return img ? img->mem : NULL; } +VDBImageLoader *ImageHandle::vdb_loader(const int tile_index) const +{ + if (tile_index >= tile_slots.size()) { + return NULL; + } + + ImageManager::Image *img = manager->images[tile_slots[tile_index]]; + + if (img == NULL) { + return NULL; + } + + ImageLoader *loader = img->loader; + + if (loader == NULL) { + return NULL; + } + + if (loader->is_vdb_loader()) { + return dynamic_cast<VDBImageLoader *>(loader); + } + + return NULL; +} + bool ImageHandle::operator==(const ImageHandle &other) const { return manager == other.manager && tile_slots == other.tile_slots; @@ -258,6 +284,11 @@ bool ImageLoader::equals(const ImageLoader *a, const ImageLoader *b) } } +bool ImageLoader::is_vdb_loader() const +{ + return false; +} + /* Image Manager */ ImageManager::ImageManager(const DeviceInfo &info) @@ -362,9 +393,11 @@ ImageHandle ImageManager::add_image(const string &filename, return handle; } -ImageHandle ImageManager::add_image(ImageLoader *loader, const ImageParams ¶ms) +ImageHandle ImageManager::add_image(ImageLoader *loader, + const ImageParams ¶ms, + const bool builtin) { - const int slot = add_image_slot(loader, params, true); + const int slot = add_image_slot(loader, params, builtin); ImageHandle handle; handle.tile_slots.push_back(slot); diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h index fffe7c5152a..cb059256ce3 100644 --- a/intern/cycles/render/image.h +++ b/intern/cycles/render/image.h @@ -39,6 +39,7 @@ class Progress; class RenderStats; class Scene; class ColorSpaceProcessor; +class VDBImageLoader; /* Image Parameters */ class ImageParams { @@ -124,6 +125,8 @@ class ImageLoader { virtual bool equals(const ImageLoader &other) const = 0; static bool equals(const ImageLoader *a, const ImageLoader *b); + virtual bool is_vdb_loader() const; + /* Work around for no RTTI. */ }; @@ -149,6 +152,8 @@ class ImageHandle { int svm_slot(const int tile_index = 0) const; device_texture *image_memory(const int tile_index = 0) const; + VDBImageLoader *vdb_loader(const int tile_index = 0) const; + protected: vector<int> tile_slots; ImageManager *manager; @@ -169,7 +174,7 @@ class ImageManager { ImageHandle add_image(const string &filename, const ImageParams ¶ms, const vector<int> &tiles); - ImageHandle add_image(ImageLoader *loader, const ImageParams ¶ms); + ImageHandle add_image(ImageLoader *loader, const ImageParams ¶ms, const bool builtin = true); void device_update(Device *device, Scene *scene, Progress &progress); void device_update_slot(Device *device, Scene *scene, int slot, Progress *progress); diff --git a/intern/cycles/render/image_vdb.cpp b/intern/cycles/render/image_vdb.cpp index 500131c2d84..3f7dd45ee88 100644 --- a/intern/cycles/render/image_vdb.cpp +++ b/intern/cycles/render/image_vdb.cpp @@ -185,4 +185,16 @@ void VDBImageLoader::cleanup() #endif } +bool VDBImageLoader::is_vdb_loader() const +{ + return true; +} + +#ifdef WITH_OPENVDB +openvdb::GridBase::ConstPtr VDBImageLoader::get_grid() +{ + return grid; +} +#endif + CCL_NAMESPACE_END diff --git a/intern/cycles/render/image_vdb.h b/intern/cycles/render/image_vdb.h index 7dec63b11e6..4500cfbfb88 100644 --- a/intern/cycles/render/image_vdb.h +++ b/intern/cycles/render/image_vdb.h @@ -43,6 +43,12 @@ class VDBImageLoader : public ImageLoader { virtual void cleanup() override; + virtual bool is_vdb_loader() const override; + +#ifdef WITH_OPENVDB + openvdb::GridBase::ConstPtr get_grid(); +#endif + protected: string grid_name; #ifdef WITH_OPENVDB diff --git a/intern/cycles/render/mesh_volume.cpp b/intern/cycles/render/mesh_volume.cpp index 607363d01c6..70189ea4812 100644 --- a/intern/cycles/render/mesh_volume.cpp +++ b/intern/cycles/render/mesh_volume.cpp @@ -15,34 +15,25 @@ */ #include "render/attribute.h" +#include "render/image_vdb.h" #include "render/mesh.h" #include "render/scene.h" +#ifdef WITH_OPENVDB +# include <openvdb/tools/Dense.h> +# include <openvdb/tools/GridTransformer.h> +# include <openvdb/tools/Morphology.h> +#endif + #include "util/util_foreach.h" #include "util/util_hash.h" #include "util/util_logging.h" +#include "util/util_openvdb.h" #include "util/util_progress.h" #include "util/util_types.h" CCL_NAMESPACE_BEGIN -const int64_t VOXEL_INDEX_NONE = -1; - -static int64_t compute_voxel_index(const int3 &resolution, int64_t x, int64_t y, int64_t z) -{ - if (x < 0 || x >= resolution.x) { - return VOXEL_INDEX_NONE; - } - else if (y < 0 || y >= resolution.y) { - return VOXEL_INDEX_NONE; - } - else if (z < 0 || z >= resolution.z) { - return VOXEL_INDEX_NONE; - } - - return x + y * resolution.x + z * resolution.x * resolution.y; -} - struct QuadData { int v0, v1, v2, v3; @@ -123,122 +114,146 @@ static void create_quad(int3 corners[8], quads.push_back(quad); } -struct VolumeParams { - int3 resolution; - float3 cell_size; - float3 start_point; - int pad_size; -}; - -static const int CUBE_SIZE = 8; - /* Create a mesh from a volume. * * The way the algorithm works is as follows: * - * - The coordinates of active voxels from a dense volume (or 3d image) are - * gathered inside an auxiliary volume. - * - Each set of coordinates of an CUBE_SIZE cube are mapped to the same - * coordinate of the auxiliary volume. - * - Quads are created between active and non-active voxels in the auxiliary - * volume to generate a tight mesh around the volume. + * - The topologies of input OpenVDB grids are merged into a temporary grid. + * - Voxels of the temporary grid are dilated to account for the padding necessary for volume + * sampling. + * - Quads are created on the boundary between active and inactive leaf nodes of the temporary + * grid. */ class VolumeMeshBuilder { - /* Auxiliary volume that is used to check if a node already added. */ - vector<char> grid; - - /* The resolution of the auxiliary volume, set to be equal to 1/CUBE_SIZE - * of the original volume on each axis. */ - int3 res; - - size_t number_of_nodes; - - /* Offset due to padding in the original grid. Padding will transform the - * coordinates of the original grid from 0...res to -padding...res+padding, - * so some coordinates are negative, and we need to properly account for - * them. */ - int3 pad_offset; - - VolumeParams *params; - public: - VolumeMeshBuilder(VolumeParams *volume_params); +#ifdef WITH_OPENVDB + /* use a MaskGrid to store the topology to save memory */ + openvdb::MaskGrid::Ptr topology_grid; + openvdb::CoordBBox bbox; +#endif + bool first_grid; - void add_node(int x, int y, int z); + VolumeMeshBuilder(); - void add_node_with_padding(int x, int y, int z); +#ifdef WITH_OPENVDB + void add_grid(openvdb::GridBase::ConstPtr grid, bool do_clipping, float volume_clipping); +#endif - void create_mesh(vector<float3> &vertices, vector<int> &indices, vector<float3> &face_normals); + void add_padding(int pad_size); + + void create_mesh(vector<float3> &vertices, + vector<int> &indices, + vector<float3> &face_normals, + const float face_overlap_avoidance); - private: void generate_vertices_and_quads(vector<int3> &vertices_is, vector<QuadData> &quads); - void convert_object_space(const vector<int3> &vertices, vector<float3> &out_vertices); + void convert_object_space(const vector<int3> &vertices, + vector<float3> &out_vertices, + const float face_overlap_avoidance); void convert_quads_to_tris(const vector<QuadData> &quads, vector<int> &tris, vector<float3> &face_normals); -}; -VolumeMeshBuilder::VolumeMeshBuilder(VolumeParams *volume_params) -{ - params = volume_params; - number_of_nodes = 0; + bool empty_grid() const; - const int64_t x = divide_up(params->resolution.x, CUBE_SIZE); - const int64_t y = divide_up(params->resolution.y, CUBE_SIZE); - const int64_t z = divide_up(params->resolution.z, CUBE_SIZE); +#ifdef WITH_OPENVDB + template <typename GridType> + void merge_grid(openvdb::GridBase::ConstPtr grid, bool do_clipping, float volume_clipping) + { + typename GridType::ConstPtr typed_grid = openvdb::gridConstPtrCast<GridType>(grid); - /* Adding 2*pad_size since we pad in both positive and negative directions - * along the axis. */ - const int64_t px = divide_up(params->resolution.x + 2 * params->pad_size, CUBE_SIZE); - const int64_t py = divide_up(params->resolution.y + 2 * params->pad_size, CUBE_SIZE); - const int64_t pz = divide_up(params->resolution.z + 2 * params->pad_size, CUBE_SIZE); + if (do_clipping) { + using ValueType = typename GridType::ValueType; + typename GridType::Ptr copy = typed_grid->deepCopy(); + typename GridType::ValueOnIter iter = copy->beginValueOn(); - res = make_int3(px, py, pz); - pad_offset = make_int3(px - x, py - y, pz - z); + for (; iter; ++iter) { + if (iter.getValue() < ValueType(volume_clipping)) { + iter.setValueOff(); + } + } - grid.resize(px * py * pz, 0); -} + typed_grid = copy; + } -void VolumeMeshBuilder::add_node(int x, int y, int z) -{ - /* Map coordinates to index space. */ - const int index_x = (x / CUBE_SIZE) + pad_offset.x; - const int index_y = (y / CUBE_SIZE) + pad_offset.y; - const int index_z = (z / CUBE_SIZE) + pad_offset.z; + topology_grid->topologyUnion(*typed_grid); + } +#endif +}; - assert((index_x >= 0) && (index_y >= 0) && (index_z >= 0)); +VolumeMeshBuilder::VolumeMeshBuilder() +{ + first_grid = true; +} - const int64_t index = compute_voxel_index(res, index_x, index_y, index_z); - if (index == VOXEL_INDEX_NONE) { - return; +#ifdef WITH_OPENVDB +void VolumeMeshBuilder::add_grid(openvdb::GridBase::ConstPtr grid, bool do_clipping, float volume_clipping) +{ + /* set the transform of our grid from the first one */ + if (first_grid) { + topology_grid = openvdb::MaskGrid::create(); + topology_grid->setTransform(grid->transform().copy()); + first_grid = false; } - - /* We already have a node here. */ - if (grid[index] == 1) { - return; + /* if the transforms do not match, we need to resample one of the grids so that + * its index space registers with that of the other, here we resample our mask + * grid so memory usage is kept low */ + else if (topology_grid->transform() != grid->transform()) { + openvdb::MaskGrid::Ptr temp_grid = topology_grid->copyWithNewTree(); + temp_grid->setTransform(grid->transform().copy()); + openvdb::tools::resampleToMatch<openvdb::tools::BoxSampler>(*topology_grid, *temp_grid); + topology_grid = temp_grid; + topology_grid->setTransform(grid->transform().copy()); } - ++number_of_nodes; - - grid[index] = 1; + if (grid->isType<openvdb::FloatGrid>()) { + merge_grid<openvdb::FloatGrid>(grid, do_clipping, volume_clipping); + } + else if (grid->isType<openvdb::Vec3fGrid>()) { + merge_grid<openvdb::Vec3fGrid>(grid, do_clipping, volume_clipping); + } + else if (grid->isType<openvdb::Vec4fGrid>()) { + merge_grid<openvdb::Vec4fGrid>(grid, do_clipping, volume_clipping); + } + else if (grid->isType<openvdb::BoolGrid>()) { + merge_grid<openvdb::BoolGrid>(grid, do_clipping, volume_clipping); + } + else if (grid->isType<openvdb::DoubleGrid>()) { + merge_grid<openvdb::DoubleGrid>(grid, do_clipping, volume_clipping); + } + else if (grid->isType<openvdb::Int32Grid>()) { + merge_grid<openvdb::Int32Grid>(grid, do_clipping, volume_clipping); + } + else if (grid->isType<openvdb::Int64Grid>()) { + merge_grid<openvdb::Int64Grid>(grid, do_clipping, volume_clipping); + } + else if (grid->isType<openvdb::Vec3IGrid>()) { + merge_grid<openvdb::Vec3IGrid>(grid, do_clipping, volume_clipping); + } + else if (grid->isType<openvdb::Vec3dGrid>()) { + merge_grid<openvdb::Vec3dGrid>(grid, do_clipping, volume_clipping); + } + else if (grid->isType<openvdb::MaskGrid>()) { + topology_grid->topologyUnion(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid)); + } } +#endif -void VolumeMeshBuilder::add_node_with_padding(int x, int y, int z) +void VolumeMeshBuilder::add_padding(int pad_size) { - for (int px = x - params->pad_size; px < x + params->pad_size; ++px) { - for (int py = y - params->pad_size; py < y + params->pad_size; ++py) { - for (int pz = z - params->pad_size; pz < z + params->pad_size; ++pz) { - add_node(px, py, pz); - } - } - } +#ifdef WITH_OPENVDB + openvdb::tools::dilateVoxels(topology_grid->tree(), pad_size); +#else + (void)pad_size; +#endif } void VolumeMeshBuilder::create_mesh(vector<float3> &vertices, vector<int> &indices, - vector<float3> &face_normals) + vector<float3> &face_normals, + const float face_overlap_avoidance) { /* We create vertices in index space (is), and only convert them to object * space when done. */ @@ -247,7 +262,7 @@ void VolumeMeshBuilder::create_mesh(vector<float3> &vertices, generate_vertices_and_quads(vertices_is, quads); - convert_object_space(vertices_is, vertices); + convert_object_space(vertices_is, vertices, face_overlap_avoidance); convert_quads_to_tris(quads, indices, face_normals); } @@ -255,85 +270,97 @@ void VolumeMeshBuilder::create_mesh(vector<float3> &vertices, void VolumeMeshBuilder::generate_vertices_and_quads(vector<ccl::int3> &vertices_is, vector<QuadData> &quads) { - unordered_map<size_t, int> used_verts; +#ifdef WITH_OPENVDB + const openvdb::MaskGrid::TreeType &tree = topology_grid->tree(); + tree.evalLeafBoundingBox(bbox); - for (int z = 0; z < res.z; ++z) { - for (int y = 0; y < res.y; ++y) { - for (int x = 0; x < res.x; ++x) { - int64_t voxel_index = compute_voxel_index(res, x, y, z); - if (grid[voxel_index] == 0) { - continue; - } + const int3 resolution = make_int3(bbox.dim().x(), bbox.dim().y(), bbox.dim().z()); - /* Compute min and max coords of the node in index space. */ - int3 min = make_int3((x - pad_offset.x) * CUBE_SIZE, - (y - pad_offset.y) * CUBE_SIZE, - (z - pad_offset.z) * CUBE_SIZE); - - /* Maximum is just CUBE_SIZE voxels away from minimum on each axis. */ - int3 max = make_int3(min.x + CUBE_SIZE, min.y + CUBE_SIZE, min.z + CUBE_SIZE); - - int3 corners[8] = { - make_int3(min[0], min[1], min[2]), - make_int3(max[0], min[1], min[2]), - make_int3(max[0], max[1], min[2]), - make_int3(min[0], max[1], min[2]), - make_int3(min[0], min[1], max[2]), - make_int3(max[0], min[1], max[2]), - make_int3(max[0], max[1], max[2]), - make_int3(min[0], max[1], max[2]), - }; - - /* Only create a quad if on the border between an active and - * an inactive node. - */ - - voxel_index = compute_voxel_index(res, x - 1, y, z); - if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) { - create_quad(corners, vertices_is, quads, res, used_verts, QUAD_X_MIN); - } + unordered_map<size_t, int> used_verts; - voxel_index = compute_voxel_index(res, x + 1, y, z); - if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) { - create_quad(corners, vertices_is, quads, res, used_verts, QUAD_X_MAX); - } + for (auto iter = tree.cbeginLeaf(); iter; ++iter) { + openvdb::CoordBBox leaf_bbox = iter->getNodeBoundingBox(); + /* +1 to convert from exclusive to include bounds. */ + leaf_bbox.max() = leaf_bbox.max().offsetBy(1); + + int3 min = make_int3(leaf_bbox.min().x(), leaf_bbox.min().y(), leaf_bbox.min().z()); + int3 max = make_int3(leaf_bbox.max().x(), leaf_bbox.max().y(), leaf_bbox.max().z()); + + int3 corners[8] = { + make_int3(min[0], min[1], min[2]), + make_int3(max[0], min[1], min[2]), + make_int3(max[0], max[1], min[2]), + make_int3(min[0], max[1], min[2]), + make_int3(min[0], min[1], max[2]), + make_int3(max[0], min[1], max[2]), + make_int3(max[0], max[1], max[2]), + make_int3(min[0], max[1], max[2]), + }; + + /* Only create a quad if on the border between an active and an inactive leaf. + * + * We verify that a leaf exists by probing a coordinate that is at its center, + * to do so we compute the center of the current leaf and offset this coordinate + * by the size of a leaf in each direction. + */ + static const int LEAF_DIM = openvdb::MaskGrid::TreeType::LeafNodeType::DIM; + auto center = leaf_bbox.min() + openvdb::Coord(LEAF_DIM / 2); + + if (!tree.probeLeaf(openvdb::Coord(center.x() - LEAF_DIM, center.y(), center.z()))) { + create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_X_MIN); + } - voxel_index = compute_voxel_index(res, x, y - 1, z); - if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) { - create_quad(corners, vertices_is, quads, res, used_verts, QUAD_Y_MIN); - } + if (!tree.probeLeaf(openvdb::Coord(center.x() + LEAF_DIM, center.y(), center.z()))) { + create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_X_MAX); + } - voxel_index = compute_voxel_index(res, x, y + 1, z); - if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) { - create_quad(corners, vertices_is, quads, res, used_verts, QUAD_Y_MAX); - } + if (!tree.probeLeaf(openvdb::Coord(center.x(), center.y() - LEAF_DIM, center.z()))) { + create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Y_MIN); + } - voxel_index = compute_voxel_index(res, x, y, z - 1); - if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) { - create_quad(corners, vertices_is, quads, res, used_verts, QUAD_Z_MIN); - } + if (!tree.probeLeaf(openvdb::Coord(center.x(), center.y() + LEAF_DIM, center.z()))) { + create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Y_MAX); + } - voxel_index = compute_voxel_index(res, x, y, z + 1); - if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) { - create_quad(corners, vertices_is, quads, res, used_verts, QUAD_Z_MAX); - } - } + if (!tree.probeLeaf(openvdb::Coord(center.x(), center.y(), center.z() - LEAF_DIM))) { + create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Z_MIN); + } + + if (!tree.probeLeaf(openvdb::Coord(center.x(), center.y(), center.z() + LEAF_DIM))) { + create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Z_MAX); } } +#else + (void)vertices_is; + (void)quads; +#endif } void VolumeMeshBuilder::convert_object_space(const vector<int3> &vertices, - vector<float3> &out_vertices) + vector<float3> &out_vertices, + const float face_overlap_avoidance) { +#ifdef WITH_OPENVDB + /* compute the offset for the face overlap avoidance */ + bbox = topology_grid->evalActiveVoxelBoundingBox(); + openvdb::Coord dim = bbox.dim(); + + float3 cell_size = make_float3(1.0f / dim.x(), 1.0f / dim.y(), 1.0f / dim.z()); + float3 point_offset = cell_size * face_overlap_avoidance; + out_vertices.reserve(vertices.size()); for (size_t i = 0; i < vertices.size(); ++i) { - float3 vertex = make_float3(vertices[i].x, vertices[i].y, vertices[i].z); - vertex *= params->cell_size; - vertex += params->start_point; - - out_vertices.push_back(vertex); + openvdb::math::Vec3d p = topology_grid->indexToWorld( + openvdb::math::Vec3d(vertices[i].x, vertices[i].y, vertices[i].z)); + float3 vertex = make_float3((float)p.x(), (float)p.y(), (float)p.z()); + out_vertices.push_back(vertex + point_offset); } +#else + (void)vertices; + (void)out_vertices; + (void)face_overlap_avoidance; +#endif } void VolumeMeshBuilder::convert_quads_to_tris(const vector<QuadData> &quads, @@ -359,57 +386,115 @@ void VolumeMeshBuilder::convert_quads_to_tris(const vector<QuadData> &quads, } } -/* ************************************************************************** */ +bool VolumeMeshBuilder::empty_grid() const +{ +#ifdef WITH_OPENVDB + return !topology_grid || topology_grid->tree().leafCount() == 0; +#else + return true; +#endif +} -struct VoxelAttributeGrid { - float *data; - int channels; -}; +#ifdef WITH_OPENVDB +template<typename GridType> +static openvdb::GridBase::ConstPtr openvdb_grid_from_device_texture(device_texture *image_memory, + float volume_clipping, + Transform transform_3d) +{ + using ValueType = typename GridType::ValueType; + + openvdb::CoordBBox dense_bbox(0, + 0, + 0, + image_memory->data_width - 1, + image_memory->data_height - 1, + image_memory->data_depth - 1); + openvdb::tools::Dense<ValueType, openvdb::tools::MemoryLayout::LayoutXYZ> dense( + dense_bbox, static_cast<ValueType *>(image_memory->host_pointer)); + + typename GridType::Ptr sparse = GridType::create(ValueType(0.0f)); + openvdb::tools::copyFromDense(dense, *sparse, ValueType(volume_clipping)); + + /* copyFromDense will remove any leaf node that contains constant data and replace it with a tile, + * however, we need to preserve the leaves in order to generate the mesh, so revoxelize the leaves + * that were pruned. This should not affect areas that were skipped due to the volume_clipping parameter. */ + sparse->tree().voxelizeActiveTiles(); + + /* Compute index to world matrix. */ + float3 voxel_size = make_float3(1.0f / image_memory->data_width, 1.0f / image_memory->data_height, 1.0f / image_memory->data_depth); + + transform_3d = transform_inverse(transform_3d); + + openvdb::Mat4R index_to_world_mat((double)(voxel_size.x * transform_3d[0][0]), 0.0, 0.0, 0.0, + 0.0, (double)(voxel_size.y * transform_3d[1][1]), 0.0, 0.0, + 0.0, 0.0, (double)(voxel_size.z * transform_3d[2][2]), 0.0, + (double)transform_3d[0][3], (double)transform_3d[1][3], (double)transform_3d[2][3], 1.0); + + openvdb::math::Transform::Ptr index_to_world_tfm = openvdb::math::Transform::createLinearTransform(index_to_world_mat); + + sparse->setTransform(index_to_world_tfm); + + return sparse; +} +#endif + +/* ************************************************************************** */ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress) { string msg = string_printf("Computing Volume Mesh %s", mesh->name.c_str()); progress.set_status("Updating Mesh", msg); - vector<VoxelAttributeGrid> voxel_grids; - - /* Compute volume parameters. */ - VolumeParams volume_params; - volume_params.resolution = make_int3(0, 0, 0); - - Transform transform = transform_identity(); + VolumeMeshBuilder builder; +#ifdef WITH_OPENVDB foreach (Attribute &attr, mesh->attributes.attributes) { if (attr.element != ATTR_ELEMENT_VOXEL) { continue; } + bool do_clipping = false; + ImageHandle &handle = attr.data_voxel(); - device_texture *image_memory = handle.image_memory(); - int3 resolution = make_int3( - image_memory->data_width, image_memory->data_height, image_memory->data_depth); - if (volume_params.resolution == make_int3(0, 0, 0)) { - volume_params.resolution = resolution; - } - else if (volume_params.resolution != resolution) { - /* TODO: support this as it's common for OpenVDB. */ - VLOG(1) << "Can't create accurate volume mesh, all voxel grid resolutions must be equal\n"; - continue; + /* Try building from OpenVDB grid directly. */ + VDBImageLoader *vdb_loader = handle.vdb_loader(); + openvdb::GridBase::ConstPtr grid; + if (vdb_loader) { + grid = vdb_loader->get_grid(); + + /* If building from an OpenVDB grid, we need to manually clip the values. */ + do_clipping = true; } - VoxelAttributeGrid voxel_grid; - voxel_grid.data = static_cast<float *>(image_memory->host_pointer); - voxel_grid.channels = image_memory->data_elements; - voxel_grids.push_back(voxel_grid); + /* Else fall back to creating an OpenVDB grid from the dense volume data. */ + if (!grid) { + device_texture *image_memory = handle.image_memory(); - /* TODO: support multiple transforms. */ - if (image_memory->info.use_transform_3d) { - transform = image_memory->info.transform_3d; + if (image_memory->data_elements == 1) { + grid = openvdb_grid_from_device_texture<openvdb::FloatGrid>(image_memory, + mesh->volume_clipping, + handle.metadata().transform_3d); + } + else if (image_memory->data_elements == 3) { + grid = openvdb_grid_from_device_texture<openvdb::Vec3fGrid>(image_memory, + mesh->volume_clipping, + handle.metadata().transform_3d); + } + else if (image_memory->data_elements == 4) { + grid = openvdb_grid_from_device_texture<openvdb::Vec4fGrid>(image_memory, + mesh->volume_clipping, + handle.metadata().transform_3d); + } + } + + if (grid) { + builder.add_grid(grid, do_clipping, mesh->volume_clipping); } } +#endif - if (voxel_grids.empty()) { + if (builder.empty_grid()) { return; } @@ -438,56 +523,19 @@ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress) return; } - /* Compute start point and cell size from transform. */ - const int3 resolution = volume_params.resolution; - float3 start_point = make_float3(0.0f, 0.0f, 0.0f); - float3 cell_size = make_float3(1.0f / resolution.x, 1.0f / resolution.y, 1.0f / resolution.z); - - /* TODO: support arbitrary transforms, not just scale + translate. */ - const Transform itfm = transform_inverse(transform); - start_point = transform_point(&itfm, start_point); - cell_size = transform_direction(&itfm, cell_size); + builder.add_padding(pad_size); /* Slightly offset vertex coordinates to avoid overlapping faces with other * volumes or meshes. The proper solution would be to improve intersection in * the kernel to support robust handling of multiple overlapping faces or use * an all-hit intersection similar to shadows. */ - const float3 face_overlap_avoidance = cell_size * 0.1f * - hash_uint_to_float(hash_string(mesh->name.c_str())); - - volume_params.start_point = start_point + face_overlap_avoidance; - volume_params.cell_size = cell_size; - volume_params.pad_size = pad_size; - - /* Build bounding mesh around non-empty volume cells. */ - VolumeMeshBuilder builder(&volume_params); - const float clipping = mesh->volume_clipping; - - for (int z = 0; z < resolution.z; ++z) { - for (int y = 0; y < resolution.y; ++y) { - for (int x = 0; x < resolution.x; ++x) { - int64_t voxel_index = compute_voxel_index(resolution, x, y, z); - - for (size_t i = 0; i < voxel_grids.size(); ++i) { - const VoxelAttributeGrid &voxel_grid = voxel_grids[i]; - const int channels = voxel_grid.channels; - - for (int c = 0; c < channels; c++) { - if (voxel_grid.data[voxel_index * channels + c] >= clipping) { - builder.add_node_with_padding(x, y, z); - break; - } - } - } - } - } - } + const float face_overlap_avoidance = 0.1f * hash_uint_to_float(hash_string(mesh->name.c_str())); /* Create mesh. */ vector<float3> vertices; vector<int> indices; vector<float3> face_normals; - builder.create_mesh(vertices, indices, face_normals); + builder.create_mesh(vertices, indices, face_normals, face_overlap_avoidance); mesh->clear(true); mesh->reserve_mesh(vertices.size(), indices.size() / 3); @@ -514,10 +562,6 @@ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress) indices.size() * sizeof(int)) / (1024.0 * 1024.0) << "Mb."; - - VLOG(1) << "Memory usage volume grid: " - << (resolution.x * resolution.y * resolution.z * sizeof(float)) / (1024.0 * 1024.0) - << "Mb."; } CCL_NAMESPACE_END diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index c5033359c6b..70c4214c684 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -945,8 +945,14 @@ void Session::set_pause(bool pause_) } } - if (notify) - pause_cond.notify_all(); + if (session_thread) { + if (notify) { + pause_cond.notify_all(); + } + } + else if (pause_) { + update_status_time(pause_); + } } void Session::set_denoising(const DenoiseParams &denoising) @@ -1150,8 +1156,15 @@ bool Session::render_need_denoise(bool &delayed) return false; } + /* Immediately denoise when we reach the start sample or last sample. */ + const int num_samples_finished = tile_manager.state.sample + 1; + if (num_samples_finished == params.denoising.start_sample || + num_samples_finished == params.samples) { + return true; + } + /* Do not denoise until the sample at which denoising should start is reached. */ - if (tile_manager.state.sample < min(params.denoising.start_sample, params.samples - 1)) { + if (num_samples_finished < params.denoising.start_sample) { return false; } diff --git a/intern/cycles/test/CMakeLists.txt b/intern/cycles/test/CMakeLists.txt index 6dcc7f7b3dd..07b345baff8 100644 --- a/intern/cycles/test/CMakeLists.txt +++ b/intern/cycles/test/CMakeLists.txt @@ -112,3 +112,4 @@ set_source_files_properties(util_avxf_avx_test.cpp PROPERTIES COMPILE_FLAGS "${C CYCLES_TEST(util_avxf_avx "cycles_util;bf_intern_numaapi;${OPENIMAGEIO_LIBRARIES};${BOOST_LIBRARIES}") set_source_files_properties(util_avxf_avx2_test.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX2_KERNEL_FLAGS}") CYCLES_TEST(util_avxf_avx2 "cycles_util;bf_intern_numaapi;${OPENIMAGEIO_LIBRARIES};${BOOST_LIBRARIES}") +CYCLES_TEST(util_transform "cycles_util;${OPENIMAGEIO_LIBRARIES};${BOOST_LIBRARIES}") diff --git a/intern/cycles/test/util_transform_test.cpp b/intern/cycles/test/util_transform_test.cpp new file mode 100644 index 00000000000..58ce0fdfee4 --- /dev/null +++ b/intern/cycles/test/util_transform_test.cpp @@ -0,0 +1,53 @@ +/* + * Copyright 2011-2020 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 "testing/testing.h" + +#include "util/util_transform.h" +#include "util/util_vector.h" + +CCL_NAMESPACE_BEGIN + +TEST(transform_motion_decompose, Degenerated) +{ + // Simple case: single degenerated matrix. + { + vector<Transform> motion = {transform_scale(0.0f, 0.0f, 0.0f)}; + vector<DecomposedTransform> decomp(motion.size()); + transform_motion_decompose(decomp.data(), motion.data(), motion.size()); + EXPECT_TRUE(transform_decomposed_isfinite_safe(&decomp[0])); + } + + // Copy from previous to current. + { + vector<Transform> motion = {transform_rotate(M_PI_4_F, make_float3(1.0f, 1.0f, 1.0f)), + transform_scale(0.0f, 0.0f, 0.0f)}; + vector<DecomposedTransform> decomp(motion.size()); + transform_motion_decompose(decomp.data(), motion.data(), motion.size()); + EXPECT_NEAR(len(decomp[1].x - decomp[0].x), 0.0f, 1e-6f); + } + + // Copy from next to current. + { + vector<Transform> motion = {transform_scale(0.0f, 0.0f, 0.0f), + transform_rotate(M_PI_4_F, make_float3(1.0f, 1.0f, 1.0f))}; + vector<DecomposedTransform> decomp(motion.size()); + transform_motion_decompose(decomp.data(), motion.data(), motion.size()); + EXPECT_NEAR(len(decomp[0].x - decomp[1].x), 0.0f, 1e-6f); + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt index f5e488d1bd2..a35ec6c7e29 100644 --- a/intern/cycles/util/CMakeLists.txt +++ b/intern/cycles/util/CMakeLists.txt @@ -88,6 +88,7 @@ set(SRC_HEADERS util_murmurhash.h util_openimagedenoise.h util_opengl.h + util_openvdb.h util_optimization.h util_param.h util_path.h diff --git a/intern/cycles/util/util_math_fast.h b/intern/cycles/util/util_math_fast.h index e979bd9e0c0..07b0878e3d5 100644 --- a/intern/cycles/util/util_math_fast.h +++ b/intern/cycles/util/util_math_fast.h @@ -87,7 +87,7 @@ ccl_device_inline int fast_rint(float x) /* Single roundps instruction on SSE4.1+ (for gcc/clang at least). */ return float_to_int(rintf(x)); #else - /* emulate rounding by adding/substracting 0.5. */ + /* emulate rounding by adding/subtracting 0.5. */ return float_to_int(x + copysignf(0.5f, x)); #endif } @@ -445,12 +445,10 @@ ccl_device_inline float fast_expf(float x) return fast_exp2f(x / M_LN2_F); } -#ifndef __KERNEL_GPU__ -/* MSVC seems to have a code-gen bug here in at least SSE41/AVX - * see T78047 for details. */ -# ifdef _MSC_VER -# pragma optimize("", off) -# endif +#if defined(__KERNEL_CPU__) && !defined(_MSC_VER) +/* MSVC seems to have a code-gen bug here in at least SSE41/AVX, see + * T78047 and T78869 for details. Just disable for now, it only makes + * a small difference in denoising performance. */ ccl_device float4 fast_exp2f4(float4 x) { const float4 one = make_float4(1.0f); @@ -466,14 +464,16 @@ ccl_device float4 fast_exp2f4(float4 x) r = madd4(x, r, make_float4(1.0f)); return __int4_as_float4(__float4_as_int4(r) + (m << 23)); } -# ifdef _MSC_VER -# pragma optimize("", on) -# endif ccl_device_inline float4 fast_expf4(float4 x) { return fast_exp2f4(x / M_LN2_F); } +#else +ccl_device_inline float4 fast_expf4(float4 x) +{ + return make_float4(fast_expf(x.x), fast_expf(x.y), fast_expf(x.z), fast_expf(x.w)); +} #endif ccl_device_inline float fast_exp10(float x) diff --git a/intern/cycles/util/util_math_float4.h b/intern/cycles/util/util_math_float4.h index cd4b3e3b74c..ec5328adb31 100644 --- a/intern/cycles/util/util_math_float4.h +++ b/intern/cycles/util/util_math_float4.h @@ -477,6 +477,24 @@ ccl_device_inline float4 safe_divide_float4_float(const float4 a, const float b) return (b != 0.0f) ? a / b : make_float4(0.0f, 0.0f, 0.0f, 0.0f); } +ccl_device_inline bool isfinite4_safe(float4 v) +{ + return isfinite_safe(v.x) && isfinite_safe(v.y) && isfinite_safe(v.z) && isfinite_safe(v.w); +} + +ccl_device_inline float4 ensure_finite4(float4 v) +{ + if (!isfinite_safe(v.x)) + v.x = 0.0f; + if (!isfinite_safe(v.y)) + v.y = 0.0f; + if (!isfinite_safe(v.z)) + v.z = 0.0f; + if (!isfinite_safe(v.w)) + v.w = 0.0f; + return v; +} + CCL_NAMESPACE_END #endif /* __UTIL_MATH_FLOAT4_H__ */ diff --git a/intern/cycles/util/util_openvdb.h b/intern/cycles/util/util_openvdb.h new file mode 100644 index 00000000000..a3ebb03e5a4 --- /dev/null +++ b/intern/cycles/util/util_openvdb.h @@ -0,0 +1,32 @@ +/* + * Copyright 2011-2020 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. + */ + +#ifndef __UTIL_OPENVDB_H__ +#define __UTIL_OPENVDB_H__ + +#ifdef WITH_OPENVDB +# include <openvdb/openvdb.h> + +namespace openvdb { + +using Vec4fTree = tree::Tree4<Vec4f, 5, 4, 3>::Type; +using Vec4fGrid = Grid<Vec4fTree>; + +}; // namespace openvdb + +#endif + +#endif /* __UTIL_OPENVDB_H__ */ diff --git a/intern/cycles/util/util_transform.cpp b/intern/cycles/util/util_transform.cpp index 101122740d7..6417752f704 100644 --- a/intern/cycles/util/util_transform.cpp +++ b/intern/cycles/util/util_transform.cpp @@ -269,17 +269,17 @@ static void transform_decompose(DecomposedTransform *decomp, const Transform *tf /* extract scale and shear first */ float3 scale, shear; scale.x = len(colx); - colx /= scale.x; + colx = safe_divide_float3_float(colx, scale.x); shear.z = dot(colx, coly); coly -= shear.z * colx; scale.y = len(coly); - coly /= scale.y; + coly = safe_divide_float3_float(coly, scale.y); shear.y = dot(colx, colz); colz -= shear.y * colx; shear.x = dot(coly, colz); colz -= shear.x * coly; scale.z = len(colz); - colz /= scale.z; + colz = safe_divide_float3_float(colz, scale.z); transform_set_column(&M, 0, colx); transform_set_column(&M, 1, coly); @@ -300,6 +300,7 @@ static void transform_decompose(DecomposedTransform *decomp, const Transform *tf void transform_motion_decompose(DecomposedTransform *decomp, const Transform *motion, size_t size) { + /* Decompose and correct rotation. */ for (size_t i = 0; i < size; i++) { transform_decompose(decomp + i, motion + i); @@ -310,6 +311,27 @@ void transform_motion_decompose(DecomposedTransform *decomp, const Transform *mo decomp[i].x = -decomp[i].x; } } + + /* Copy rotation to decomposed transform where scale is degenerate. This avoids weird object + * rotation interpolation when the scale goes to 0 for a time step. + * + * Note that this is very simple and naive implementation, which only deals with degenerated + * scale happening only on one frame. It is possible to improve it further by interpolating + * rotation into s degenerated range using rotation from timesteps from adjacent non-degenerated + * time steps. */ + for (size_t i = 0; i < size; i++) { + const float3 scale = make_float3(decomp[i].y.w, decomp[i].z.w, decomp[i].w.w); + if (!is_zero(scale)) { + continue; + } + + if (i > 0) { + decomp[i].x = decomp[i - 1].x; + } + else if (i < size - 1) { + decomp[i].x = decomp[i + 1].x; + } + } } Transform transform_from_viewplane(BoundBox2D &viewplane) diff --git a/intern/cycles/util/util_transform.h b/intern/cycles/util/util_transform.h index d0a6264d5cf..d8bbd389aa6 100644 --- a/intern/cycles/util/util_transform.h +++ b/intern/cycles/util/util_transform.h @@ -466,6 +466,17 @@ ccl_device void transform_motion_array_interpolate(Transform *tfm, transform_compose(tfm, &decomp); } +ccl_device_inline bool transform_isfinite_safe(Transform *tfm) +{ + return isfinite4_safe(tfm->x) && isfinite4_safe(tfm->y) && isfinite4_safe(tfm->z); +} + +ccl_device_inline bool transform_decomposed_isfinite_safe(DecomposedTransform *decomp) +{ + return isfinite4_safe(decomp->x) && isfinite4_safe(decomp->y) && isfinite4_safe(decomp->z) && + isfinite4_safe(decomp->w); +} + #ifndef __KERNEL_GPU__ class BoundBox2D; |