Cycles: Add half precision float support for volumes with NanoVDB
This patch makes it possible to change the precision with which to store volume data in the NanoVDB data structure (as float, half, or using variable bit quantization) via the previously unused precision field in the volume data block. It makes it possible to further reduce memory usage during rendering, at a slight cost to the visual detail of a volume. Differential Revision: https://developer.blender.org/D10023
This commit is contained in:
parent
14a5a91e0e
commit
a8c81ffa83
Notes:
blender-bot
2023-02-14 04:07:50 +01:00
Referenced by issue #81454, Cycles: enable NanoVDB by default
|
@ -219,7 +219,10 @@ static void sync_smoke_volume(
|
|||
|
||||
class BlenderVolumeLoader : public VDBImageLoader {
|
||||
public:
|
||||
BlenderVolumeLoader(BL::BlendData &b_data, BL::Volume &b_volume, const string &grid_name)
|
||||
BlenderVolumeLoader(BL::BlendData &b_data,
|
||||
BL::Volume &b_volume,
|
||||
const string &grid_name,
|
||||
BL::VolumeRender::precision_enum precision_)
|
||||
: VDBImageLoader(grid_name), b_volume(b_volume)
|
||||
{
|
||||
b_volume.grids.load(b_data.ptr.data);
|
||||
|
@ -240,6 +243,20 @@ class BlenderVolumeLoader : public VDBImageLoader {
|
|||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef WITH_NANOVDB
|
||||
switch (precision_) {
|
||||
case BL::VolumeRender::precision_FULL:
|
||||
precision = 32;
|
||||
break;
|
||||
case BL::VolumeRender::precision_HALF:
|
||||
precision = 16;
|
||||
break;
|
||||
default:
|
||||
case BL::VolumeRender::precision_VARIABLE:
|
||||
precision = 0;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -318,7 +335,8 @@ static void sync_volume_object(BL::BlendData &b_data,
|
|||
volume->attributes.add(std) :
|
||||
volume->attributes.add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VOXEL);
|
||||
|
||||
ImageLoader *loader = new BlenderVolumeLoader(b_data, b_volume, name.string());
|
||||
ImageLoader *loader = new BlenderVolumeLoader(
|
||||
b_data, b_volume, name.string(), b_render.precision());
|
||||
ImageParams params;
|
||||
params.frame = b_volume.grids.frame();
|
||||
|
||||
|
|
|
@ -1084,7 +1084,9 @@ void CUDADevice::tex_alloc(device_texture &mem)
|
|||
need_texture_info = true;
|
||||
|
||||
if (mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT &&
|
||||
mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) {
|
||||
mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3 &&
|
||||
mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FPN &&
|
||||
mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FP16) {
|
||||
CUDA_RESOURCE_DESC resDesc;
|
||||
memset(&resDesc, 0, sizeof(resDesc));
|
||||
|
||||
|
|
|
@ -1042,7 +1042,9 @@ void HIPDevice::tex_alloc(device_texture &mem)
|
|||
need_texture_info = true;
|
||||
|
||||
if (mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT &&
|
||||
mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) {
|
||||
mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3 &&
|
||||
mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FPN &&
|
||||
mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FP16) {
|
||||
/* Bindless textures. */
|
||||
hipResourceDesc resDesc;
|
||||
memset(&resDesc, 0, sizeof(resDesc));
|
||||
|
|
|
@ -165,6 +165,8 @@ device_texture::device_texture(Device *device,
|
|||
case IMAGE_DATA_TYPE_BYTE:
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FLOAT:
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FLOAT3:
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FPN:
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FP16:
|
||||
data_type = TYPE_UCHAR;
|
||||
data_elements = 1;
|
||||
break;
|
||||
|
|
|
@ -817,6 +817,14 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals kg,
|
|||
}
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FLOAT3:
|
||||
return NanoVDBInterpolator<nanovdb::Vec3f>::interp_3d(info, P.x, P.y, P.z, interp);
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FPN: {
|
||||
const float f = NanoVDBInterpolator<nanovdb::FpN, float>::interp_3d(info, P.x, P.y, P.z, interp);
|
||||
return make_float4(f, f, f, 1.0f);
|
||||
}
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FP16: {
|
||||
const float f = NanoVDBInterpolator<nanovdb::Fp16, float>::interp_3d(info, P.x, P.y, P.z, interp);
|
||||
return make_float4(f, f, f, 1.0f);
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
assert(0);
|
||||
|
|
|
@ -125,7 +125,8 @@ kernel_tex_image_interp_tricubic(ccl_global const TextureInfo &info, float x, fl
|
|||
|
||||
#ifdef WITH_NANOVDB
|
||||
template<typename T, typename S>
|
||||
ccl_device T kernel_tex_image_interp_tricubic_nanovdb(S &s, float x, float y, float z)
|
||||
ccl_device typename nanovdb::NanoGrid<T>::ValueType kernel_tex_image_interp_tricubic_nanovdb(
|
||||
S &s, float x, float y, float z)
|
||||
{
|
||||
float px = floorf(x);
|
||||
float py = floorf(y);
|
||||
|
@ -157,7 +158,7 @@ ccl_device T kernel_tex_image_interp_tricubic_nanovdb(S &s, float x, float y, fl
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
ccl_device_noinline T kernel_tex_image_interp_nanovdb(
|
||||
ccl_device_noinline typename nanovdb::NanoGrid<T>::ValueType kernel_tex_image_interp_nanovdb(
|
||||
ccl_global const TextureInfo &info, float x, float y, float z, uint interpolation)
|
||||
{
|
||||
using namespace nanovdb;
|
||||
|
@ -238,6 +239,14 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals kg,
|
|||
info, x, y, z, interpolation);
|
||||
return make_float4(f[0], f[1], f[2], 1.0f);
|
||||
}
|
||||
if (texture_type == IMAGE_DATA_TYPE_NANOVDB_FPN) {
|
||||
float f = kernel_tex_image_interp_nanovdb<nanovdb::FpN>(info, x, y, z, interpolation);
|
||||
return make_float4(f, f, f, 1.0f);
|
||||
}
|
||||
if (texture_type == IMAGE_DATA_TYPE_NANOVDB_FP16) {
|
||||
float f = kernel_tex_image_interp_nanovdb<nanovdb::Fp16>(info, x, y, z, interpolation);
|
||||
return make_float4(f, f, f, 1.0f);
|
||||
}
|
||||
#endif
|
||||
if (texture_type == IMAGE_DATA_TYPE_FLOAT4 || texture_type == IMAGE_DATA_TYPE_BYTE4 ||
|
||||
texture_type == IMAGE_DATA_TYPE_HALF4 || texture_type == IMAGE_DATA_TYPE_USHORT4) {
|
||||
|
|
|
@ -64,6 +64,10 @@ const char *name_from_type(ImageDataType type)
|
|||
return "nanovdb_float";
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FLOAT3:
|
||||
return "nanovdb_float3";
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FPN:
|
||||
return "nanovdb_fpn";
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FP16:
|
||||
return "nanovdb_fp16";
|
||||
case IMAGE_DATA_NUM_TYPES:
|
||||
assert(!"System enumerator type, should never be used");
|
||||
return "";
|
||||
|
@ -378,7 +382,9 @@ void ImageManager::load_image_metadata(Image *img)
|
|||
metadata.detect_colorspace();
|
||||
|
||||
assert(features.has_nanovdb || (metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT ||
|
||||
metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3));
|
||||
metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3 ||
|
||||
metadata.type != IMAGE_DATA_TYPE_NANOVDB_FPN ||
|
||||
metadata.type != IMAGE_DATA_TYPE_NANOVDB_FP16));
|
||||
|
||||
img->need_metadata = false;
|
||||
}
|
||||
|
@ -796,7 +802,8 @@ void ImageManager::device_load_image(Device *device, Scene *scene, int slot, Pro
|
|||
}
|
||||
}
|
||||
#ifdef WITH_NANOVDB
|
||||
else if (type == IMAGE_DATA_TYPE_NANOVDB_FLOAT || type == IMAGE_DATA_TYPE_NANOVDB_FLOAT3) {
|
||||
else if (type == IMAGE_DATA_TYPE_NANOVDB_FLOAT || type == IMAGE_DATA_TYPE_NANOVDB_FLOAT3 ||
|
||||
type == IMAGE_DATA_TYPE_NANOVDB_FPN || type == IMAGE_DATA_TYPE_NANOVDB_FP16) {
|
||||
thread_scoped_lock device_lock(device_mutex);
|
||||
void *pixels = img->mem->alloc(img->metadata.byte_size, 0);
|
||||
|
||||
|
|
|
@ -199,6 +199,8 @@ bool OIIOImageLoader::load_pixels(const ImageMetaData &metadata,
|
|||
break;
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FLOAT:
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FLOAT3:
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FPN:
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FP16:
|
||||
case IMAGE_DATA_NUM_TYPES:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -44,14 +44,30 @@ struct ToDenseOp {
|
|||
# ifdef WITH_NANOVDB
|
||||
struct ToNanoOp {
|
||||
nanovdb::GridHandle<> nanogrid;
|
||||
int precision;
|
||||
|
||||
template<typename GridType, typename FloatGridType, typename FloatDataType, int channels>
|
||||
bool operator()(const openvdb::GridBase::ConstPtr &grid)
|
||||
{
|
||||
if constexpr (!std::is_same_v<GridType, openvdb::MaskGrid>) {
|
||||
try {
|
||||
nanogrid = nanovdb::openToNanoVDB(
|
||||
FloatGridType(*openvdb::gridConstPtrCast<GridType>(grid)));
|
||||
FloatGridType floatgrid(*openvdb::gridConstPtrCast<GridType>(grid));
|
||||
if constexpr (std::is_same_v<FloatGridType, openvdb::FloatGrid>) {
|
||||
if (precision == 0) {
|
||||
nanogrid = nanovdb::openToNanoVDB<nanovdb::HostBuffer,
|
||||
typename FloatGridType::TreeType,
|
||||
nanovdb::FpN>(floatgrid);
|
||||
return true;
|
||||
}
|
||||
else if (precision == 16) {
|
||||
nanogrid = nanovdb::openToNanoVDB<nanovdb::HostBuffer,
|
||||
typename FloatGridType::TreeType,
|
||||
nanovdb::Fp16>(floatgrid);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
nanogrid = nanovdb::openToNanoVDB(floatgrid);
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
VLOG(1) << "Error converting OpenVDB to NanoVDB grid: " << e.what();
|
||||
|
@ -102,6 +118,7 @@ bool VDBImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMet
|
|||
openvdb::tools::pruneInactive(pruned_grid.tree());
|
||||
nanogrid = nanovdb::openToNanoVDB(pruned_grid);*/
|
||||
ToNanoOp op;
|
||||
op.precision = precision;
|
||||
if (!openvdb::grid_type_operation(grid, op)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -124,7 +141,15 @@ bool VDBImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMet
|
|||
if (nanogrid) {
|
||||
metadata.byte_size = nanogrid.size();
|
||||
if (metadata.channels == 1) {
|
||||
metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT;
|
||||
if (precision == 0) {
|
||||
metadata.type = IMAGE_DATA_TYPE_NANOVDB_FPN;
|
||||
}
|
||||
else if (precision == 16) {
|
||||
metadata.type = IMAGE_DATA_TYPE_NANOVDB_FP16;
|
||||
}
|
||||
else {
|
||||
metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT;
|
||||
}
|
||||
}
|
||||
else {
|
||||
metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT3;
|
||||
|
|
|
@ -51,6 +51,7 @@ class VDBImageLoader : public ImageLoader {
|
|||
#endif
|
||||
#ifdef WITH_NANOVDB
|
||||
nanovdb::GridHandle<> nanogrid;
|
||||
int precision = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -327,9 +327,11 @@ float Object::compute_volume_step_size() const
|
|||
/* Auto detect step size. */
|
||||
float3 size = one_float3();
|
||||
#ifdef WITH_NANOVDB
|
||||
/* Dimensions were not applied to image transform with NanOVDB (see image_vdb.cpp) */
|
||||
/* Dimensions were not applied to image transform with NanoVDB (see image_vdb.cpp) */
|
||||
if (metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT &&
|
||||
metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3)
|
||||
metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3 &&
|
||||
metadata.type != IMAGE_DATA_TYPE_NANOVDB_FPN &&
|
||||
metadata.type != IMAGE_DATA_TYPE_NANOVDB_FP16)
|
||||
#endif
|
||||
size /= make_float3(metadata.width, metadata.height, metadata.depth);
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@ typedef enum ImageDataType {
|
|||
IMAGE_DATA_TYPE_USHORT = 7,
|
||||
IMAGE_DATA_TYPE_NANOVDB_FLOAT = 8,
|
||||
IMAGE_DATA_TYPE_NANOVDB_FLOAT3 = 9,
|
||||
IMAGE_DATA_TYPE_NANOVDB_FPN = 10,
|
||||
IMAGE_DATA_TYPE_NANOVDB_FP16 = 11,
|
||||
|
||||
IMAGE_DATA_NUM_TYPES
|
||||
} ImageDataType;
|
||||
|
|
|
@ -115,6 +115,9 @@ class DATA_PT_volume_render(DataButtonsPanel, Panel):
|
|||
col = layout.column(align=True)
|
||||
col.prop(render, "clipping")
|
||||
|
||||
col = layout.column()
|
||||
col.prop(render, "precision")
|
||||
|
||||
col = layout.column(align=False)
|
||||
col.prop(volume, "velocity_grid")
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#define _DNA_DEFAULT_VolumeRender \
|
||||
{ \
|
||||
.precision = VOLUME_PRECISION_HALF, \
|
||||
.space = VOLUME_SPACE_OBJECT, \
|
||||
.step_size = 0.0f, \
|
||||
.clipping = 0.001f, \
|
||||
|
|
|
@ -126,6 +126,13 @@ typedef enum VolumeWireframeDetail {
|
|||
VOLUME_WIREFRAME_FINE = 1,
|
||||
} VolumeWireframeDetail;
|
||||
|
||||
/** #VolumeRender.precision */
|
||||
typedef enum VolumeRenderPrecision {
|
||||
VOLUME_PRECISION_HALF = 0,
|
||||
VOLUME_PRECISION_FULL = 1,
|
||||
VOLUME_PRECISION_VARIABLE = 2,
|
||||
} VolumeRenderPrecision;
|
||||
|
||||
/** #VolumeRender.space */
|
||||
typedef enum VolumeRenderSpace {
|
||||
VOLUME_SPACE_OBJECT = 0,
|
||||
|
|
|
@ -485,6 +485,21 @@ static void rna_def_volume_render(BlenderRNA *brna)
|
|||
RNA_def_struct_sdna(srna, "VolumeRender");
|
||||
RNA_def_struct_path_func(srna, "rna_VolumeRender_path");
|
||||
|
||||
static const EnumPropertyItem precision_items[] = {
|
||||
{VOLUME_PRECISION_FULL, "FULL", 0, "Full", "Full float (Use 32 bit for all data)"},
|
||||
{VOLUME_PRECISION_HALF, "HALF", 0, "Half", "Half float (Use 16 bit for all data)"},
|
||||
{VOLUME_PRECISION_VARIABLE, "VARIABLE", 0, "Variable", "Use variable bit quantization"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
prop = RNA_def_property(srna, "precision", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, precision_items);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Precision",
|
||||
"Specify volume data precision. Lower values reduce memory consumption "
|
||||
"at the cost of detail");
|
||||
RNA_def_property_update(prop, 0, "rna_Volume_update_display");
|
||||
|
||||
static const EnumPropertyItem space_items[] = {
|
||||
{VOLUME_SPACE_OBJECT,
|
||||
"OBJECT",
|
||||
|
|
Loading…
Reference in New Issue