Team Ai
Apppublic

CVPR/LIVE

sourceHugging Facegpl-3.0updated 4y agoView on Hugging Face
34likes
scene.cpp1036 linesDownload Raw Back to root
1#include "scene.h"2#include "aabb.h"3#include "cuda_utils.h"4#include "filter.h"5#include "shape.h"6#include <numeric>7#include <algorithm>8#include <cstring>9#include <chrono>10#include <cstddef>11 12size_t align(size_t s) {13    auto a = alignof(std::max_align_t);14    return ((s + a - 1) / a) * a;15}16 17template <typename T>18void allocate(bool use_gpu, T **p) {19    if (use_gpu) {20#ifdef __NVCC__21        checkCuda(cudaMallocManaged(p, sizeof(T)));22#else23        throw std::runtime_error("diffvg not compiled with GPU");24        assert(false);25#endif26    } else {27        *p = (T*)malloc(sizeof(T));28    }29}30 31template <typename T>32void allocate(bool use_gpu, size_t size, T **p) {33    if (use_gpu) {34#ifdef __NVCC__35        checkCuda(cudaMallocManaged(p, size * sizeof(T)));36#else37        throw std::runtime_error("diffvg not compiled with GPU");38        assert(false);39#endif40    } else {41        *p = (T*)malloc(size * sizeof(T));42    }43}44 45void copy_and_init_shapes(Scene &scene,46                          const std::vector<const Shape *> &shape_list) {47    for (int shape_id = 0; shape_id < scene.num_shapes; shape_id++) {48        switch (shape_list[shape_id]->type) {49            case ShapeType::Circle: {50                Circle *p = (Circle *)scene.shapes[shape_id].ptr;51                const Circle *p_ = (const Circle*)(shape_list[shape_id]->ptr);52                *p = *p_;53                Circle *d_p = (Circle *)scene.d_shapes[shape_id].ptr;54                d_p->radius = 0;55                d_p->center = Vector2f{0, 0};56                break;57            } case ShapeType::Ellipse: {58                Ellipse *p = (Ellipse *)scene.shapes[shape_id].ptr;59                const Ellipse *p_ = (const Ellipse*)(shape_list[shape_id]->ptr);60                *p = *p_;61                Ellipse *d_p = (Ellipse *)scene.d_shapes[shape_id].ptr;62                d_p->radius = Vector2f{0, 0};63                d_p->center = Vector2f{0, 0};64                break;65            } case ShapeType::Path: {66                Path *p = (Path *)scene.shapes[shape_id].ptr;67                const Path *p_ = (const Path*)(shape_list[shape_id]->ptr);68                p->num_points = p_->num_points;69                p->num_base_points = p_->num_base_points;70                for (int i = 0; i < p_->num_base_points; i++) {71                    p->num_control_points[i] = p_->num_control_points[i];72                }73                for (int i = 0; i < 2 * p_->num_points; i++) {74                    p->points[i] = p_->points[i];75                }76                p->is_closed = p_->is_closed;77                p->use_distance_approx = p_->use_distance_approx;78                Path *d_p = (Path *)scene.d_shapes[shape_id].ptr;79                d_p->num_points = p_->num_points;80                d_p->num_base_points = p_->num_base_points;81                for (int i = 0; i < 2 * p_->num_points; i++) {82                    d_p->points[i] = 0;83                }84                d_p->is_closed = p_->is_closed;85                if (p_->thickness != nullptr) {86                    for (int i = 0; i < p_->num_points; i++) {87                        p->thickness[i] = p_->thickness[i];88                        d_p->thickness[i] = 0;89                    }90                }91                d_p->use_distance_approx = p_->use_distance_approx;92                break;93            } case ShapeType::Rect: {94                Rect *p = (Rect *)scene.shapes[shape_id].ptr;95                const Rect *p_ = (const Rect*)(shape_list[shape_id]->ptr);96                *p = *p_;97                Rect *d_p = (Rect *)scene.d_shapes[shape_id].ptr;98                d_p->p_min = Vector2f{0, 0};99                d_p->p_max = Vector2f{0, 0};100                break;101            } default: {102                assert(false);103                break;104            }105        }106        scene.shapes[shape_id].type = shape_list[shape_id]->type;107        scene.shapes[shape_id].stroke_width = shape_list[shape_id]->stroke_width;108        scene.d_shapes[shape_id].type = shape_list[shape_id]->type;109        scene.d_shapes[shape_id].stroke_width = 0;110    }111}112 113std::vector<float>114compute_shape_length(const std::vector<const Shape *> &shape_list) {115    int num_shapes = (int)shape_list.size();116    std::vector<float> shape_length_list(num_shapes, 0.f);117    for (int shape_id = 0; shape_id < num_shapes; shape_id++) {118        auto shape_length = 0.f;119        switch (shape_list[shape_id]->type) {120            case ShapeType::Circle: {121                const Circle *p_ = (const Circle*)(shape_list[shape_id]->ptr);122                shape_length += float(2.f * M_PI) * p_->radius;123                break;124            } case ShapeType::Ellipse: {125                const Ellipse *p_ = (const Ellipse*)(shape_list[shape_id]->ptr);126                // https://en.wikipedia.org/wiki/Ellipse#Circumference127                // Ramanujan's ellipse circumference approximation128                auto a = p_->radius.x;129                auto b = p_->radius.y;130                shape_length += float(M_PI) * (3 * (a + b) - sqrt((3 * a + b) * (a + 3 * b)));131                break;132            } case ShapeType::Path: {133                const Path *p_ = (const Path*)(shape_list[shape_id]->ptr);134                auto length = 0.f;135                auto point_id = 0;136                for (int i = 0; i < p_->num_base_points; i++) {137                    if (p_->num_control_points[i] == 0) {138                        // Straight line139                        auto i0 = point_id;140                        assert(i0 < p_->num_points);141                        auto i1 = (i0 + 1) % p_->num_points;142                        point_id += 1;143                        auto p0 = Vector2f{p_->points[2 * i0], p_->points[2 * i0 + 1]};144                        auto p1 = Vector2f{p_->points[2 * i1], p_->points[2 * i1 + 1]};145                        length += distance(p1, p0);146                    } else if (p_->num_control_points[i] == 1) {147                        // Quadratic Bezier curve148                        auto i0 = point_id;149                        auto i1 = i0 + 1;150                        auto i2 = (i0 + 2) % p_->num_points;151                        point_id += 2;152                        auto p0 = Vector2f{p_->points[2 * i0], p_->points[2 * i0 + 1]};153                        auto p1 = Vector2f{p_->points[2 * i1], p_->points[2 * i1 + 1]};154                        auto p2 = Vector2f{p_->points[2 * i2], p_->points[2 * i2 + 1]};155                        auto eval = [&](float t) -> Vector2f {156                            auto tt = 1 - t;157                            return (tt*tt)*p0 + (2*tt*t)*p1 + (t*t)*p2;158                        };159                        // We use 3-point samples to approximate the length160                        auto v0 = p0;161                        auto v1 = eval(0.5f);162                        auto v2 = p2;163                        length += distance(v1, v0) + distance(v1, v2);164                    } else if (p_->num_control_points[i] == 2) {165                        // Cubic Bezier curve166                        auto i0 = point_id;167                        auto i1 = i0 + 1;168                        auto i2 = i0 + 2;169                        auto i3 = (i0 + 3) % p_->num_points;170                        point_id += 3;171                        auto p0 = Vector2f{p_->points[2 * i0], p_->points[2 * i0 + 1]};172                        auto p1 = Vector2f{p_->points[2 * i1], p_->points[2 * i1 + 1]};173                        auto p2 = Vector2f{p_->points[2 * i2], p_->points[2 * i2 + 1]};174                        auto p3 = Vector2f{p_->points[2 * i3], p_->points[2 * i3 + 1]};175                        auto eval = [&](float t) -> Vector2f {176                            auto tt = 1 - t;177                            return (tt*tt*tt)*p0 + (3*tt*tt*t)*p1 + (3*tt*t*t)*p2 + (t*t*t)*p3;178                        };179                        // We use 4-point samples to approximate the length180                        auto v0 = p0;181                        auto v1 = eval(1.f/3.f);182                        auto v2 = eval(2.f/3.f);183                        auto v3 = p3;184                        length += distance(v1, v0) + distance(v1, v2) + distance(v2, v3);185                    } else {186                        assert(false);187                    }188                }189                assert(isfinite(length));190                shape_length += length;191                break;192            } case ShapeType::Rect: {193                const Rect *p_ = (const Rect*)(shape_list[shape_id]->ptr);194                shape_length += 2 * (p_->p_max.x - p_->p_min.x + p_->p_max.y - p_->p_min.y);195                break;196            } default: {197                assert(false);198                break;199            }200        }201        assert(isfinite(shape_length));202        shape_length_list[shape_id] = shape_length;203    }204    return shape_length_list;205}206 207void build_shape_cdfs(Scene &scene,208                      const std::vector<const ShapeGroup *> &shape_group_list,209                      const std::vector<float> &shape_length_list) {210    int sample_id = 0;211    for (int shape_group_id = 0; shape_group_id < (int)shape_group_list.size(); shape_group_id++) {212        const ShapeGroup *shape_group = shape_group_list[shape_group_id];213        for (int i = 0; i < shape_group->num_shapes; i++) {214            int shape_id = shape_group->shape_ids[i];215            float length = shape_length_list[shape_id];216            scene.sample_shape_id[sample_id] = shape_id;217            if (sample_id == 0) {218                scene.sample_shapes_cdf[sample_id] = length;219            } else {220                scene.sample_shapes_cdf[sample_id] = length +221                    scene.sample_shapes_cdf[sample_id - 1];222            }223            assert(isfinite(length));224            scene.sample_shapes_pmf[sample_id] = length;225            scene.sample_group_id[sample_id] = shape_group_id;226            sample_id++;227        }228    }229    assert(sample_id == scene.num_total_shapes);230    auto normalization = scene.sample_shapes_cdf[scene.num_total_shapes - 1];231    if (normalization <= 0) {232        char buf[256];233        sprintf(buf, "The total length of the shape boundaries in the scene is equal or less than 0. Length = %f", normalization);234        throw std::runtime_error(buf);235    }236    if (!isfinite(normalization)) {237        char buf[256];238        sprintf(buf, "The total length of the shape boundaries in the scene is not a number. Length = %f", normalization);239        throw std::runtime_error(buf);240    }241    assert(normalization > 0);242    for (int sample_id = 0; sample_id < scene.num_total_shapes; sample_id++) {243        scene.sample_shapes_cdf[sample_id] /= normalization;244        scene.sample_shapes_pmf[sample_id] /= normalization;245    }246}247 248void build_path_cdfs(Scene &scene,249                     const std::vector<const Shape *> &shape_list,250                     const std::vector<float> &shape_length_list) {251    for (int shape_id = 0; shape_id < scene.num_shapes; shape_id++) {252        if (shape_list[shape_id]->type == ShapeType::Path) {253            const Path &path = shape_list[shape_id]->as_path();254            float *pmf = scene.path_length_pmf[shape_id];255            float *cdf = scene.path_length_cdf[shape_id];256            int *point_id_map = scene.path_point_id_map[shape_id];257            auto path_length = shape_length_list[shape_id];258            auto inv_length = 1.f / path_length;259            auto point_id = 0;260            for (int i = 0; i < path.num_base_points; i++) {261                point_id_map[i] = point_id;262                if (path.num_control_points[i] == 0) {263                    // Straight line264                    auto i0 = point_id;265                    auto i1 = (i0 + 1) % path.num_points;266                    point_id += 1;267                    auto p0 = Vector2f{path.points[2 * i0], path.points[2 * i0 + 1]};268                    auto p1 = Vector2f{path.points[2 * i1], path.points[2 * i1 + 1]};269                    auto d = distance(p0, p1) * inv_length;270                    pmf[i] = d;271                    if (i == 0) {272                        cdf[i] = d;273                    } else {274                        cdf[i] = d + cdf[i - 1];275                    }276                } else if (path.num_control_points[i] == 1) {277                    // Quadratic Bezier curve278                    auto i0 = point_id;279                    auto i1 = i0 + 1;280                    auto i2 = (i0 + 2) % path.num_points;281                    point_id += 2;282                    auto p0 = Vector2f{path.points[2 * i0], path.points[2 * i0 + 1]};283                    auto p1 = Vector2f{path.points[2 * i1], path.points[2 * i1 + 1]};284                    auto p2 = Vector2f{path.points[2 * i2], path.points[2 * i2 + 1]};285                    auto eval = [&](float t) -> Vector2f {286                        auto tt = 1 - t;287                        return (tt*tt)*p0 + (2*tt*t)*p1 + (t*t)*p2;288                    };289                    // We use 3-point samples to approximate the length290                    auto v0 = p0;291                    auto v1 = eval(0.5f);292                    auto v2 = p2;293                    auto d = (distance(v0, v1) + distance(v1, v2)) * inv_length;294                    pmf[i] = d;295                    if (i == 0) {296                        cdf[i] = d;297                    } else {298                        cdf[i] = d + cdf[i - 1];299                    }300                } else if (path.num_control_points[i] == 2) {301                    // Cubic Bezier curve302                    auto i0 = point_id;303                    auto i1 = point_id + 1;304                    auto i2 = point_id + 2;305                    auto i3 = (point_id + 3) % path.num_points;306                    point_id += 3;307                    auto p0 = Vector2f{path.points[2 * i0], path.points[2 * i0 + 1]};308                    auto p1 = Vector2f{path.points[2 * i1], path.points[2 * i1 + 1]};309                    auto p2 = Vector2f{path.points[2 * i2], path.points[2 * i2 + 1]};310                    auto p3 = Vector2f{path.points[2 * i3], path.points[2 * i3 + 1]};311                    auto eval = [&](float t) -> Vector2f {312                        auto tt = 1 - t;313                        return (tt*tt*tt)*p0 + (3*tt*tt*t)*p1 + (3*tt*t*t)*p2 + (t*t*t)*p3;314                    };315                    // We use 4-point samples to approximate the length316                    auto v0 = p0;317                    auto v1 = eval(1.f/3.f);318                    auto v2 = eval(2.f/3.f);319                    auto v3 = p3;320                    auto d = (distance(v1, v0) + distance(v1, v2) + distance(v2, v3)) * inv_length;321                    pmf[i] = d;322                    if (i == 0) {323                        cdf[i] = d;324                    } else {325                        cdf[i] = d + cdf[i - 1];326                    }327                } else {328                    assert(false);329                }330            }331        }332    }333}334 335void copy_and_init_shape_groups(Scene &scene,336                                const std::vector<const ShapeGroup *> &shape_group_list) {337    for (int group_id = 0; group_id < scene.num_shape_groups; group_id++) {338        const ShapeGroup *shape_group = shape_group_list[group_id];339        auto copy_and_init_color = [&](const ColorType &color_type, void *color_ptr, void *target_ptr, void *d_target_ptr) {340            switch (color_type) {341                case ColorType::Constant: {342                    Constant *c = (Constant*)target_ptr;343                    Constant *d_c = (Constant*)d_target_ptr;344                    const Constant *c_ = (const Constant*)color_ptr;345                    *c = *c_;346                    d_c->color = Vector4{0, 0, 0, 0};347                    break;348                } case ColorType::LinearGradient: {349                    LinearGradient *c = (LinearGradient*)target_ptr;350                    LinearGradient *d_c = (LinearGradient*)d_target_ptr;351                    const LinearGradient *c_ = (const LinearGradient*)color_ptr;352                    c->begin = c_->begin;353                    c->end = c_->end;354                    c->num_stops = c_->num_stops;355                    for (int i = 0; i < c_->num_stops; i++) {356                        c->stop_offsets[i] = c_->stop_offsets[i];357                    }358                    for (int i = 0; i < 4 * c_->num_stops; i++) {359                        c->stop_colors[i] = c_->stop_colors[i];360                    }361                    d_c->begin = Vector2f{0, 0};362                    d_c->end = Vector2f{0, 0};363                    d_c->num_stops = c_->num_stops;364                    for (int i = 0; i < c_->num_stops; i++) {365                        d_c->stop_offsets[i] = 0;366                    }367                    for (int i = 0; i < 4 * c_->num_stops; i++) {368                        d_c->stop_colors[i] = 0;369                    }370                    break;371                } case ColorType::RadialGradient: {372                    RadialGradient *c = (RadialGradient*)target_ptr;373                    RadialGradient *d_c = (RadialGradient*)d_target_ptr;374                    const RadialGradient *c_ = (const RadialGradient*)color_ptr;375                    c->center = c_->center;376                    c->radius = c_->radius;377                    c->num_stops = c_->num_stops;378                    for (int i = 0; i < c_->num_stops; i++) {379                        c->stop_offsets[i] = c_->stop_offsets[i];380                    }381                    for (int i = 0; i < 4 * c_->num_stops; i++) {382                        c->stop_colors[i] = c_->stop_colors[i];383                    }384                    d_c->center = Vector2f{0, 0};385                    d_c->radius = Vector2f{0, 0};386                    d_c->num_stops = c_->num_stops;387                    for (int i = 0; i < c_->num_stops; i++) {388                        d_c->stop_offsets[i] = 0;389                    }390                    for (int i = 0; i < 4 * c_->num_stops; i++) {391                        d_c->stop_colors[i] = 0;392                    }393                    break;394                } default: {395                    assert(false);396                }397            }398        };399        for (int i = 0; i < shape_group->num_shapes; i++) {400            scene.shape_groups[group_id].shape_ids[i] = shape_group->shape_ids[i];401        }402        scene.shape_groups[group_id].num_shapes = shape_group->num_shapes;403        scene.shape_groups[group_id].use_even_odd_rule = shape_group->use_even_odd_rule;404        scene.shape_groups[group_id].canvas_to_shape = shape_group->canvas_to_shape;405        scene.shape_groups[group_id].shape_to_canvas = shape_group->shape_to_canvas;406        scene.d_shape_groups[group_id].shape_ids = nullptr;407        scene.d_shape_groups[group_id].num_shapes = shape_group->num_shapes;408        scene.d_shape_groups[group_id].use_even_odd_rule = shape_group->use_even_odd_rule;409        scene.d_shape_groups[group_id].canvas_to_shape = Matrix3x3f{};410        scene.d_shape_groups[group_id].shape_to_canvas = Matrix3x3f{};411 412        scene.shape_groups[group_id].fill_color_type = shape_group->fill_color_type;413        scene.d_shape_groups[group_id].fill_color_type = shape_group->fill_color_type;414        if (shape_group->fill_color != nullptr) {415            copy_and_init_color(shape_group->fill_color_type,416                                shape_group->fill_color,417                                scene.shape_groups[group_id].fill_color,418                                scene.d_shape_groups[group_id].fill_color);419        }420        scene.shape_groups[group_id].stroke_color_type = shape_group->stroke_color_type;421        scene.d_shape_groups[group_id].stroke_color_type = shape_group->stroke_color_type;422        if (shape_group->stroke_color != nullptr) {423            copy_and_init_color(shape_group->stroke_color_type,424                                shape_group->stroke_color,425                                scene.shape_groups[group_id].stroke_color,426                                scene.d_shape_groups[group_id].stroke_color);427        }428    }429}430 431DEVICE uint32_t morton2D(const Vector2f &p, int canvas_width, int canvas_height) {432    auto scene_bounds = Vector2f{canvas_width, canvas_height};433    auto pp = p / scene_bounds;434    TVector2<uint32_t> pp_i{pp.x * 1023, pp.y * 1023};435    return (expand_bits(pp_i.x) << 1u) |436           (expand_bits(pp_i.y) << 0u);437}438 439template <bool sort>440void build_bvh(const Scene &scene, BVHNode *nodes, int num_primitives) {441    auto bvh_size = 2 * num_primitives - 1;442    if (bvh_size > 1) {443        if (sort) {444            // Sort by Morton code445            std::sort(nodes, nodes + num_primitives,446                [&] (const BVHNode &n0, const BVHNode &n1) {447                    auto p0 = 0.5f * (n0.box.p_min + n0.box.p_max);448                    auto p1 = 0.5f * (n1.box.p_min + n1.box.p_max);449                    auto m0 = morton2D(p0, scene.canvas_width, scene.canvas_height);450                    auto m1 = morton2D(p1, scene.canvas_width, scene.canvas_height);451                    return m0 < m1;452            });453        }454        for (int i = num_primitives; i < bvh_size; i++) {455            nodes[i] = BVHNode{-1, -1, AABB{}, 0.f};456        }457        int prev_beg = 0;458        int prev_end = num_primitives;459        // For handling odd number of nodes at a level460        int leftover = prev_end % 2 == 0 ? -1 : prev_end - 1;461        while (prev_end - prev_beg >= 1 || leftover != -1) {462            int length = (prev_end - prev_beg) / 2;463            if ((prev_end - prev_beg) % 2 == 1 && leftover != -1 &&464                    leftover != prev_end - 1) {465                length += 1;466            }467            for (int i = 0; i < length; i++) {468                BVHNode node;469                node.child0 = prev_beg + 2 * i;470                node.child1 = prev_beg + 2 * i + 1;471                if (node.child1 >= prev_end) {472                    assert(leftover != -1);473                    node.child1 = leftover;474                    leftover = -1;475                }476                AABB child0_box = nodes[node.child0].box;477                AABB child1_box = nodes[node.child1].box;478                node.box = merge(child0_box, child1_box);479                node.max_radius = std::max(nodes[node.child0].max_radius,480                                           nodes[node.child1].max_radius);481                nodes[prev_end + i] = node;482            }483            if (length == 1 && leftover == -1) {484                break;485            }486            prev_beg = prev_end;487            prev_end = prev_beg + length;488            if (length % 2 == 1 && leftover == -1) {489                leftover = prev_end - 1;490            }491        }492    }493    assert(nodes[2 * num_primitives - 2].child0 != -1);494}495 496void compute_bounding_boxes(Scene &scene,497                            const std::vector<const Shape *> &shape_list,498                            const std::vector<const ShapeGroup *> &shape_group_list) {499    for (int shape_id = 0; shape_id < scene.num_shapes; shape_id++) {500        switch (shape_list[shape_id]->type) {501            case ShapeType::Circle: {502                const Circle *p = (const Circle*)(shape_list[shape_id]->ptr);503                scene.shapes_bbox[shape_id] = AABB{p->center - p->radius,504                                                   p->center + p->radius};505                break;506            } case ShapeType::Ellipse: {507                const Ellipse *p = (const Ellipse*)(shape_list[shape_id]->ptr);508                scene.shapes_bbox[shape_id] = AABB{p->center - p->radius,509                                                   p->center + p->radius};510                break;511            } case ShapeType::Path: {512                const Path *p = (const Path*)(shape_list[shape_id]->ptr);513                AABB box;514                if (p->num_points > 0) {515                    box = AABB{Vector2f{p->points[0], p->points[1]},516                               Vector2f{p->points[0], p->points[1]}};517                }518                for (int i = 1; i < p->num_points; i++) {519                    box = merge(box, Vector2f{p->points[2 * i], p->points[2 * i + 1]});520                }521                scene.shapes_bbox[shape_id] = box;522                std::vector<AABB> boxes(p->num_base_points);523                std::vector<float> thickness(p->num_base_points);524                std::vector<int> first_point_id(p->num_base_points);525                auto r = shape_list[shape_id]->stroke_width;526                auto point_id = 0;527                for (int i = 0; i < p->num_base_points; i++) {528                    first_point_id[i] = point_id;529                    if (p->num_control_points[i] == 0) {530                        // Straight line531                        auto i0 = point_id;532                        auto i1 = (i0 + 1) % p->num_points;533                        point_id += 1;534                        auto p0 = Vector2f{p->points[2 * i0], p->points[2 * i0 + 1]};535                        auto p1 = Vector2f{p->points[2 * i1], p->points[2 * i1 + 1]};536                        boxes[i] = AABB();537                        boxes[i] = merge(boxes[i], p0);538                        boxes[i] = merge(boxes[i], p1);539                        auto r0 = r;540                        auto r1 = r;541                        // override radius if path has thickness542                        if (p->thickness != nullptr) {543                            r0 = p->thickness[i0];544                            r1 = p->thickness[i1];545                        }546                        thickness[i] = max(r0, r1);547                    } else if (p->num_control_points[i] == 1) {548                        // Quadratic Bezier curve549                        auto i0 = point_id;550                        auto i1 = i0 + 1;551                        auto i2 = (i0 + 2) % p->num_points;552                        point_id += 2;553                        auto p0 = Vector2f{p->points[2 * i0], p->points[2 * i0 + 1]};554                        auto p1 = Vector2f{p->points[2 * i1], p->points[2 * i1 + 1]};555                        auto p2 = Vector2f{p->points[2 * i2], p->points[2 * i2 + 1]};556                        boxes[i] = AABB();557                        boxes[i] = merge(boxes[i], p0);558                        boxes[i] = merge(boxes[i], p1);559                        boxes[i] = merge(boxes[i], p2);560                        auto r0 = r;561                        auto r1 = r;562                        auto r2 = r;563                        // override radius if path has thickness564                        if (p->thickness != nullptr) {565                            r0 = p->thickness[i0];566                            r1 = p->thickness[i1];567                            r2 = p->thickness[i2];568                        }569                        thickness[i] = max(max(r0, r1), r2);570                    } else if (p->num_control_points[i] == 2) {571                        // Cubic Bezier curve572                        auto i0 = point_id;573                        auto i1 = i0 + 1;574                        auto i2 = i0 + 2;575                        auto i3 = (i0 + 3) % p->num_points;576                        point_id += 3;577                        auto p0 = Vector2f{p->points[2 * i0], p->points[2 * i0 + 1]};578                        auto p1 = Vector2f{p->points[2 * i1], p->points[2 * i1 + 1]};579                        auto p2 = Vector2f{p->points[2 * i2], p->points[2 * i2 + 1]};580                        auto p3 = Vector2f{p->points[2 * i3], p->points[2 * i3 + 1]};581                        boxes[i] = AABB();582                        boxes[i] = merge(boxes[i], p0);583                        boxes[i] = merge(boxes[i], p1);584                        boxes[i] = merge(boxes[i], p2);585                        boxes[i] = merge(boxes[i], p3);586                        auto r0 = r;587                        auto r1 = r;588                        auto r2 = r;589                        auto r3 = r;590                        // override radius if path has thickness591                        if (p->thickness != nullptr) {592                            r0 = p->thickness[i0];593                            r1 = p->thickness[i1];594                            r2 = p->thickness[i2];595                            r3 = p->thickness[i3];596                        }597                        thickness[i] = max(max(max(r0, r1), r2), r3);598                    } else {599                        assert(false);600                    }601                }602                // Sort the boxes by y603                std::vector<int> idx(boxes.size());604                std::iota(idx.begin(), idx.end(), 0);605                std::sort(idx.begin(), idx.end(), [&](int i0, int i1) {606                    const AABB &b0 = boxes[i0];607                    const AABB &b1 = boxes[i1];608                    auto b0y = 0.5f * (b0.p_min.y + b0.p_max.y);609                    auto b1y = 0.5f * (b1.p_min.y + b1.p_max.y);610                    return b0y < b1y;611                });612                BVHNode *nodes = scene.path_bvhs[shape_id];613                for (int i = 0; i < (int)idx.size(); i++) {614                    nodes[i] = BVHNode{idx[i],615                                       -(first_point_id[idx[i]]+1),616                                       boxes[idx[i]],617                                       thickness[idx[i]]};618                }619                build_bvh<false /*sort*/>(scene, nodes, boxes.size());620                break;621            } case ShapeType::Rect: {622                const Rect *p = (const Rect*)(shape_list[shape_id]->ptr);623                scene.shapes_bbox[shape_id] = AABB{p->p_min, p->p_max};624                break;625            } default: {626                assert(false);627                break;628            }629        }630    }631    632    for (int shape_group_id = 0; shape_group_id < (int)shape_group_list.size(); shape_group_id++) {633        const ShapeGroup *shape_group = shape_group_list[shape_group_id];634        // Build a BVH for each shape group635        BVHNode *nodes = scene.shape_groups_bvh_nodes[shape_group_id];636        for (int i = 0; i < shape_group->num_shapes; i++) {637            auto shape_id = shape_group->shape_ids[i];638            auto r = shape_group->stroke_color == nullptr ? 0 : shape_list[shape_id]->stroke_width;639            nodes[i] = BVHNode{shape_id,640                               -1,641                               scene.shapes_bbox[shape_id],642                               r};643        }644        build_bvh<true /*sort*/>(scene, nodes, shape_group->num_shapes);645    }646 647    BVHNode *nodes = scene.bvh_nodes;648    for (int shape_group_id = 0; shape_group_id < (int)shape_group_list.size(); shape_group_id++) {649        const ShapeGroup *shape_group = shape_group_list[shape_group_id];650        auto max_radius = shape_list[shape_group->shape_ids[0]]->stroke_width;651        if (shape_list[shape_group->shape_ids[0]]->type == ShapeType::Path) {652            const Path *p = (const Path*)(shape_list[shape_group->shape_ids[0]]->ptr);653            if (p->thickness != nullptr) {654                const BVHNode *nodes = scene.path_bvhs[shape_group->shape_ids[0]];655                max_radius = nodes[0].max_radius;656            }657        }658        for (int i = 1; i < shape_group->num_shapes; i++) {659            auto shape_id = shape_group->shape_ids[i];660            auto shape = shape_list[shape_id];661            auto r = shape->stroke_width;662            if (shape->type == ShapeType::Path) {663                const Path *p = (const Path*)(shape_list[shape_id]->ptr);664                if (p->thickness != nullptr) {665                    const BVHNode *nodes = scene.path_bvhs[shape_id];666                    r = nodes[0].max_radius;667                }668            }669            max_radius = std::max(max_radius, r);670        }671        // Fetch group bbox from BVH672        auto bbox = scene.shape_groups_bvh_nodes[shape_group_id][2 * shape_group->num_shapes - 2].box;673        // Transform box from local to world space674        nodes[shape_group_id].child0 = shape_group_id;675        nodes[shape_group_id].child1 = -1;676        nodes[shape_group_id].box = transform(shape_group->shape_to_canvas, bbox);677        if (shape_group->stroke_color == nullptr) {678            nodes[shape_group_id].max_radius = 0;679        } else {680            nodes[shape_group_id].max_radius = max_radius;681        }682    }683    build_bvh<true /*sort*/>(scene, nodes, shape_group_list.size());684}685 686template <bool alloc_mode>687size_t allocate_buffers(Scene &scene,688                        const std::vector<const Shape *> &shape_list,689                        const std::vector<const ShapeGroup *> &shape_group_list) {690    auto num_shapes = shape_list.size();691    auto num_shape_groups = shape_group_list.size();692 693    size_t buffer_size = 0;694    if (alloc_mode) scene.shapes = (Shape*)&scene.buffer[buffer_size];695    buffer_size += align(sizeof(Shape) * num_shapes);696    if (alloc_mode) scene.d_shapes = (Shape*)&scene.buffer[buffer_size];697    buffer_size += align(sizeof(Shape) * num_shapes); 698    if (alloc_mode) scene.shape_groups = (ShapeGroup*)&scene.buffer[buffer_size];699    buffer_size += align(sizeof(ShapeGroup) * num_shape_groups);700    if (alloc_mode) scene.d_shape_groups = (ShapeGroup*)&scene.buffer[buffer_size];701    buffer_size += align(sizeof(ShapeGroup) * num_shape_groups);702    if (alloc_mode) scene.sample_shapes_cdf = (float*)&scene.buffer[buffer_size];703    buffer_size += align(sizeof(float) * scene.num_total_shapes);704    if (alloc_mode) scene.sample_shapes_pmf = (float*)&scene.buffer[buffer_size];705    buffer_size += align(sizeof(float) * scene.num_total_shapes);706    if (alloc_mode) scene.sample_shape_id = (int*)&scene.buffer[buffer_size];707    buffer_size += align(sizeof(int) * scene.num_total_shapes);708    if (alloc_mode) scene.sample_group_id = (int*)&scene.buffer[buffer_size];709    buffer_size += align(sizeof(int) * scene.num_total_shapes);710    if (alloc_mode) scene.shapes_length = (float*)&scene.buffer[buffer_size];711    buffer_size += align(sizeof(float) * num_shapes);712    if (alloc_mode) scene.path_length_cdf = (float**)&scene.buffer[buffer_size];713    buffer_size += align(sizeof(float*) * num_shapes);714    if (alloc_mode) scene.path_length_pmf = (float**)&scene.buffer[buffer_size];715    buffer_size += align(sizeof(float*) * num_shapes);716    if (alloc_mode) scene.path_point_id_map = (int**)&scene.buffer[buffer_size];717    buffer_size += align(sizeof(int*) * num_shapes);718    if (alloc_mode) scene.filter = (Filter*)&scene.buffer[buffer_size];719    buffer_size += align(sizeof(Filter));720    if (alloc_mode) scene.d_filter = (DFilter*)&scene.buffer[buffer_size];721    buffer_size += align(sizeof(DFilter));722    if (alloc_mode) scene.shapes_bbox = (AABB*)&scene.buffer[buffer_size];723    buffer_size += align(sizeof(AABB) * num_shapes);724    if (alloc_mode) scene.path_bvhs = (BVHNode**)&scene.buffer[buffer_size];725    buffer_size += align(sizeof(BVHNode*) * num_shapes);726    if (alloc_mode) scene.shape_groups_bvh_nodes = (BVHNode**)&scene.buffer[buffer_size];727    buffer_size += align(sizeof(BVHNode*) * num_shape_groups);728    if (alloc_mode) scene.bvh_nodes = (BVHNode*)&scene.buffer[buffer_size];729    buffer_size += align(sizeof(BVHNode) * (2 * num_shape_groups - 1));730 731    if (alloc_mode) {732        for (int i = 0; i < num_shapes; i++) {733            scene.path_length_cdf[i] = nullptr;734            scene.path_length_pmf[i] = nullptr;735            scene.path_point_id_map[i] = nullptr;736            scene.path_bvhs[i] = nullptr;737        }738    }739 740    for (int shape_id = 0; shape_id < scene.num_shapes; shape_id++) {741        switch (shape_list[shape_id]->type) {742            case ShapeType::Circle: {743                if (alloc_mode) scene.shapes[shape_id].ptr = (Circle*)&scene.buffer[buffer_size];744                buffer_size += align(sizeof(Circle)); // scene.shapes[shape_id].ptr745                if (alloc_mode) scene.d_shapes[shape_id].ptr = (Circle*)&scene.buffer[buffer_size];746                buffer_size += align(sizeof(Circle)); // scene.d_shapes[shape_id].ptr747                break;748            } case ShapeType::Ellipse: {749                if (alloc_mode) scene.shapes[shape_id].ptr = (Ellipse*)&scene.buffer[buffer_size];750                buffer_size += align(sizeof(Ellipse)); // scene.shapes[shape_id].ptr751                if (alloc_mode) scene.d_shapes[shape_id].ptr = (Ellipse*)&scene.buffer[buffer_size];752                buffer_size += align(sizeof(Ellipse)); // scene.d_shapes[shape_id].ptr753                break;754            } case ShapeType::Path: {755                if (alloc_mode) scene.shapes[shape_id].ptr = (Path*)&scene.buffer[buffer_size];756                buffer_size += align(sizeof(Path)); // scene.shapes[shape_id].ptr757                if (alloc_mode) scene.d_shapes[shape_id].ptr = (Path*)&scene.buffer[buffer_size];758                buffer_size += align(sizeof(Path)); // scene.d_shapes[shape_id].ptr759 760                const Path *p_ = (const Path*)(shape_list[shape_id]->ptr);761                Path *p = nullptr, *d_p = nullptr;762                if (alloc_mode) p = (Path*)scene.shapes[shape_id].ptr;763                if (alloc_mode) d_p = (Path*)scene.d_shapes[shape_id].ptr; 764                if (alloc_mode) p->num_control_points = (int*)&scene.buffer[buffer_size];765                buffer_size += align(sizeof(int) * p_->num_base_points); // p->num_control_points766                if (alloc_mode) p->points = (float*)&scene.buffer[buffer_size];767                buffer_size += align(sizeof(float) * (2 * p_->num_points)); // p->points768                if (alloc_mode) d_p->points = (float*)&scene.buffer[buffer_size];769                buffer_size += align(sizeof(float) * (2 * p_->num_points)); // d_p->points770                if (p_->thickness != nullptr) {771                    if (alloc_mode) p->thickness = (float*)&scene.buffer[buffer_size];772                    buffer_size += align(sizeof(float) * p_->num_points); // p->thickness773                    if (alloc_mode) d_p->thickness = (float*)&scene.buffer[buffer_size];774                    buffer_size += align(sizeof(float) * p_->num_points); // d_p->thickness775                } else {776                    if (alloc_mode) p->thickness = nullptr;777                    if (alloc_mode) d_p->thickness = nullptr;778                }779                if (alloc_mode) scene.path_length_pmf[shape_id] = (float*)&scene.buffer[buffer_size];780                buffer_size += align(sizeof(float) * p_->num_base_points); // scene.path_length_pmf781                if (alloc_mode) scene.path_length_cdf[shape_id] = (float*)&scene.buffer[buffer_size];782                buffer_size += align(sizeof(float) * p_->num_base_points); // scene.path_length_cdf783                if (alloc_mode) scene.path_point_id_map[shape_id] = (int*)&scene.buffer[buffer_size];784                buffer_size += align(sizeof(int) * p_->num_base_points); // scene.path_point_id_map785                if (alloc_mode) scene.path_bvhs[shape_id] = (BVHNode*)&scene.buffer[buffer_size];786                buffer_size += align(sizeof(BVHNode) * (2 * p_->num_base_points - 1));787                break;788            } case ShapeType::Rect: {789                if (alloc_mode) scene.shapes[shape_id].ptr = (Ellipse*)&scene.buffer[buffer_size];790                buffer_size += align(sizeof(Rect)); // scene.shapes[shape_id].ptr791                if (alloc_mode) scene.d_shapes[shape_id].ptr = (Ellipse*)&scene.buffer[buffer_size];792                buffer_size += align(sizeof(Rect)); // scene.d_shapes[shape_id].ptr793                break;794            } default: {795                assert(false);796                break;797            }798        }799    }800 801    for (int group_id = 0; group_id < scene.num_shape_groups; group_id++) {802        const ShapeGroup *shape_group = shape_group_list[group_id];803        if (shape_group->fill_color != nullptr) {804            switch (shape_group->fill_color_type) {805                case ColorType::Constant: {806                    if (alloc_mode) scene.shape_groups[group_id].fill_color = (Constant*)&scene.buffer[buffer_size];807                    buffer_size += align(sizeof(Constant)); // color808                    if (alloc_mode) scene.d_shape_groups[group_id].fill_color = (Constant*)&scene.buffer[buffer_size];809                    buffer_size += align(sizeof(Constant)); // d_color810                    break;811                } case ColorType::LinearGradient: {812                    if (alloc_mode) scene.shape_groups[group_id].fill_color = (LinearGradient*)&scene.buffer[buffer_size];813                    buffer_size += align(sizeof(LinearGradient)); // color814                    if (alloc_mode) scene.d_shape_groups[group_id].fill_color = (LinearGradient*)&scene.buffer[buffer_size];815                    buffer_size += align(sizeof(LinearGradient)); // d_color816 817                    const LinearGradient *c_ = (const LinearGradient *)shape_group->fill_color;818                    LinearGradient *c = nullptr, *d_c = nullptr;819                    if (alloc_mode) c = (LinearGradient *)scene.shape_groups[group_id].fill_color;820                    if (alloc_mode) d_c = (LinearGradient *)scene.d_shape_groups[group_id].fill_color;821                    if (alloc_mode) c->stop_offsets = (float*)&scene.buffer[buffer_size];822                    buffer_size += align(sizeof(float) * c_->num_stops); // c->stop_offsets823                    if (alloc_mode) c->stop_colors = (float*)&scene.buffer[buffer_size];824                    buffer_size += align(sizeof(float) * 4 * c_->num_stops); // c->stop_colors825                    if (alloc_mode) d_c->stop_offsets = (float*)&scene.buffer[buffer_size];826                    buffer_size += align(sizeof(float) * c_->num_stops); // d_c->stop_offsets827                    if (alloc_mode) d_c->stop_colors = (float*)&scene.buffer[buffer_size];828                    buffer_size += align(sizeof(float) * 4 * c_->num_stops); // d_c->stop_colors829                    break;830                } case ColorType::RadialGradient: {831                    if (alloc_mode) scene.shape_groups[group_id].fill_color = (RadialGradient*)&scene.buffer[buffer_size];832                    buffer_size += align(sizeof(RadialGradient)); // color833                    if (alloc_mode) scene.d_shape_groups[group_id].fill_color = (RadialGradient*)&scene.buffer[buffer_size];834                    buffer_size += align(sizeof(RadialGradient)); // d_color835 836                    const RadialGradient *c_ = (const RadialGradient *)shape_group->fill_color;837                    RadialGradient *c = nullptr, *d_c = nullptr;838                    if (alloc_mode) c = (RadialGradient *)scene.shape_groups[group_id].fill_color;839                    if (alloc_mode) d_c = (RadialGradient *)scene.d_shape_groups[group_id].fill_color;840                    if (alloc_mode) c->stop_offsets = (float*)&scene.buffer[buffer_size];841                    buffer_size += align(sizeof(float) * c_->num_stops); // c->stop_offsets842                    if (alloc_mode) c->stop_colors = (float*)&scene.buffer[buffer_size];843                    buffer_size += align(sizeof(float) * 4 * c_->num_stops); // c->stop_colors844                    if (alloc_mode) d_c->stop_offsets = (float*)&scene.buffer[buffer_size];845                    buffer_size += align(sizeof(float) * c_->num_stops); // d_c->stop_offsets846                    if (alloc_mode) d_c->stop_colors = (float*)&scene.buffer[buffer_size];847                    buffer_size += align(sizeof(float) * 4 * c_->num_stops); // d_c->stop_colors848                    break;849                } default: {850                    assert(false);851                }852            }853        } else {854            if (alloc_mode) scene.shape_groups[group_id].fill_color = nullptr;855            if (alloc_mode) scene.d_shape_groups[group_id].fill_color = nullptr;856        }857        if (shape_group->stroke_color != nullptr) {858            switch (shape_group->stroke_color_type) {859                case ColorType::Constant: {860                    if (alloc_mode) scene.shape_groups[group_id].stroke_color = (Constant*)&scene.buffer[buffer_size];861                    buffer_size += align(sizeof(Constant)); // color862                    if (alloc_mode) scene.d_shape_groups[group_id].stroke_color = (Constant*)&scene.buffer[buffer_size];863                    buffer_size += align(sizeof(Constant)); // d_color864                    break;865                } case ColorType::LinearGradient: {866                    if (alloc_mode) scene.shape_groups[group_id].stroke_color = (LinearGradient*)&scene.buffer[buffer_size];867                    buffer_size += align(sizeof(LinearGradient)); // color868                    if (alloc_mode) scene.shape_groups[group_id].stroke_color = (LinearGradient*)&scene.buffer[buffer_size];869                    buffer_size += align(sizeof(LinearGradient)); // d_color870 871                    const LinearGradient *c_ = (const LinearGradient *)shape_group->stroke_color;872                    LinearGradient *c = nullptr, *d_c = nullptr;873                    if (alloc_mode) c = (LinearGradient *)scene.shape_groups[group_id].stroke_color;874                    if (alloc_mode) d_c = (LinearGradient *)scene.d_shape_groups[group_id].stroke_color;875                    if (alloc_mode) c->stop_offsets = (float*)&scene.buffer[buffer_size];876                    buffer_size += align(sizeof(float) * c_->num_stops); // c->stop_offsets877                    if (alloc_mode) c->stop_colors = (float*)&scene.buffer[buffer_size];878                    buffer_size += align(sizeof(float) * 4 * c_->num_stops); // c->stop_colors879                    if (alloc_mode) d_c->stop_offsets = (float*)&scene.buffer[buffer_size];880                    buffer_size += align(sizeof(float) * c_->num_stops); // d_c->stop_offsets881                    if (alloc_mode) d_c->stop_colors = (float*)&scene.buffer[buffer_size];882                    buffer_size += align(sizeof(float) * 4 * c_->num_stops); // d_c->stop_colors883                    break;884                } case ColorType::RadialGradient: {885                    if (alloc_mode) scene.shape_groups[group_id].stroke_color = (RadialGradient*)&scene.buffer[buffer_size];886                    buffer_size += align(sizeof(RadialGradient)); // color887                    if (alloc_mode) scene.shape_groups[group_id].stroke_color = (RadialGradient*)&scene.buffer[buffer_size];888                    buffer_size += align(sizeof(RadialGradient)); // d_color889 890                    const RadialGradient *c_ = (const RadialGradient *)shape_group->stroke_color;891                    RadialGradient *c = nullptr, *d_c = nullptr;892                    if (alloc_mode) c = (RadialGradient *)scene.shape_groups[group_id].stroke_color;893                    if (alloc_mode) d_c = (RadialGradient *)scene.d_shape_groups[group_id].stroke_color;894                    if (alloc_mode) c->stop_offsets = (float*)&scene.buffer[buffer_size];895                    buffer_size += align(sizeof(float) * c_->num_stops); // c->stop_offsets896                    if (alloc_mode) c->stop_colors = (float*)&scene.buffer[buffer_size];897                    buffer_size += align(sizeof(float) * 4 * c_->num_stops); // c->stop_colors898                    if (alloc_mode) d_c->stop_offsets = (float*)&scene.buffer[buffer_size];899                    buffer_size += align(sizeof(float) * c_->num_stops); // d_c->stop_offsets900                    if (alloc_mode) d_c->stop_colors = (float*)&scene.buffer[buffer_size];901                    buffer_size += align(sizeof(float) * 4 * c_->num_stops); // d_c->stop_colors902                    break;903                } default: {904                    assert(false);905                }906            }907        } else {908            if (alloc_mode) scene.shape_groups[group_id].stroke_color = nullptr;909            if (alloc_mode) scene.d_shape_groups[group_id].stroke_color = nullptr;910        }911        if (alloc_mode) scene.shape_groups[group_id].shape_ids = (int*)&scene.buffer[buffer_size];912        buffer_size += align(sizeof(int) * shape_group->num_shapes); // shape_group->shape_ids913        if (alloc_mode) scene.shape_groups_bvh_nodes[group_id] = (BVHNode*)&scene.buffer[buffer_size];914        buffer_size += align(sizeof(BVHNode) * (2 * shape_group->num_shapes - 1)); // scene.shape_groups_bvh_nodes[group_id]915    }916    return buffer_size;917}918 919Scene::Scene(int canvas_width,920             int canvas_height,921             const std::vector<const Shape *> &shape_list,922             const std::vector<const ShapeGroup *> &shape_group_list,923             const Filter &filter,924             bool use_gpu,925             int gpu_index)926    : canvas_width(canvas_width),927      canvas_height(canvas_height),928      num_shapes(shape_list.size()),929      num_shape_groups(shape_group_list.size()),930      use_gpu(use_gpu),931      gpu_index(gpu_index) {932    if (num_shapes == 0) {933        return;934    }935    // Shape group may reuse some of the shapes,936    // record the total number of shapes.937    int num_total_shapes = 0;938    for (const ShapeGroup *sg : shape_group_list) {939        num_total_shapes += sg->num_shapes;940    }941    this->num_total_shapes = num_total_shapes;942 943    // Memory initialization944#ifdef __NVCC__945    int old_device_id = -1;946#endif947    if (use_gpu) {948#ifdef __NVCC__949        checkCuda(cudaGetDevice(&old_device_id));950        if (gpu_index != -1) {951            checkCuda(cudaSetDevice(gpu_index));952        }953#else954        throw std::runtime_error("diffvg not compiled with GPU");955        assert(false);956#endif957    }958 959    size_t buffer_size = allocate_buffers<false /*alloc_mode*/>(*this, shape_list, shape_group_list);960    // Allocate a huge buffer for everything961    allocate<uint8_t>(use_gpu, buffer_size, &buffer);962    // memset(buffer, 111, buffer_size);963    // Actually distribute the buffer964    allocate_buffers<true /*alloc_mode*/>(*this, shape_list, shape_group_list);965    copy_and_init_shapes(*this, shape_list);966    copy_and_init_shape_groups(*this, shape_group_list);967 968    std::vector<float> shape_length_list = compute_shape_length(shape_list);969    // Copy shape_length970    if (use_gpu) {971#ifdef __NVCC__972        checkCuda(cudaMemcpy(this->shapes_length, &shape_length_list[0], num_shapes * sizeof(float), cudaMemcpyHostToDevice));973#else974        throw std::runtime_error("diffvg not compiled with GPU");975        assert(false);976#endif977    } else {978        memcpy(this->shapes_length, &shape_length_list[0], num_shapes * sizeof(float));979    }980    build_shape_cdfs(*this, shape_group_list, shape_length_list);981    build_path_cdfs(*this, shape_list, shape_length_list);982    compute_bounding_boxes(*this, shape_list, shape_group_list);983 984    // Filter initialization985    *(this->filter) = filter;986    this->d_filter->radius = 0;987 988    if (use_gpu) {989#ifdef __NVCC__990        if (old_device_id != -1) {991            checkCuda(cudaSetDevice(old_device_id));992        }993#else994        throw std::runtime_error("diffvg not compiled with GPU");995        assert(false);996#endif997    }998}999 1000Scene::~Scene() {1001    if (num_shapes == 0) {1002        return;1003    }1004    if (use_gpu) {1005#ifdef __NVCC__1006        int old_device_id = -1;1007        checkCuda(cudaGetDevice(&old_device_id));1008        if (gpu_index != -1) {1009            checkCuda(cudaSetDevice(gpu_index));1010        }1011 1012        checkCuda(cudaFree(buffer));1013 1014        checkCuda(cudaSetDevice(old_device_id));1015#else1016        // Don't throw because C++ don't want a destructor to throw.1017        std::cerr << "diffvg not compiled with GPU";1018        exit(1);1019#endif1020    } else {1021        free(buffer);1022    }1023}1024 1025Shape Scene::get_d_shape(int shape_id) const {1026    return d_shapes[shape_id];1027}1028 1029ShapeGroup Scene::get_d_shape_group(int group_id) const {1030    return d_shape_groups[group_id];1031}1032 1033float Scene::get_d_filter_radius() const {1034    return d_filter->radius;1035}1036