Cycles: Implement selective nodes compilation
authorSergey Sharybin <sergey.vfx@gmail.com>
Mon, 1 Jun 2015 12:48:45 +0000 (17:48 +0500)
committerSergey Sharybin <sergey.vfx@gmail.com>
Mon, 1 Jun 2015 14:49:52 +0000 (19:49 +0500)
This commits finishes initial selective nodes compilation into kernel, which
helps a lot performance-wise for AMD OpenCL kernels.

Split by node groups is based on statistics from simple scenes like BMW and
more complex scenes like mango and gooseberry production files. Further
tweaks are always possible, but it should be a good starting point.

TODO: Still need to ignore unused nodes when calculating requested shader
features.

intern/cycles/kernel/svm/svm.h
intern/cycles/kernel/svm/svm_types.h
intern/cycles/render/nodes.h

index 16847cc98ffdb4ebb69eb07bb365a04a85d93bed..42a581cbd9a96f298c1a6706fb33ace098f7f3af 100644 (file)
@@ -212,17 +212,6 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, Shade
                        case NODE_CLOSURE_BACKGROUND:
                                svm_node_closure_background(sd, stack, node);
                                break;
-                       case NODE_CLOSURE_HOLDOUT:
-                               svm_node_closure_holdout(sd, stack, node);
-                               break;
-                       case NODE_CLOSURE_AMBIENT_OCCLUSION:
-                               svm_node_closure_ambient_occlusion(sd, stack, node);
-                               break;
-#if NODES_FEATURE(NODE_FEATURE_VOLUME)
-                       case NODE_CLOSURE_VOLUME:
-                               svm_node_closure_volume(kg, sd, stack, node, path_flag);
-                               break;
-#endif  /* NODES_FEATURE(NODE_FEATURE_VOLUME) */
                        case NODE_CLOSURE_SET_WEIGHT:
                                svm_node_closure_set_weight(sd, node.y, node.z, node.w);
                                break;
@@ -235,7 +224,6 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, Shade
                        case NODE_MIX_CLOSURE:
                                svm_node_mix_closure(sd, stack, node);
                                break;
-#endif  /* NODES_GROUP(NODE_GROUP_LEVEL_0) */
                        case NODE_JUMP_IF_ZERO:
                                if(stack_load_float(stack, node.z) == 0.0f)
                                        offset += node.y;
@@ -244,13 +232,137 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, Shade
                                if(stack_load_float(stack, node.z) == 1.0f)
                                        offset += node.y;
                                break;
-#ifdef __TEXTURES__
+                       case NODE_GEOMETRY:
+                               svm_node_geometry(kg, sd, stack, node.y, node.z);
+                               break;
+                       case NODE_CONVERT:
+                               svm_node_convert(sd, stack, node.y, node.z, node.w);
+                               break;
+                       case NODE_TEX_COORD:
+                               svm_node_tex_coord(kg, sd, path_flag, stack, node, &offset);
+                               break;
+                       case NODE_VALUE_F:
+                               svm_node_value_f(kg, sd, stack, node.y, node.z);
+                               break;
+                       case NODE_VALUE_V:
+                               svm_node_value_v(kg, sd, stack, node.y, &offset);
+                               break;
+                       case NODE_ATTR:
+                               svm_node_attr(kg, sd, stack, node);
+                               break;
+#  if NODES_FEATURE(NODE_FEATURE_BUMP)
+                       case NODE_GEOMETRY_BUMP_DX:
+                               svm_node_geometry_bump_dx(kg, sd, stack, node.y, node.z);
+                               break;
+                       case NODE_GEOMETRY_BUMP_DY:
+                               svm_node_geometry_bump_dy(kg, sd, stack, node.y, node.z);
+                               break;
+                       case NODE_SET_DISPLACEMENT:
+                               svm_node_set_displacement(sd, stack, node.y);
+                               break;
+#  endif  /* NODES_FEATURE(NODE_FEATURE_BUMP) */
+#  ifdef __TEXTURES__
                        case NODE_TEX_IMAGE:
                                svm_node_tex_image(kg, sd, stack, node);
                                break;
                        case NODE_TEX_IMAGE_BOX:
                                svm_node_tex_image_box(kg, sd, stack, node);
                                break;
+                       case NODE_TEX_NOISE:
+                               svm_node_tex_noise(kg, sd, stack, node, &offset);
+                               break;
+#  endif  /* __TEXTURES__ */
+#  ifdef __EXTRA_NODES__
+#    if NODES_FEATURE(NODE_FEATURE_BUMP)
+                       case NODE_SET_BUMP:
+                               svm_node_set_bump(kg, sd, stack, node);
+                               break;
+                       case NODE_ATTR_BUMP_DX:
+                               svm_node_attr_bump_dx(kg, sd, stack, node);
+                               break;
+                       case NODE_ATTR_BUMP_DY:
+                               svm_node_attr_bump_dy(kg, sd, stack, node);
+                               break;
+                       case NODE_TEX_COORD_BUMP_DX:
+                               svm_node_tex_coord_bump_dx(kg, sd, path_flag, stack, node, &offset);
+                               break;
+                       case NODE_TEX_COORD_BUMP_DY:
+                               svm_node_tex_coord_bump_dy(kg, sd, path_flag, stack, node, &offset);
+                               break;
+                       case NODE_CLOSURE_SET_NORMAL:
+                               svm_node_set_normal(kg, sd, stack, node.y, node.z);
+                               break;
+#    endif  /* NODES_FEATURE(NODE_FEATURE_BUMP) */
+                       case NODE_HSV:
+                               svm_node_hsv(kg, sd, stack, node.y, node.z, node.w, &offset);
+                               break;
+#  endif  /* __EXTRA_NODES__ */
+#endif  /* NODES_GROUP(NODE_GROUP_LEVEL_0) */
+
+#if NODES_GROUP(NODE_GROUP_LEVEL_1)
+                       case NODE_CLOSURE_HOLDOUT:
+                               svm_node_closure_holdout(sd, stack, node);
+                               break;
+                       case NODE_CLOSURE_AMBIENT_OCCLUSION:
+                               svm_node_closure_ambient_occlusion(sd, stack, node);
+                               break;
+                       case NODE_FRESNEL:
+                               svm_node_fresnel(sd, stack, node.y, node.z, node.w);
+                               break;
+                       case NODE_LAYER_WEIGHT:
+                               svm_node_layer_weight(sd, stack, node);
+                               break;
+#  if NODES_FEATURE(NODE_FEATURE_VOLUME)
+                       case NODE_CLOSURE_VOLUME:
+                               svm_node_closure_volume(kg, sd, stack, node, path_flag);
+                               break;
+#  endif  /* NODES_FEATURE(NODE_FEATURE_VOLUME) */
+#  ifdef __EXTRA_NODES__
+                       case NODE_MATH:
+                               svm_node_math(kg, sd, stack, node.y, node.z, node.w, &offset);
+                               break;
+                       case NODE_VECTOR_MATH:
+                               svm_node_vector_math(kg, sd, stack, node.y, node.z, node.w, &offset);
+                               break;
+                       case NODE_RGB_RAMP:
+                               svm_node_rgb_ramp(kg, sd, stack, node, &offset);
+                               break;
+                       case NODE_GAMMA:
+                               svm_node_gamma(sd, stack, node.y, node.z, node.w);
+                               break;
+                       case NODE_BRIGHTCONTRAST:
+                               svm_node_brightness(sd, stack, node.y, node.z, node.w);
+                               break;
+                       case NODE_LIGHT_PATH:
+                               svm_node_light_path(sd, stack, node.y, node.z, path_flag);
+                               break;
+                       case NODE_OBJECT_INFO:
+                               svm_node_object_info(kg, sd, stack, node.y, node.z);
+                               break;
+                       case NODE_PARTICLE_INFO:
+                               svm_node_particle_info(kg, sd, stack, node.y, node.z);
+                               break;
+#    ifdef __HAIR__
+#      if NODES_FEATURE(NODE_FEATURE_HAIR)
+                       case NODE_HAIR_INFO:
+                               svm_node_hair_info(kg, sd, stack, node.y, node.z);
+                               break;
+#      endif  /* NODES_FEATURE(NODE_FEATURE_HAIR) */
+#    endif  /* __HAIR__ */
+#  endif  /* __EXTRA_NODES__ */
+#endif  /* NODES_GROUP(NODE_GROUP_LEVEL_1) */
+
+#if NODES_GROUP(NODE_GROUP_LEVEL_2)
+                       case NODE_MAPPING:
+                               svm_node_mapping(kg, sd, stack, node.y, node.z, &offset);
+                               break;
+                       case NODE_MIN_MAX:
+                               svm_node_min_max(kg, sd, stack, node.y, node.z, &offset);
+                               break;
+                       case NODE_CAMERA:
+                               svm_node_camera(kg, sd, stack, node.y, node.z, node.w);
+                               break;
+#  ifdef __TEXTURES__
                        case NODE_TEX_ENVIRONMENT:
                                svm_node_tex_environment(kg, sd, stack, node);
                                break;
@@ -260,9 +372,6 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, Shade
                        case NODE_TEX_GRADIENT:
                                svm_node_tex_gradient(sd, stack, node);
                                break;
-                       case NODE_TEX_NOISE:
-                               svm_node_tex_noise(kg, sd, stack, node, &offset);
-                               break;
                        case NODE_TEX_VORONOI:
                                svm_node_tex_voronoi(kg, sd, stack, node, &offset);
                                break;
@@ -281,57 +390,34 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, Shade
                        case NODE_TEX_BRICK:
                                svm_node_tex_brick(kg, sd, stack, node, &offset);
                                break;
-#endif  /* __TEXTURES__ */
-                       case NODE_CAMERA:
-                               svm_node_camera(kg, sd, stack, node.y, node.z, node.w);
-                               break;
-                       case NODE_GEOMETRY:
-                               svm_node_geometry(kg, sd, stack, node.y, node.z);
-                               break;
-#ifdef __EXTRA_NODES__
-                       case NODE_GEOMETRY_BUMP_DX:
-                               svm_node_geometry_bump_dx(kg, sd, stack, node.y, node.z);
-                               break;
-                       case NODE_GEOMETRY_BUMP_DY:
-                               svm_node_geometry_bump_dy(kg, sd, stack, node.y, node.z);
-                               break;
-                       case NODE_LIGHT_PATH:
-                               svm_node_light_path(sd, stack, node.y, node.z, path_flag);
-                               break;
-                       case NODE_OBJECT_INFO:
-                               svm_node_object_info(kg, sd, stack, node.y, node.z);
-                               break;
-                       case NODE_PARTICLE_INFO:
-                               svm_node_particle_info(kg, sd, stack, node.y, node.z);
+#  endif  /* __TEXTURES__ */
+#  ifdef __EXTRA_NODES__
+                       case NODE_NORMAL:
+                               svm_node_normal(kg, sd, stack, node.y, node.z, node.w, &offset);
                                break;
-#ifdef __HAIR__
-#  if NODES_FEATURE(NODE_FEATURE_HAIR)
-                       case NODE_HAIR_INFO:
-                               svm_node_hair_info(kg, sd, stack, node.y, node.z);
+                       case NODE_LIGHT_FALLOFF:
+                               svm_node_light_falloff(sd, stack, node);
                                break;
-#  endif  /* NODES_FEATURE(NODE_FEATURE_HAIR) */
-#endif  /* __HAIR__ */
+#  endif  /* __EXTRA_NODES__ */
+#endif  /* NODES_GROUP(NODE_GROUP_LEVEL_2) */
 
-#endif  /* __EXTRA_NODES__ */
-                       case NODE_CONVERT:
-                               svm_node_convert(sd, stack, node.y, node.z, node.w);
+#if NODES_GROUP(NODE_GROUP_LEVEL_3)
+                       case NODE_RGB_CURVES:
+                               svm_node_rgb_curves(kg, sd, stack, node, &offset);
                                break;
-                       case NODE_VALUE_F:
-                               svm_node_value_f(kg, sd, stack, node.y, node.z);
+                       case NODE_VECTOR_CURVES:
+                               svm_node_vector_curves(kg, sd, stack, node, &offset);
                                break;
-                       case NODE_VALUE_V:
-                               svm_node_value_v(kg, sd, stack, node.y, &offset);
+                       case NODE_TANGENT:
+                               svm_node_tangent(kg, sd, stack, node);
                                break;
-#ifdef __EXTRA_NODES__
+                       case NODE_NORMAL_MAP:
+                               svm_node_normal_map(kg, sd, stack, node);
+                               break;
+#  ifdef __EXTRA_NODES__
                        case NODE_INVERT:
                                svm_node_invert(sd, stack, node.y, node.z, node.w);
                                break;
-                       case NODE_GAMMA:
-                               svm_node_gamma(sd, stack, node.y, node.z, node.w);
-                               break;
-                       case NODE_BRIGHTCONTRAST:
-                               svm_node_brightness(sd, stack, node.y, node.z, node.w);
-                               break;
                        case NODE_MIX:
                                svm_node_mix(kg, sd, stack, node.y, node.z, node.w, &offset);
                                break;
@@ -347,28 +433,9 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, Shade
                        case NODE_COMBINE_HSV:
                                svm_node_combine_hsv(kg, sd, stack, node.y, node.z, node.w, &offset);
                                break;
-                       case NODE_HSV:
-                               svm_node_hsv(kg, sd, stack, node.y, node.z, node.w, &offset);
-                               break;
-#endif  /* __EXTRA_NODES__ */
-                       case NODE_ATTR:
-                               svm_node_attr(kg, sd, stack, node);
-                               break;
-#ifdef __EXTRA_NODES__
-                       case NODE_ATTR_BUMP_DX:
-                               svm_node_attr_bump_dx(kg, sd, stack, node);
-                               break;
-                       case NODE_ATTR_BUMP_DY:
-                               svm_node_attr_bump_dy(kg, sd, stack, node);
-                               break;
-#endif  /* __EXTRA_NODES__ */
-                       case NODE_FRESNEL:
-                               svm_node_fresnel(sd, stack, node.y, node.z, node.w);
-                               break;
-                       case NODE_LAYER_WEIGHT:
-                               svm_node_layer_weight(sd, stack, node);
+                       case NODE_VECTOR_TRANSFORM:
+                               svm_node_vector_transform(kg, sd, stack, node);
                                break;
-#ifdef __EXTRA_NODES__
                        case NODE_WIREFRAME:
                                svm_node_wireframe(kg, sd, stack, node);
                                break;
@@ -378,63 +445,8 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, Shade
                        case NODE_BLACKBODY:
                                svm_node_blackbody(kg, sd, stack, node.y, node.z);
                                break;
-                       case NODE_SET_DISPLACEMENT:
-                               svm_node_set_displacement(sd, stack, node.y);
-                               break;
-                       case NODE_SET_BUMP:
-                               svm_node_set_bump(kg, sd, stack, node);
-                               break;
-                       case NODE_MATH:
-                               svm_node_math(kg, sd, stack, node.y, node.z, node.w, &offset);
-                               break;
-                       case NODE_VECTOR_MATH:
-                               svm_node_vector_math(kg, sd, stack, node.y, node.z, node.w, &offset);
-                               break;
-                       case NODE_VECTOR_TRANSFORM:
-                               svm_node_vector_transform(kg, sd, stack, node);
-                               break;
-                       case NODE_NORMAL:
-                               svm_node_normal(kg, sd, stack, node.y, node.z, node.w, &offset);
-                               break;
-#endif  /* __EXTRA_NODES__ */
-                       case NODE_MAPPING:
-                               svm_node_mapping(kg, sd, stack, node.y, node.z, &offset);
-                               break;
-                       case NODE_MIN_MAX:
-                               svm_node_min_max(kg, sd, stack, node.y, node.z, &offset);
-                               break;
-                       case NODE_TEX_COORD:
-                               svm_node_tex_coord(kg, sd, path_flag, stack, node, &offset);
-                               break;
-#ifdef __EXTRA_NODES__
-                       case NODE_TEX_COORD_BUMP_DX:
-                               svm_node_tex_coord_bump_dx(kg, sd, path_flag, stack, node, &offset);
-                               break;
-                       case NODE_TEX_COORD_BUMP_DY:
-                               svm_node_tex_coord_bump_dy(kg, sd, path_flag, stack, node, &offset);
-                               break;
-                       case NODE_CLOSURE_SET_NORMAL:
-                               svm_node_set_normal(kg, sd, stack, node.y, node.z );
-                               break;
-                       case NODE_RGB_RAMP:
-                               svm_node_rgb_ramp(kg, sd, stack, node, &offset);
-                               break;
-                       case NODE_RGB_CURVES:
-                               svm_node_rgb_curves(kg, sd, stack, node, &offset);
-                               break;
-                       case NODE_VECTOR_CURVES:
-                               svm_node_vector_curves(kg, sd, stack, node, &offset);
-                               break;
-                       case NODE_LIGHT_FALLOFF:
-                               svm_node_light_falloff(sd, stack, node);
-                               break;
-#endif  /* __EXTRA_NODES__ */
-                       case NODE_TANGENT:
-                               svm_node_tangent(kg, sd, stack, node);
-                               break;
-                       case NODE_NORMAL_MAP:
-                               svm_node_normal_map(kg, sd, stack, node);
-                               break;  
+#  endif  /* __EXTRA_NODES__ */
+#endif  /* NODES_GROUP(NODE_GROUP_LEVEL_3) */
                        case NODE_END:
                        default:
                                return;
index 11dc14f72e00c5e0df96c7843c0965a21bd16936..009e91192eba8218192a5608c3c9a5b28790cc32 100644 (file)
@@ -39,7 +39,8 @@ CCL_NAMESPACE_BEGIN
 #define NODE_GROUP_LEVEL_0    0
 #define NODE_GROUP_LEVEL_1    1
 #define NODE_GROUP_LEVEL_2    2
-#define NODE_GROUP_LEVEL_MAX  NODE_GROUP_LEVEL_2
+#define NODE_GROUP_LEVEL_3    3
+#define NODE_GROUP_LEVEL_MAX  NODE_GROUP_LEVEL_3
 
 #define NODE_FEATURE_VOLUME     (1 << 0)
 #define NODE_FEATURE_HAIR       (1 << 1)
index c0a118dd9b2ae81a9370810e397030857301196f..d1b30db18a950c8913bf815b5413c3dea9bfb18f 100644 (file)
@@ -105,6 +105,7 @@ public:
        ~EnvironmentTextureNode();
        ShaderNode *clone() const;
        void attributes(Shader *shader, AttributeRequestSet *attributes);
+       virtual int get_group() { return NODE_GROUP_LEVEL_2; }
 
        ImageManager *image_manager;
        int is_float;
@@ -124,6 +125,8 @@ class SkyTextureNode : public TextureNode {
 public:
        SHADER_NODE_CLASS(SkyTextureNode)
 
+       virtual int get_group() { return NODE_GROUP_LEVEL_2; }
+
        float3 sun_direction;
        float turbidity;
        float ground_albedo;
@@ -141,6 +144,8 @@ class GradientTextureNode : public TextureNode {
 public:
        SHADER_NODE_CLASS(GradientTextureNode)
 
+       virtual int get_group() { return NODE_GROUP_LEVEL_2; }
+
        ustring type;
        static ShaderEnum type_enum;
 };
@@ -154,6 +159,8 @@ class VoronoiTextureNode : public TextureNode {
 public:
        SHADER_NODE_CLASS(VoronoiTextureNode)
 
+       virtual int get_group() { return NODE_GROUP_LEVEL_2; }
+
        ustring coloring;
 
        static ShaderEnum coloring_enum;
@@ -163,6 +170,8 @@ class MusgraveTextureNode : public TextureNode {
 public:
        SHADER_NODE_CLASS(MusgraveTextureNode)
 
+       virtual int get_group() { return NODE_GROUP_LEVEL_2; }
+
        ustring type;
 
        static ShaderEnum type_enum;
@@ -172,6 +181,8 @@ class WaveTextureNode : public TextureNode {
 public:
        SHADER_NODE_CLASS(WaveTextureNode)
 
+       virtual int get_group() { return NODE_GROUP_LEVEL_2; }
+
        ustring type;
        static ShaderEnum type_enum;
 };
@@ -180,12 +191,16 @@ class MagicTextureNode : public TextureNode {
 public:
        SHADER_NODE_CLASS(MagicTextureNode)
 
+       virtual int get_group() { return NODE_GROUP_LEVEL_2; }
+
        int depth;
 };
 
 class CheckerTextureNode : public TextureNode {
 public:
        SHADER_NODE_CLASS(CheckerTextureNode)
+
+       virtual int get_group() { return NODE_GROUP_LEVEL_2; }
 };
 
 class BrickTextureNode : public TextureNode {
@@ -194,11 +209,14 @@ public:
        
        float offset, squash;
        int offset_frequency, squash_frequency;
+
+       virtual int get_group() { return NODE_GROUP_LEVEL_2; }
 };
 
 class MappingNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(MappingNode)
+       virtual int get_group() { return NODE_GROUP_LEVEL_2; }
 
        TextureMapping tex_mapping;
 };
@@ -321,6 +339,7 @@ public:
 class HoldoutNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(HoldoutNode)
+       virtual int get_group() { return NODE_GROUP_LEVEL_1; }
 };
 
 class AmbientOcclusionNode : public ShaderNode {
@@ -328,6 +347,7 @@ public:
        SHADER_NODE_CLASS(AmbientOcclusionNode)
 
        bool has_spatial_varying() { return true; }
+       virtual int get_group() { return NODE_GROUP_LEVEL_1; }
 };
 
 class VolumeNode : public ShaderNode {
@@ -335,6 +355,7 @@ public:
        SHADER_NODE_CLASS(VolumeNode)
 
        void compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput *param2);
+       virtual int get_group() { return NODE_GROUP_LEVEL_1; }
 
        ClosureType closure;
 };
@@ -382,6 +403,7 @@ public:
        SHADER_NODE_CLASS(UVMapNode)
        void attributes(Shader *shader, AttributeRequestSet *attributes);
        bool has_spatial_varying() { return true; }
+       virtual int get_group() { return NODE_GROUP_LEVEL_1; }
 
        ustring attribute;
        bool from_dupli;
@@ -390,6 +412,7 @@ public:
 class LightPathNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(LightPathNode)
+       virtual int get_group() { return NODE_GROUP_LEVEL_1; }
 };
 
 class LightFalloffNode : public ShaderNode {
@@ -401,12 +424,14 @@ public:
 class ObjectInfoNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(ObjectInfoNode)
+       virtual int get_group() { return NODE_GROUP_LEVEL_1; }
 };
 
 class ParticleInfoNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(ParticleInfoNode)
        void attributes(Shader *shader, AttributeRequestSet *attributes);
+       virtual int get_group() { return NODE_GROUP_LEVEL_1; }
 };
 
 class HairInfoNode : public ShaderNode {
@@ -415,6 +440,7 @@ public:
 
        void attributes(Shader *shader, AttributeRequestSet *attributes);
        bool has_spatial_varying() { return true; }
+       virtual int get_group() { return NODE_GROUP_LEVEL_1; }
        virtual int get_feature() {
                return ShaderNode::get_feature() | NODE_FEATURE_HAIR;
        }
@@ -452,12 +478,16 @@ public:
 class InvertNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(InvertNode)
+
+       virtual int get_group() { return NODE_GROUP_LEVEL_3; }
 };
 
 class MixNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(MixNode)
 
+       virtual int get_group() { return NODE_GROUP_LEVEL_3; }
+
        bool use_clamp;
 
        ustring type;
@@ -467,41 +497,55 @@ public:
 class CombineRGBNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(CombineRGBNode)
+
+       virtual int get_group() { return NODE_GROUP_LEVEL_3; }
 };
 
 class CombineHSVNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(CombineHSVNode)
+
+       virtual int get_group() { return NODE_GROUP_LEVEL_3; }
 };
 
 class CombineXYZNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(CombineXYZNode)
+
+       virtual int get_group() { return NODE_GROUP_LEVEL_3; }
 };
 
 class GammaNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(GammaNode)
+       virtual int get_group() { return NODE_GROUP_LEVEL_1; }
 };
 
 class BrightContrastNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(BrightContrastNode)
+       virtual int get_group() { return NODE_GROUP_LEVEL_1; }
 };
 
 class SeparateRGBNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(SeparateRGBNode)
+
+       virtual int get_group() { return NODE_GROUP_LEVEL_3; }
 };
 
 class SeparateHSVNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(SeparateHSVNode)
+
+       virtual int get_group() { return NODE_GROUP_LEVEL_3; }
 };
 
 class SeparateXYZNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(SeparateXYZNode)
+
+       virtual int get_group() { return NODE_GROUP_LEVEL_3; }
 };
 
 class HSVNode : public ShaderNode {
@@ -528,18 +572,21 @@ class FresnelNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(FresnelNode)
        bool has_spatial_varying() { return true; }
+       virtual int get_group() { return NODE_GROUP_LEVEL_1; }
 };
 
 class LayerWeightNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(LayerWeightNode)
        bool has_spatial_varying() { return true; }
+       virtual int get_group() { return NODE_GROUP_LEVEL_1; }
 };
 
 class WireframeNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(WireframeNode)
        bool has_spatial_varying() { return true; }
+       virtual int get_group() { return NODE_GROUP_LEVEL_3; }
        
        bool use_pixel_size;
 };
@@ -547,16 +594,21 @@ public:
 class WavelengthNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(WavelengthNode)
+
+       virtual int get_group() { return NODE_GROUP_LEVEL_3; }
 };
 
 class BlackbodyNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(BlackbodyNode)
+
+       virtual int get_group() { return NODE_GROUP_LEVEL_3; }
 };
 
 class MathNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(MathNode)
+       virtual int get_group() { return NODE_GROUP_LEVEL_1; }
 
        bool use_clamp;
 
@@ -567,6 +619,7 @@ public:
 class NormalNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(NormalNode)
+       virtual int get_group() { return NODE_GROUP_LEVEL_2; }
 
        float3 direction;
 };
@@ -574,6 +627,7 @@ public:
 class VectorMathNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(VectorMathNode)
+       virtual int get_group() { return NODE_GROUP_LEVEL_1; }
 
        ustring type;
        static ShaderEnum type_enum;
@@ -583,6 +637,8 @@ class VectorTransformNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(VectorTransformNode)
 
+       virtual int get_group() { return NODE_GROUP_LEVEL_3; }
+
        ustring type;
        ustring convert_from;
        ustring convert_to;
@@ -602,12 +658,18 @@ public:
 class RGBCurvesNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(RGBCurvesNode)
+
+       virtual int get_group() { return NODE_GROUP_LEVEL_3; }
+
        float4 curves[RAMP_TABLE_SIZE];
 };
 
 class VectorCurvesNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(VectorCurvesNode)
+
+       virtual int get_group() { return NODE_GROUP_LEVEL_3; }
+
        float4 curves[RAMP_TABLE_SIZE];
 };
 
@@ -616,6 +678,7 @@ public:
        SHADER_NODE_CLASS(RGBRampNode)
        float4 ramp[RAMP_TABLE_SIZE];
        bool interpolate;
+       virtual int get_group() { return NODE_GROUP_LEVEL_1; }
 };
 
 class SetNormalNode : public ShaderNode {
@@ -644,6 +707,7 @@ public:
        SHADER_NODE_CLASS(NormalMapNode)
        void attributes(Shader *shader, AttributeRequestSet *attributes);
        bool has_spatial_varying() { return true; }
+       virtual int get_group() { return NODE_GROUP_LEVEL_3; }
 
        ustring space;
        static ShaderEnum space_enum;
@@ -656,6 +720,7 @@ public:
        SHADER_NODE_CLASS(TangentNode)
        void attributes(Shader *shader, AttributeRequestSet *attributes);
        bool has_spatial_varying() { return true; }
+       virtual int get_group() { return NODE_GROUP_LEVEL_3; }
 
        ustring direction_type;
        static ShaderEnum direction_type_enum;