Files
test/intern/cycles/scene/pointcloud.cpp
William Leeson 6c03339e48 Cycles: reduce mesh memory usage by unflattening
To improve mesh upload speeds and reduce the size of the scene data which allows larger scenes to be rendered.

The meshes in Cycles are currently stored as flattened meshes, where each triangle is stored as a set of 3 vertices. Unflattening writes out the vertices in a list according to the index buffer. This uses a lot of memory and for current hardware does not provide a noticeable benefit. This change unflattens the mesh by directly using the meshes vertex and index buffers directly and skips the unflattening. This change allows for larger scenes and also a reduction in the sizes of the meshes. Further it results in a decrease the amount of time it takes to upload the data to a GPU. This is especially important for when multiple GPUs are used in a single machine.

Pull Request #105173
2023-02-27 10:39:19 +01:00

298 lines
8.2 KiB
C++

/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#include "bvh/bvh.h"
#include "scene/pointcloud.h"
#include "scene/scene.h"
CCL_NAMESPACE_BEGIN
/* PointCloud Point */
void PointCloud::Point::bounds_grow(const float3 *points,
const float *radius,
BoundBox &bounds) const
{
bounds.grow(points[index], radius[index]);
}
void PointCloud::Point::bounds_grow(const float3 *points,
const float *radius,
const Transform &aligned_space,
BoundBox &bounds) const
{
float3 P = transform_point(&aligned_space, points[index]);
bounds.grow(P, radius[index]);
}
void PointCloud::Point::bounds_grow(const float4 &point, BoundBox &bounds) const
{
bounds.grow(float4_to_float3(point), point.w);
}
float4 PointCloud::Point::motion_key(const float3 *points,
const float *radius,
const float3 *point_steps,
size_t num_points,
size_t num_steps,
float time,
size_t p) const
{
/* Figure out which steps we need to fetch and their
* interpolation factor. */
const size_t max_step = num_steps - 1;
const size_t step = min((size_t)(time * max_step), max_step - 1);
const float t = time * max_step - step;
/* Fetch vertex coordinates. */
const float4 curr_key = point_for_step(
points, radius, point_steps, num_points, num_steps, step, p);
const float4 next_key = point_for_step(
points, radius, point_steps, num_points, num_steps, step + 1, p);
/* Interpolate between steps. */
return (1.0f - t) * curr_key + t * next_key;
}
float4 PointCloud::Point::point_for_step(const float3 *points,
const float *radius,
const float3 *point_steps,
size_t num_points,
size_t num_steps,
size_t step,
size_t p) const
{
const size_t center_step = ((num_steps - 1) / 2);
if (step == center_step) {
/* Center step: regular key location. */
return make_float4(points[p].x, points[p].y, points[p].z, radius[p]);
}
else {
/* Center step is not stored in this array. */
if (step > center_step) {
step--;
}
const size_t offset = step * num_points;
return make_float4(point_steps[offset + p].x,
point_steps[offset + p].y,
point_steps[offset + p].z,
radius[offset + p]);
}
}
/* PointCloud */
NODE_DEFINE(PointCloud)
{
NodeType *type = NodeType::add(
"pointcloud", create, NodeType::NONE, Geometry::get_node_base_type());
SOCKET_POINT_ARRAY(points, "Points", array<float3>());
SOCKET_FLOAT_ARRAY(radius, "Radius", array<float>());
SOCKET_INT_ARRAY(shader, "Shader", array<int>());
return type;
}
PointCloud::PointCloud() : Geometry(node_type, Geometry::POINTCLOUD)
{
}
PointCloud::~PointCloud()
{
}
void PointCloud::resize(int numpoints)
{
points.resize(numpoints);
radius.resize(numpoints);
shader.resize(numpoints);
attributes.resize();
tag_points_modified();
tag_radius_modified();
tag_shader_modified();
}
void PointCloud::reserve(int numpoints)
{
points.reserve(numpoints);
radius.reserve(numpoints);
shader.reserve(numpoints);
attributes.resize(true);
}
void PointCloud::clear(const bool preserve_shaders)
{
Geometry::clear(preserve_shaders);
points.clear();
radius.clear();
shader.clear();
attributes.clear();
tag_points_modified();
tag_radius_modified();
tag_shader_modified();
}
void PointCloud::add_point(float3 co, float r, int shader_index)
{
points.push_back_reserved(co);
radius.push_back_reserved(r);
shader.push_back_reserved(shader_index);
tag_points_modified();
tag_radius_modified();
tag_shader_modified();
}
void PointCloud::copy_center_to_motion_step(const int motion_step)
{
Attribute *attr_mP = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr_mP) {
float3 *points_data = points.data();
size_t numpoints = points.size();
float *radius_data = radius.data();
float4 *attrib_P = attr_mP->data_float4() + motion_step * numpoints;
for (int i = 0; i < numpoints; i++) {
float3 P = points_data[i];
float r = radius_data[i];
attrib_P[i] = make_float4(P.x, P.y, P.z, r);
}
}
}
void PointCloud::get_uv_tiles(ustring map, unordered_set<int> &tiles)
{
Attribute *attr;
if (map.empty()) {
attr = attributes.find(ATTR_STD_UV);
}
else {
attr = attributes.find(map);
}
if (attr) {
attr->get_uv_tiles(this, ATTR_PRIM_GEOMETRY, tiles);
}
}
void PointCloud::compute_bounds()
{
BoundBox bnds = BoundBox::empty;
size_t numpoints = points.size();
if (numpoints > 0) {
for (size_t i = 0; i < numpoints; i++) {
bnds.grow(points[i], radius[i]);
}
Attribute *attr = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (use_motion_blur && attr) {
size_t steps_size = points.size() * (motion_steps - 1);
float3 *point_steps = attr->data_float3();
for (size_t i = 0; i < steps_size; i++)
bnds.grow(point_steps[i]);
}
if (!bnds.valid()) {
bnds = BoundBox::empty;
/* skip nan or inf coordinates */
for (size_t i = 0; i < numpoints; i++)
bnds.grow_safe(points[i], radius[i]);
if (use_motion_blur && attr) {
size_t steps_size = points.size() * (motion_steps - 1);
float3 *point_steps = attr->data_float3();
for (size_t i = 0; i < steps_size; i++)
bnds.grow_safe(point_steps[i]);
}
}
}
if (!bnds.valid()) {
/* empty mesh */
bnds.grow(make_float3(0.0f, 0.0f, 0.0f));
}
bounds = bnds;
}
void PointCloud::apply_transform(const Transform &tfm, const bool apply_to_motion)
{
/* compute uniform scale */
float3 c0 = transform_get_column(&tfm, 0);
float3 c1 = transform_get_column(&tfm, 1);
float3 c2 = transform_get_column(&tfm, 2);
float scalar = powf(fabsf(dot(cross(c0, c1), c2)), 1.0f / 3.0f);
/* apply transform to curve keys */
for (size_t i = 0; i < points.size(); i++) {
float3 co = transform_point(&tfm, points[i]);
float r = radius[i] * scalar;
/* scale for curve radius is only correct for uniform scale
*/
points[i] = co;
radius[i] = r;
}
if (apply_to_motion) {
Attribute *attr = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr) {
/* apply transform to motion curve keys */
size_t steps_size = points.size() * (motion_steps - 1);
float4 *point_steps = attr->data_float4();
for (size_t i = 0; i < steps_size; i++) {
float3 co = transform_point(&tfm, float4_to_float3(point_steps[i]));
float radius = point_steps[i].w * scalar;
/* scale for curve radius is only correct for uniform
* scale */
point_steps[i] = float3_to_float4(co);
point_steps[i].w = radius;
}
}
}
}
void PointCloud::pack(Scene *scene, float4 *packed_points, uint *packed_shader)
{
size_t numpoints = points.size();
float3 *points_data = points.data();
float *radius_data = radius.data();
int *shader_data = shader.data();
for (size_t i = 0; i < numpoints; i++) {
packed_points[i] = make_float4(
points_data[i].x, points_data[i].y, points_data[i].z, radius_data[i]);
}
uint shader_id = 0;
uint last_shader = -1;
for (size_t i = 0; i < numpoints; i++) {
if (last_shader != shader_data[i]) {
last_shader = shader_data[i];
Shader *shader = (last_shader < used_shaders.size()) ?
static_cast<Shader *>(used_shaders[last_shader]) :
scene->default_surface;
shader_id = scene->shader_manager->get_shader_id(shader);
}
packed_shader[i] = shader_id;
}
}
PrimitiveType PointCloud::primitive_type() const
{
return has_motion_blur() ? PRIMITIVE_MOTION_POINT : PRIMITIVE_POINT;
}
CCL_NAMESPACE_END