From e1db45c41a9acb9dadbb40183e1037d441a15035 Mon Sep 17 00:00:00 2001 From: Geraldine Chua Date: Tue, 5 Jun 2018 21:35:24 +0800 Subject: Updates to tiling function. 1. Supports all data types (or at least the ones enumerated in ImageDataType). 2. Will tile all volume attributes. 3. Changed method of activating tiling to a bool member of Image. --- intern/cycles/blender/blender_mesh.cpp | 2 + intern/cycles/blender/blender_session.cpp | 14 +- .../cycles/kernel/kernels/cpu/kernel_cpu_image.h | 47 +-- intern/cycles/render/image.cpp | 412 ++++++++------------- intern/cycles/render/image.h | 24 +- intern/cycles/render/nodes.cpp | 18 +- intern/cycles/util/CMakeLists.txt | 3 +- intern/cycles/util/util_image_impl.h | 45 ++- intern/cycles/util/util_sparse_grid.cpp | 174 --------- intern/cycles/util/util_sparse_grid.h | 205 ++++++++-- intern/cycles/util/util_texture.h | 1 - 11 files changed, 418 insertions(+), 527 deletions(-) delete mode 100644 intern/cycles/util/util_sparse_grid.cpp diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index 7d6ca18b074..8bf3f66dff6 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -342,6 +342,7 @@ static void create_mesh_volume_attribute(BL::Object& b_ob, ImageMetaData metadata; bool animated = false; bool use_alpha = true; + bool make_sparse = true; volume_data->manager = image_manager; volume_data->slot = image_manager->add_image( @@ -352,6 +353,7 @@ static void create_mesh_volume_attribute(BL::Object& b_ob, INTERPOLATION_LINEAR, EXTENSION_CLIP, use_alpha, + make_sparse, metadata); } diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 649d6edc679..00d23b9095e 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -1051,7 +1051,6 @@ void BlenderSession::builtin_image_info(const string &builtin_name, BL::SmokeDomainSettings b_domain = object_smoke_domain_find(b_ob); metadata.is_float = true; - metadata.is_volume = false; metadata.depth = 1; metadata.channels = 1; @@ -1061,19 +1060,14 @@ void BlenderSession::builtin_image_info(const string &builtin_name, if(builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_DENSITY) || builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_FLAME) || builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_HEAT) || - builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_TEMPERATURE)) { + builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_TEMPERATURE)) metadata.channels = 1; - } - else if(builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_COLOR)) { + else if(builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_COLOR)) metadata.channels = 4; - metadata.is_volume = true; - } - else if(builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY)) { + else if(builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY)) metadata.channels = 3; - } - else { + else return; - } int3 resolution = get_int3(b_domain.domain_resolution()); int amplify = (b_domain.use_high_resolution())? b_domain.amplify() + 1: 1; diff --git a/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h b/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h index 3c18b08d5b8..3435bcab70a 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h +++ b/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h @@ -21,8 +21,7 @@ CCL_NAMESPACE_BEGIN -template -struct TextureInterpolator { +template struct TextureInterpolator { #define SET_CUBIC_SPLINE_WEIGHTS(u, t) \ { \ u[0] = (((-1.0f/6.0f)* t + 0.5f) * t - 0.5f) * t + (1.0f/6.0f); \ @@ -282,10 +281,13 @@ struct TextureInterpolator { return make_float4(0.0f); } - if(is_tile) { - const SparseTile *data = (const SparseTile*)info.data; - const int *ofs = (const int*)info.offsets; - return read(get_value(data, ofs, ix, iy, iz, width, height, depth)); + const int *ofs = (const int*)info.offsets; + if(ofs) { + const SparseTile *data = (const SparseTile*)info.data; + return read(get_value(data, ofs, ix, iy, iz, + compute_tile_resolution(width), + compute_tile_resolution(height), + compute_tile_resolution(depth))); } else { const T *data = (const T*)info.data; @@ -338,23 +340,26 @@ struct TextureInterpolator { } float4 r; + const int *ofs = (const int*)info.offsets; - if(is_tile) { - const SparseTile *data = (const SparseTile*)info.data; - const int *ofs = (const int*)info.offsets; + if(ofs) { + const SparseTile *data = (const SparseTile*)info.data; + int tiw = compute_tile_resolution(width); + int tih = compute_tile_resolution(height); + int tid = compute_tile_resolution(depth); /* Initial check if either voxel is in an active tile. */ - if(!is_active(ofs, ix, iy, iz, width, height, depth) && - !is_active(ofs, nix, niy, niz, width, height, depth)) { + if(!tile_is_active(ofs, ix, iy, iz, tiw, tih, tid) && + !tile_is_active(ofs, nix, niy, niz, tiw, tih, tid)) { return make_float4(0.0f); } - r = (1.0f - tz)*(1.0f - ty)*(1.0f - tx) * read(get_value(data, ofs, ix, iy, iz, width, height, depth)); - r += (1.0f - tz)*(1.0f - ty)*tx * read(get_value(data, ofs, nix, iy, iz, width, height, depth)); - r += (1.0f - tz)*ty*(1.0f - tx) * read(get_value(data, ofs, ix, niy, iz, width, height, depth)); - r += (1.0f - tz)*ty*tx * read(get_value(data, ofs, nix, niy, iz, width, height, depth)); - r += tz*(1.0f - ty)*(1.0f - tx) * read(get_value(data, ofs, ix, iy, niz, width, height, depth)); - r += tz*(1.0f - ty)*tx * read(get_value(data, ofs, nix, iy, niz, width, height, depth)); - r += tz*ty*(1.0f - tx) * read(get_value(data, ofs, ix, niy, niz, width, height, depth)); - r += tz*ty*tx * read(get_value(data, ofs, nix, niy, niz, width, height, depth)); + r = (1.0f - tz)*(1.0f - ty)*(1.0f - tx) * read(get_value(data, ofs, ix, iy, iz, tiw, tih, tid)); + r += (1.0f - tz)*(1.0f - ty)*tx * read(get_value(data, ofs, nix, iy, iz, tiw, tih, tid)); + r += (1.0f - tz)*ty*(1.0f - tx) * read(get_value(data, ofs, ix, niy, iz, tiw, tih, tid)); + r += (1.0f - tz)*ty*tx * read(get_value(data, ofs, nix, niy, iz, tiw, tih, tid)); + r += tz*(1.0f - ty)*(1.0f - tx) * read(get_value(data, ofs, ix, iy, niz, tiw, tih, tid)); + r += tz*(1.0f - ty)*tx * read(get_value(data, ofs, nix, iy, niz, tiw, tih, tid)); + r += tz*ty*(1.0f - tx) * read(get_value(data, ofs, ix, niy, niz, tiw, tih, tid)); + r += tz*ty*tx * read(get_value(data, ofs, nix, niy, niz, tiw, tih, tid)); } else { const T *data = (const T*)info.data; @@ -384,8 +389,6 @@ struct TextureInterpolator { #endif float4 interp_3d_tricubic(const TextureInfo& info, float x, float y, float z) { - /* todo (gchua): add tile support for this */ - kernel_assert(!is_tile); int width = info.width; int height = info.height; int depth = info.depth; @@ -538,8 +541,6 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg, int id, float x, return TextureInterpolator::interp_3d(info, x, y, z, interp); case IMAGE_DATA_TYPE_BYTE4: return TextureInterpolator::interp_3d(info, x, y, z, interp); - case IMAGE_DATA_TYPE_VOLUME: - return TextureInterpolator::interp_3d(info, x, y, z, interp); case IMAGE_DATA_TYPE_FLOAT4: default: return TextureInterpolator::interp_3d(info, x, y, z, interp); diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp index f7e2f6488a1..c1caea34619 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/render/image.cpp @@ -22,6 +22,7 @@ #include "util/util_logging.h" #include "util/util_path.h" #include "util/util_progress.h" +#include "util/util_sparse_grid.h" #include "util/util_texture.h" #ifdef WITH_OSL @@ -108,10 +109,7 @@ bool ImageManager::get_image_metadata(const string& filename, return false; } - if(metadata.is_volume) { - metadata.type = IMAGE_DATA_TYPE_VOLUME; - } - else if(metadata.is_float) { + if(metadata.is_float) { metadata.is_linear = true; metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_FLOAT4 : IMAGE_DATA_TYPE_FLOAT; } @@ -186,10 +184,7 @@ bool ImageManager::get_image_metadata(const string& filename, /* set type and channels */ metadata.channels = spec.nchannels; - if(metadata.is_volume) { - metadata.type = IMAGE_DATA_TYPE_VOLUME; - } - else if(metadata.is_half) { + if(metadata.is_half) { metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_HALF4 : IMAGE_DATA_TYPE_HALF; } else if(metadata.is_float) { @@ -241,8 +236,6 @@ string ImageManager::name_from_type(int type) return "half4"; else if(type == IMAGE_DATA_TYPE_HALF) return "half"; - else if(type == IMAGE_DATA_TYPE_VOLUME) - return "volume"; else return "byte4"; } @@ -268,6 +261,7 @@ int ImageManager::add_image(const string& filename, InterpolationType interpolation, ExtensionType extension, bool use_alpha, + bool make_sparse, ImageMetaData& metadata) { Image *img; @@ -345,6 +339,7 @@ int ImageManager::add_image(const string& filename, img->extension = extension; img->users = 1; img->use_alpha = use_alpha; + img->make_sparse = make_sparse; img->mem = NULL; images[type][slot] = img; @@ -582,7 +577,6 @@ bool ImageManager::file_load_image(Image *img, * but device doesn't support single channel textures. */ bool is_rgba = (type == IMAGE_DATA_TYPE_FLOAT4 || - type == IMAGE_DATA_TYPE_VOLUME || type == IMAGE_DATA_TYPE_HALF4 || type == IMAGE_DATA_TYPE_BYTE4); if(is_rgba) { @@ -691,87 +685,160 @@ bool ImageManager::file_load_image(Image *img, return true; } - -/* If volume, call this to convert to sparse grid. */ -template -bool ImageManager::file_load_image(Image *img, - ImageDataType type, - int texture_limit, - device_vector& tex_img, - device_vector& tex_offsets) +void ImageManager::file_load_failed(device_vector *tex_img, + ImageDataType type) { + /* On failure to load, we set a 1x1 pixels pink image. */ + thread_scoped_lock device_lock(device_mutex); + StorageType *pixels = (StorageType*)tex_img->alloc(1, 1); - device_vector *tex_img_dense - = new device_vector(NULL, img->mem_name.c_str(), MEM_TEXTURE); - - if(!file_load_image(img, - type, - texture_limit, - *tex_img_dense)) - { - /* Release temporary pointer. */ - delete tex_img_dense; - tex_img_dense = NULL; - return false; + switch(type) { + case IMAGE_DATA_TYPE_FLOAT4: + pixels[0] = TEX_IMAGE_MISSING_R; + pixels[1] = TEX_IMAGE_MISSING_G; + pixels[2] = TEX_IMAGE_MISSING_B; + pixels[3] = TEX_IMAGE_MISSING_A; + break; + case IMAGE_DATA_TYPE_FLOAT: + pixels[0] = TEX_IMAGE_MISSING_R; + break; + case IMAGE_DATA_TYPE_BYTE4: + pixels[0] = (TEX_IMAGE_MISSING_R * 255); + pixels[1] = (TEX_IMAGE_MISSING_G * 255); + pixels[2] = (TEX_IMAGE_MISSING_B * 255); + pixels[3] = (TEX_IMAGE_MISSING_A * 255); + break; + case IMAGE_DATA_TYPE_BYTE: + pixels[0] = (TEX_IMAGE_MISSING_R * 255); + break; + case IMAGE_DATA_TYPE_HALF4: + pixels[0] = TEX_IMAGE_MISSING_R; + pixels[1] = TEX_IMAGE_MISSING_G; + pixels[2] = TEX_IMAGE_MISSING_B; + pixels[3] = TEX_IMAGE_MISSING_A; + break; + case IMAGE_DATA_TYPE_HALF: + pixels[0] = TEX_IMAGE_MISSING_R; + break; + default: + assert(0); } +} - DeviceType *data = tex_img_dense->data(); - size_t tile_width = compute_tile_resolution(tex_img_dense->data_width); - size_t tile_height = compute_tile_resolution(tex_img_dense->data_height); - size_t tile_depth = compute_tile_resolution(tex_img_dense->data_depth); - - vector sparse_grid; +template +bool ImageManager::file_make_image_sparse(Device *device, + Image *img, + device_vector *tex_dense) +{ + device_vector> *tex_sparse + = new device_vector>(device, + (img->mem_name).c_str(), + MEM_TEXTURE); + device_vector *tex_offsets + = new device_vector(device, + (img->mem_name + "_offsets").c_str(), + MEM_TEXTURE); + + vector> sparse_grid; vector offsets; - /* Sample threshold value for now. */ - float4 threshold = make_float4(0.0f); - int active_tile_count = create_sparse_grid(data, threshold, - tex_img_dense->data_width, - tex_img_dense->data_height, - tex_img_dense->data_depth, - &sparse_grid, &offsets); + int active_tile_count = create_sparse_grid( + tex_dense->data(), + tex_dense->data_width, + tex_dense->data_height, + tex_dense->data_depth, + &sparse_grid, + &offsets); if(active_tile_count < 1) { - /* to-do (gchua): handle this. */ + VLOG(1) << "Could not make sparse grid for " + << path_filename(img->filename) << " (" << img->mem_name << ")" + << ", no active tiles"; + delete tex_sparse; + delete tex_offsets; + tex_sparse = NULL; + tex_offsets = NULL; + return false; } - SparseTile *texture_pixels; + SparseTile *texture_pixels; int *texture_offsets; + int tiw = compute_tile_resolution(tex_dense->data_width); + int tih = compute_tile_resolution(tex_dense->data_height); + int tid = compute_tile_resolution(tex_dense->data_depth); + { - /* Since only active tiles are stored in tex_img, its + /* Since only active tiles are stored in tex_sparse, its * allocated memory will be <= the actual resolution * of the volume. We store the true resolution (in tiles) in the * tex_offsets instead, since it needs to be allocated enough * space to track all tiles anyway. */ thread_scoped_lock device_lock(device_mutex); - texture_pixels = (SparseTile*)tex_img.alloc(active_tile_count); - texture_offsets = (int*)tex_offsets.alloc(tile_width, - tile_height, - tile_depth); + texture_pixels = (SparseTile*)tex_sparse->alloc(active_tile_count); + texture_offsets = (int*)tex_offsets->alloc(tiw, tih, tid); } - memcpy(texture_offsets, + memcpy(&texture_offsets[0], &offsets[0], offsets.size() * sizeof(int)); - memcpy(texture_pixels, + memcpy(&texture_pixels[0], &sparse_grid[0], - active_tile_count * sizeof(SparseTile)); + active_tile_count * sizeof(SparseTile)); - VLOG(1) << "Memory usage of dense grid '" << img->filename << "': " - << string_human_readable_size(tex_img_dense->memory_size()); - VLOG(1) << "Memory usage of sparse grid '" << img->filename << "': " - << string_human_readable_size(tex_img.memory_size()); - VLOG(1) << "Memory usage of auxiliary index: " - << string_human_readable_size(tex_offsets.memory_size()); + img->mem = tex_sparse; + img->mem->interpolation = img->interpolation; + img->mem->extension = img->extension; + img->mem->offsets = tex_offsets; - /* Release temporary pointer. */ - delete tex_img_dense; - tex_img_dense = NULL; + thread_scoped_lock device_lock(device_mutex); + tex_sparse->copy_to_device(); + + VLOG(1) << "Original memory usage of '" + << path_filename(img->filename) << "' (" << img->mem_name << "): " + << string_human_readable_size(tex_dense->memory_size()); return true; } +template +void ImageManager::load_image(Device *device, + Image *img, + ImageDataType type, + int texture_limit) +{ + device_vector *tex_img + = new device_vector(device, + img->mem_name.c_str(), + MEM_TEXTURE); + + if(!file_load_image(img, + type, + texture_limit, + *tex_img)) { + VLOG(1) << "Failed to load " + << path_filename(img->filename) << " (" << img->mem_name << ")"; + file_load_failed(tex_img, type); + } + + if(img->make_sparse) { + if(file_make_image_sparse(device, img, tex_img)) { + delete tex_img; + tex_img = NULL; + } + } + + if(tex_img) { + img->mem = tex_img; + img->mem->interpolation = img->interpolation; + img->mem->extension = img->extension; + thread_scoped_lock device_lock(device_mutex); + tex_img->copy_to_device(); + } +} + void ImageManager::device_load_image(Device *device, Scene *scene, ImageDataType type, @@ -807,198 +874,27 @@ void ImageManager::device_load_image(Device *device, } /* Create new texture. */ - if(type == IMAGE_DATA_TYPE_FLOAT4) { - device_vector *tex_img - = new device_vector(device, img->mem_name.c_str(), MEM_TEXTURE); - - if(!file_load_image(img, - type, - texture_limit, - *tex_img)) - { - /* on failure to load, we set a 1x1 pixels pink image */ - thread_scoped_lock device_lock(device_mutex); - float *pixels = (float*)tex_img->alloc(1, 1); - - pixels[0] = TEX_IMAGE_MISSING_R; - pixels[1] = TEX_IMAGE_MISSING_G; - pixels[2] = TEX_IMAGE_MISSING_B; - pixels[3] = TEX_IMAGE_MISSING_A; - } - - img->mem = tex_img; - img->mem->interpolation = img->interpolation; - img->mem->extension = img->extension; - - thread_scoped_lock device_lock(device_mutex); - tex_img->copy_to_device(); - } - else if(type == IMAGE_DATA_TYPE_FLOAT) { - device_vector *tex_img - = new device_vector(device, img->mem_name.c_str(), MEM_TEXTURE); - - if(!file_load_image(img, - type, - texture_limit, - *tex_img)) - { - /* on failure to load, we set a 1x1 pixels pink image */ - thread_scoped_lock device_lock(device_mutex); - float *pixels = (float*)tex_img->alloc(1, 1); - - pixels[0] = TEX_IMAGE_MISSING_R; - } - - img->mem = tex_img; - img->mem->interpolation = img->interpolation; - img->mem->extension = img->extension; - - thread_scoped_lock device_lock(device_mutex); - tex_img->copy_to_device(); - } - else if(type == IMAGE_DATA_TYPE_BYTE4) { - device_vector *tex_img - = new device_vector(device, img->mem_name.c_str(), MEM_TEXTURE); - - if(!file_load_image(img, - type, - texture_limit, - *tex_img)) - { - /* on failure to load, we set a 1x1 pixels pink image */ - thread_scoped_lock device_lock(device_mutex); - uchar *pixels = (uchar*)tex_img->alloc(1, 1); - - pixels[0] = (TEX_IMAGE_MISSING_R * 255); - pixels[1] = (TEX_IMAGE_MISSING_G * 255); - pixels[2] = (TEX_IMAGE_MISSING_B * 255); - pixels[3] = (TEX_IMAGE_MISSING_A * 255); - } - - img->mem = tex_img; - img->mem->interpolation = img->interpolation; - img->mem->extension = img->extension; - - thread_scoped_lock device_lock(device_mutex); - tex_img->copy_to_device(); - } - else if(type == IMAGE_DATA_TYPE_BYTE) { - device_vector *tex_img - = new device_vector(device, img->mem_name.c_str(), MEM_TEXTURE); - - if(!file_load_image(img, - type, - texture_limit, - *tex_img)) { - /* on failure to load, we set a 1x1 pixels pink image */ - thread_scoped_lock device_lock(device_mutex); - uchar *pixels = (uchar*)tex_img->alloc(1, 1); - - pixels[0] = (TEX_IMAGE_MISSING_R * 255); - } - - img->mem = tex_img; - img->mem->interpolation = img->interpolation; - img->mem->extension = img->extension; - - thread_scoped_lock device_lock(device_mutex); - tex_img->copy_to_device(); - } - else if(type == IMAGE_DATA_TYPE_HALF4) { - device_vector *tex_img - = new device_vector(device, img->mem_name.c_str(), MEM_TEXTURE); - - if(!file_load_image(img, - type, - texture_limit, - *tex_img)) { - /* on failure to load, we set a 1x1 pixels pink image */ - thread_scoped_lock device_lock(device_mutex); - half *pixels = (half*)tex_img->alloc(1, 1); - - pixels[0] = TEX_IMAGE_MISSING_R; - pixels[1] = TEX_IMAGE_MISSING_G; - pixels[2] = TEX_IMAGE_MISSING_B; - pixels[3] = TEX_IMAGE_MISSING_A; - } - - img->mem = tex_img; - img->mem->interpolation = img->interpolation; - img->mem->extension = img->extension; - - thread_scoped_lock device_lock(device_mutex); - tex_img->copy_to_device(); - } - else if(type == IMAGE_DATA_TYPE_HALF) { - device_vector *tex_img - = new device_vector(device, img->mem_name.c_str(), MEM_TEXTURE); - - if(!file_load_image(img, - type, - texture_limit, - *tex_img)) { - /* on failure to load, we set a 1x1 pixels pink image */ - thread_scoped_lock device_lock(device_mutex); - half *pixels = (half*)tex_img->alloc(1, 1); - - pixels[0] = TEX_IMAGE_MISSING_R; - } - - img->mem = tex_img; - img->mem->interpolation = img->interpolation; - img->mem->extension = img->extension; - - thread_scoped_lock device_lock(device_mutex); - tex_img->copy_to_device(); - } - else if(type == IMAGE_DATA_TYPE_VOLUME) { - device_vector *tex_img - = new device_vector(device, img->mem_name.c_str(), MEM_TEXTURE); - device_vector *tex_offsets - = new device_vector(device, (img->mem_name + "_offsets").c_str(), MEM_TEXTURE); - - if(!file_load_image(img, - type, - texture_limit, - *tex_img, - *tex_offsets)) - { - /* Clear pointers. */ - delete tex_img; - delete tex_offsets; - tex_img = NULL; - tex_offsets = NULL; - - /* on failure to load, we set a 1x1 pixels pink image (float4) */ - device_vector *tex_fail - = new device_vector(device, img->mem_name.c_str(), MEM_TEXTURE); - - thread_scoped_lock device_lock(device_mutex); - float *pixels = (float*)tex_fail->alloc(1, 1); - - pixels[0] = TEX_IMAGE_MISSING_R; - pixels[1] = TEX_IMAGE_MISSING_G; - pixels[2] = TEX_IMAGE_MISSING_B; - pixels[3] = TEX_IMAGE_MISSING_A; - - img->mem = tex_fail; - img->mem->interpolation = img->interpolation; - img->mem->extension = img->extension; - tex_fail->copy_to_device(); - } - else { - img->mem = tex_img; - img->mem->interpolation = img->interpolation; - img->mem->extension = img->extension; - img->mem->offets = tex_offsets; - /* Need to set interpolation so that tex_alloc() will treat - * tex_offsets as a image instead of data texture. */ - tex_offsets->interpolation = img->interpolation; - - thread_scoped_lock device_lock(device_mutex); - tex_img->copy_to_device(); - tex_offsets->copy_to_device(); - } + switch(type) { + case IMAGE_DATA_TYPE_FLOAT4: + load_image(device, img, type, texture_limit); + break; + case IMAGE_DATA_TYPE_FLOAT: + load_image(device, img, type, texture_limit); + break; + case IMAGE_DATA_TYPE_BYTE4: + load_image(device, img, type, texture_limit); + break; + case IMAGE_DATA_TYPE_BYTE: + load_image(device, img, type, texture_limit); + break; + case IMAGE_DATA_TYPE_HALF4: + load_image(device, img, type, texture_limit); + break; + case IMAGE_DATA_TYPE_HALF: + load_image(device, img, type, texture_limit); + break; + default: + assert(0); } img->need_load = false; @@ -1007,6 +903,7 @@ void ImageManager::device_load_image(Device *device, void ImageManager::device_free_image(Device *, ImageDataType type, int slot) { Image *img = images[type][slot]; + VLOG(1) << "Freeing " << img->mem_name; if(img) { if(osl_texture_system && !img->builtin_data) { @@ -1018,6 +915,9 @@ void ImageManager::device_free_image(Device *, ImageDataType type, int slot) if(img->mem) { thread_scoped_lock device_lock(device_mutex); + if(img->mem->offsets){ + delete img->mem->offsets; + } delete img->mem; } diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h index 0b493110076..7ef9cfb5ba5 100644 --- a/intern/cycles/render/image.h +++ b/intern/cycles/render/image.h @@ -24,7 +24,6 @@ #include "util/util_string.h" #include "util/util_thread.h" #include "util/util_vector.h" -#include "util/util_sparse_grid.h" CCL_NAMESPACE_BEGIN @@ -35,7 +34,7 @@ class Scene; class ImageMetaData { public: /* Must be set by image file or builtin callback. */ - bool is_float, is_half, is_volume; + bool is_float, is_half; int channels; size_t width, height, depth; bool builtin_free_cache; @@ -57,6 +56,7 @@ public: InterpolationType interpolation, ExtensionType extension, bool use_alpha, + bool make_sparse, ImageMetaData& metadata); void remove_image(int flat_slot); void remove_image(const string& filename, @@ -116,6 +116,7 @@ public: bool use_alpha; bool need_load; bool animated; + bool make_sparse; float frame; InterpolationType interpolation; ExtensionType extension; @@ -152,14 +153,23 @@ private: int texture_limit, device_vector& tex_img); + template + void file_load_failed(device_vector *tex_img, + ImageDataType type); + + template + bool file_make_image_sparse(Device *device, + Image *img, + device_vector *tex_dense); + template - bool file_load_image(Image *img, - ImageDataType type, - int texture_limit, - device_vector& tex_img, - device_vector& tex_offsets); + void load_image(Device *device, + Image *img, + ImageDataType type, + int texture_limit); int max_flattened_slot(ImageDataType type); int type_index_to_flattened_slot(int slot, ImageDataType type); diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 3dad4d1a346..71b1d42b25e 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -307,6 +307,7 @@ void ImageTextureNode::compile(SVMCompiler& compiler) interpolation, extension, use_alpha, + false, metadata); is_float = metadata.is_float; is_linear = metadata.is_linear; @@ -372,6 +373,7 @@ void ImageTextureNode::compile(OSLCompiler& compiler) interpolation, extension, use_alpha, + false, metadata); } is_float = metadata.is_float; @@ -502,6 +504,7 @@ void EnvironmentTextureNode::compile(SVMCompiler& compiler) interpolation, EXTENSION_REPEAT, use_alpha, + false, metadata); is_float = metadata.is_float; is_linear = metadata.is_linear; @@ -558,6 +561,7 @@ void EnvironmentTextureNode::compile(OSLCompiler& compiler) interpolation, EXTENSION_REPEAT, use_alpha, + false, metadata); } is_float = metadata.is_float; @@ -1502,11 +1506,14 @@ void PointDensityTextureNode::compile(SVMCompiler& compiler) if(use_density || use_color) { if(slot == -1) { ImageMetaData metadata; - slot = image_manager->add_image(filename.string(), builtin_data, - false, 0, + slot = image_manager->add_image(filename.string(), + builtin_data, + false, + 0, interpolation, EXTENSION_CLIP, true, + false, metadata); } @@ -1553,11 +1560,14 @@ void PointDensityTextureNode::compile(OSLCompiler& compiler) if(use_density || use_color) { if(slot == -1) { ImageMetaData metadata; - slot = image_manager->add_image(filename.string(), builtin_data, - false, 0, + slot = image_manager->add_image(filename.string(), + builtin_data, + false, + 0, interpolation, EXTENSION_CLIP, true, + false, metadata); } diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt index 940e9d0441e..005b1d41dc2 100644 --- a/intern/cycles/util/CMakeLists.txt +++ b/intern/cycles/util/CMakeLists.txt @@ -18,7 +18,6 @@ set(SRC util_path.cpp util_string.cpp util_simd.cpp - util_sparse_grid.cpp util_system.cpp util_task.cpp util_thread.cpp @@ -79,7 +78,7 @@ set(SRC_HEADERS util_sky_model.h util_sky_model_data.h util_avxf.h - util_sparse_grid.cpp + util_sparse_grid.h util_sseb.h util_ssef.h util_ssei.h diff --git a/intern/cycles/util/util_image_impl.h b/intern/cycles/util/util_image_impl.h index 751f52aaa86..c60588dcedb 100644 --- a/intern/cycles/util/util_image_impl.h +++ b/intern/cycles/util/util_image_impl.h @@ -23,21 +23,6 @@ CCL_NAMESPACE_BEGIN -namespace { - -template -const T *util_image_read(const vector& pixels, - const size_t width, - const size_t height, - const size_t /*depth*/, - const size_t components, - const size_t x, const size_t y, const size_t z) { - const size_t index = ((size_t)z * (width * height) + - (size_t)y * width + - (size_t)x) * components; - return &pixels[index]; -} - /* Cast input pixel from unknown storage to float. */ template inline float cast_to_float(T value); @@ -83,6 +68,36 @@ inline half cast_from_float(float value) { return float_to_half(value); } +template<> +inline float4 cast_from_float(float value) +{ + return make_float4(value); +} +template<> +inline uchar4 cast_from_float(float value) +{ + return make_uchar4(cast_from_float(value)); +} +template<> +inline half4 cast_from_float(float value) +{ + return make_half4(cast_from_float(value)); +} + +namespace { + +template +const T *util_image_read(const vector& pixels, + const size_t width, + const size_t height, + const size_t /*depth*/, + const size_t components, + const size_t x, const size_t y, const size_t z) { + const size_t index = ((size_t)z * (width * height) + + (size_t)y * width + + (size_t)x) * components; + return &pixels[index]; +} template void util_image_downscale_sample(const vector& pixels, diff --git a/intern/cycles/util/util_sparse_grid.cpp b/intern/cycles/util/util_sparse_grid.cpp deleted file mode 100644 index e5ae32b3f78..00000000000 --- a/intern/cycles/util/util_sparse_grid.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2011-2018 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 "util/util_sparse_grid.h" -#include "util/util_math.h" - -/* Functions to help generate and handle Sparse Grids. */ - -CCL_NAMESPACE_BEGIN - -int compute_index(int x, int y, int z, int width, int height, int depth, int min) -{ - if(x < min || y < min || z < min || x >= width || y >= height || z >= depth) { - return min-1; - } - return x + width * (y + z * height); -} - -int3 compute_coordinates(int index, int width, int height, int depth, int min) -{ - if(index < min || index >= width * height * depth) { - return make_int3(min-1, min-1, min-1); - } - int x = index % width; - int y = (index / width) % height; - int z = index / (width * height); - return make_int3(x, y, z); -} - -int compute_tile_resolution(int res) -{ - if(res % TILE_SIZE == 0) { - return res / TILE_SIZE; - } - return res / TILE_SIZE + 1; -} - -bool is_active(const int *offsets, - size_t x, size_t y, size_t z, - size_t width, size_t height, size_t depth) -{ - if(offsets == NULL) - return false; - int tile_width = compute_tile_resolution(width); - int tile_height = compute_tile_resolution(height); - int tile_depth = compute_tile_resolution(depth); - int tile_index = compute_index(x/TILE_SIZE, y/TILE_SIZE, z/TILE_SIZE, - tile_width, tile_height, tile_depth); - if(tile_index < 0 || tile_index > tile_width*tile_height*tile_depth) - return false; - int grid_index = offsets[tile_index]; - return (grid_index >= 0 && grid_index < width*height*depth); -} - -float4 get_value(const SparseTile *grid, const int *offsets, - size_t x, size_t y, size_t z, - size_t width, size_t height, size_t depth) -{ - if(offsets == NULL) { - return make_float4(0.0f); - } - int tile_width = compute_tile_resolution(width); - int tile_height = compute_tile_resolution(height); - int tile_depth = compute_tile_resolution(depth); - /* Get the 1D array tile index of the tile the voxel (x, y, z) is in. */ - int tile_index = compute_index(x/TILE_SIZE, y/TILE_SIZE, z/TILE_SIZE, - tile_width, tile_height, tile_depth); - if(tile_index < 0 || tile_index > tile_width*tile_height*tile_depth) { - return make_float4(0.0f); - } - /* Get the index of the tile in the sparse grid. */ - int grid_index = offsets[tile_index]; - if (grid_index < 0 || grid_index > width*height*depth) { - return make_float4(0.0f); - } - /* Get tile and look up voxel in tile. */ - int voxel_index = compute_index(x%TILE_SIZE, y%TILE_SIZE, z%TILE_SIZE); - return grid[grid_index].values[voxel_index]; -} - -float4 get_value(const SparseTile *grid, const int *offsets, - size_t tile_index, - size_t width, size_t height, size_t depth) -{ - int3 c = compute_coordinates(tile_index, width, height, depth); - return get_value(grid, offsets, c.x, c.y, c.z, width, height, depth); -} - -int create_sparse_grid(float4 *dense_grid, - const size_t width, - const size_t height, - const size_t depth, - vector *sparse_grid, - vector *offsets) -{ - - /* Resize vectors to tiled resolution. */ - int active_tile_count = 0; - int total_tile_count = compute_tile_resolution(width) * - compute_tile_resolution(height) * - compute_tile_resolution(depth); - /* Overalloc grid because we don't know the - * number of active tiles yet. */ - sparse_grid->resize(total_tile_count); - offsets->resize(total_tile_count); - total_tile_count = 0; - - for(int z=0 ; z < depth ; z += TILE_SIZE) { - for(int y=0 ; y < height ; y += TILE_SIZE) { - for(int x=0 ; x < width ; x += TILE_SIZE) { - - SparseTile tile; - bool is_empty = true; - int c = 0; - - /* Populate the tile. */ - for(int k=z ; k < z+TILE_SIZE ; ++k) { - for(int j=y ; j < y+TILE_SIZE ; ++j) { - for(int i=x ; i < x+TILE_SIZE ; ++i) { - int index = compute_index(i, j, k, width, height, depth); - if(index < 0) { - /* Out of bounds of original image, store an empty voxel. */ - tile.values[c] = make_float4(0.0f); - } - else { - tile.values[c] = dense_grid[index]; - if(is_empty) { - if(dense_grid[index].x > 0.0f || - dense_grid[index].y > 0.0f || - dense_grid[index].z > 0.0f || - dense_grid[index].w > 0.0f) { - /* All values are greater than the threshold. */ - is_empty = false; - } - } - } - ++c; - } - } - } - - /* Add tile if active. */ - if(is_empty) { - (*offsets)[total_tile_count] = -1; - } - else { - (*sparse_grid)[active_tile_count] = tile; - (*offsets)[total_tile_count] = active_tile_count; - ++active_tile_count; - } - ++total_tile_count; - } - } - } - - /* Return so that the parent function can resize - * sparse_grid appropriately. */ - return active_tile_count; -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_sparse_grid.h b/intern/cycles/util/util_sparse_grid.h index cf94dcd62b1..910d4f4e1fb 100644 --- a/intern/cycles/util/util_sparse_grid.h +++ b/intern/cycles/util/util_sparse_grid.h @@ -1,5 +1,5 @@ /* - * Copyright 2011-2017 Blender Foundation + * Copyright 2011-2018 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,49 +17,184 @@ #ifndef __UTIL_SPARSE_GRID_H__ #define __UTIL_SPARSE_GRID_H__ +#include "util/util_half.h" +#include "util/util_image.h" #include "util/util_types.h" #include "util/util_vector.h" +/* Functions to help generate and handle Sparse Grids. + * + * In this file, we use two different indexing systems: + * in terms of voxels and in terms of tiles. + * + * The compute_*() functions work with either system, + * so long as all args are in the same format. + * + * For all other functions: + * - x, y, z, width, height, depth are measured by voxels. + * - tix, tiy, tiz, tiw, tih, tid are measured by tiles. + * + */ + CCL_NAMESPACE_BEGIN +/* For some type4 textures (e.g. color), they may still be + * considered active even if at least one of their values + * is beneath the threshold. Thus, we require a custom + * function using ORs instead of ANDs. + */ +namespace { + inline bool gt(float a, float b) { return a > b; } + inline bool gt(uchar a, uchar b) { return a > b; } + inline bool gt(half a, half b) { return a > b; } + inline bool gt(float4 a, float4 b) { return a.x > b.x || a.y > b.y || a.z > b.z || a.w > b.w; } + inline bool gt(uchar4 a, uchar4 b) { return a.x > b.x || a.y > b.y || a.z > b.z || a.w > b.w; } + inline bool gt(half4 a, half4 b) { return a.x > b.x || a.y > b.y || a.z > b.z || a.w > b.w; } +} + static const int TILE_SIZE = 8; +static const float THRESHOLD = 0.001f; -struct SparseTile { - float4 values[TILE_SIZE * TILE_SIZE * TILE_SIZE]; +template struct SparseTile { + T values[TILE_SIZE * TILE_SIZE * TILE_SIZE]; }; -int compute_index(int x, int y, int z, - int width=TILE_SIZE, - int height=TILE_SIZE, - int depth=TILE_SIZE, - int min=0); - -int3 compute_coordinates(int index, - int width=TILE_SIZE, - int height=TILE_SIZE, - int depth=TILE_SIZE, - int min=0); - -int compute_tile_resolution(int res); - -bool is_active(const int *offsets, - size_t x, size_t y, size_t z, - size_t width, size_t height, size_t depth); - -int create_sparse_grid(float4 *dense_grid, - const size_t width, - const size_t height, - const size_t depth, - vector *sparse_grid, - vector *offsets); - -float4 get_value(const SparseTile *grid, const int *offsets, - size_t x, size_t y, size_t z, - size_t width, size_t height, size_t depth); - -float4 get_value(const SparseTile *grid, const int *offsets, - size_t tile_index, - size_t width, size_t height, size_t depth); +const inline int compute_index(const size_t x, const size_t y, const size_t z, + const size_t width, const size_t height, const size_t depth) +{ + if(x >= width || y >= height || z >= depth) { + return -1; + } + return x + width * (y + z * height); +} + +const inline int3 compute_coordinates(const size_t index, const size_t width, + const size_t height, const size_t depth) +{ + if(index >= width * height * depth) { + return make_int3(-1, -1, -1); + } + int x = index % width; + int y = (index / width) % height; + int z = index / (width * height); + return make_int3(x, y, z); +} + +const inline size_t compute_tile_resolution(const size_t res) +{ + return (res / TILE_SIZE) + !(res % TILE_SIZE == 0); +} + +/* Sampling functions accept lookup coordinates in voxel format + * and image resolution in tile format. This is because most + * algorithms will sample one image multiple times, so it is + * more efficient for the parent function itself to convert the + * resolution to the tiled system only once. + */ + +const inline bool tile_is_active(const int *offsets, + int x, int y, int z, + int tiw, int tih, int tid) +{ + int tix = x/TILE_SIZE, tiy = y/TILE_SIZE, tiz = z/TILE_SIZE; + int dense_index = compute_index(tix, tiy, tiz, tiw, tih, tid); + return dense_index < 0 ? false : offsets[dense_index] >= 0; +} + +template +const T get_value(const SparseTile *grid, const int *offsets, + int x, int y, int z, int tiw, int tih, int tid) +{ + /* Get the 1D array index in the dense grid of the tile (x, y, z) is in. */ + int tix = x/TILE_SIZE, tiy = y/TILE_SIZE, tiz = z/TILE_SIZE; + int dense_index = compute_index(tix, tiy, tiz, tiw, tih, tid); + if(dense_index < 0) { + return cast_from_float(0.0f); + } + /* Get the index of the tile in the sparse grid. */ + int sparse_index = offsets[dense_index]; + if (sparse_index < 0) { + return cast_from_float(0.0f); + } + /* Look up voxel in the tile. */ + int in_tile_index = compute_index(x%TILE_SIZE, y%TILE_SIZE, z%TILE_SIZE, + TILE_SIZE, TILE_SIZE, TILE_SIZE); + return grid[sparse_index].values[in_tile_index]; +} + +template +int create_sparse_grid(const T *dense_grid, + int width, int height, int depth, + vector> *sparse_grid, + vector *offsets) +{ + if(!dense_grid) { + return 0; + } + + const T empty = cast_from_float(0.0f); + const T threshold = cast_from_float(THRESHOLD); + int active_tile_count = 0; + int total_tile_count = compute_tile_resolution(width) * + compute_tile_resolution(height) * + compute_tile_resolution(depth); + + /* Resize vectors to tiled resolution. Have to overalloc + * sparse_grid because we don't know the number of + * active tiles yet. */ + sparse_grid->resize(total_tile_count); + offsets->resize(total_tile_count); + total_tile_count = 0; + + for(int z=0 ; z < depth ; z += TILE_SIZE) { + for(int y=0 ; y < height ; y += TILE_SIZE) { + for(int x=0 ; x < width ; x += TILE_SIZE) { + + SparseTile tile; + bool tile_is_empty = true; + int c = 0; + + /* Populate the tile. */ + for(int k=z ; k < z+TILE_SIZE ; ++k) { + for(int j=y ; j < y+TILE_SIZE ; ++j) { + for(int i=x ; i < x+TILE_SIZE ; ++i) { + int index = compute_index(i, j, k, width, height, depth); + if(index < 0) { + /* Out of bounds of original image + * store an empty voxel. */ + tile.values[c] = empty; + } + else { + tile.values[c] = dense_grid[index]; + if(tile_is_empty) { + if(gt(dense_grid[index], threshold)) { + tile_is_empty = false; + } + } + } + ++c; + } + } + } + + /* Add tile if active. */ + if(tile_is_empty) { + (*offsets)[total_tile_count] = -1; + } + else { + (*sparse_grid)[active_tile_count] = tile; + (*offsets)[total_tile_count] = active_tile_count; + ++active_tile_count; + } + ++total_tile_count; + } + } + } + + /* Return so that the parent function can resize + * sparse_grid appropriately. */ + return active_tile_count; +} CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_texture.h b/intern/cycles/util/util_texture.h index a747d783009..a571afcd9a2 100644 --- a/intern/cycles/util/util_texture.h +++ b/intern/cycles/util/util_texture.h @@ -53,7 +53,6 @@ typedef enum ImageDataType { IMAGE_DATA_TYPE_FLOAT = 3, IMAGE_DATA_TYPE_BYTE = 4, IMAGE_DATA_TYPE_HALF = 5, - IMAGE_DATA_TYPE_VOLUME = 6, IMAGE_DATA_NUM_TYPES } ImageDataType; -- cgit v1.2.3