Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brechtvanlommel@pandora.be>2012-11-03 18:32:35 +0400
committerBrecht Van Lommel <brechtvanlommel@pandora.be>2012-11-03 18:32:35 +0400
commiteb87529e23cdc744ed52b00f3de25e208b29d7f1 (patch)
treea0579a1bfb292d8908c8e704a6d82f4dc3f47ecb
parente02b23b81ab05579c0ee11ee3a1acb283643e528 (diff)
Cycles OSL: shader script node
Documentation here: http://wiki.blender.org/index.php/Doc:2.6/Manual/Render/Cycles/Nodes/OSL http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.65/Cycles These changes require an OSL build from this repository: https://github.com/DingTo/OpenShadingLanguage The lib/ OSL has not been updated yet, so you might want to keep OSL disabled until that is done. Still todo: * Auto update for external .osl files not working currently, press update manually * Node could indicate better when a refresh is needed * Attributes like UV or generated coordinates may be missing when requested from an OSL shader, need a way to request them to be loaded by cycles * Expose string, enum and other non-socket parameters * Scons build support Thanks to Thomas, Lukas and Dalai for the implementation.
-rw-r--r--intern/cycles/blender/CMakeLists.txt1
-rw-r--r--intern/cycles/blender/addon/__init__.py8
-rw-r--r--intern/cycles/blender/addon/osl.py124
-rw-r--r--intern/cycles/blender/blender_python.cpp176
-rw-r--r--intern/cycles/blender/blender_shader.cpp67
-rw-r--r--intern/cycles/render/nodes.cpp20
-rw-r--r--intern/cycles/render/nodes.h12
-rw-r--r--intern/cycles/render/osl.cpp330
-rw-r--r--intern/cycles/render/osl.h23
-rw-r--r--intern/cycles/render/shader.h12
-rw-r--r--intern/cycles/util/util_path.cpp10
-rw-r--r--intern/cycles/util/util_path.h3
12 files changed, 677 insertions, 109 deletions
diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt
index a8c7eef89fa..96948e49d1a 100644
--- a/intern/cycles/blender/CMakeLists.txt
+++ b/intern/cycles/blender/CMakeLists.txt
@@ -38,6 +38,7 @@ set(ADDON_FILES
addon/__init__.py
addon/engine.py
addon/enums.py
+ addon/osl.py
addon/presets.py
addon/properties.py
addon/ui.py
diff --git a/intern/cycles/blender/addon/__init__.py b/intern/cycles/blender/addon/__init__.py
index 6292c09fbb1..16697c08b2b 100644
--- a/intern/cycles/blender/addon/__init__.py
+++ b/intern/cycles/blender/addon/__init__.py
@@ -71,6 +71,13 @@ class CyclesRender(bpy.types.RenderEngine):
def view_draw(self, context):
engine.draw(self, context.region, context.space_data, context.region_data)
+ def update_script_node(self, node):
+ if engine.with_osl():
+ from . import osl
+ osl.update_script_node(node, self.report)
+ else:
+ self.report({'ERROR'}, "OSL support disabled in this build.")
+
def register():
properties.register()
@@ -84,3 +91,4 @@ def unregister():
properties.unregister()
presets.unregister()
bpy.utils.unregister_module(__name__)
+
diff --git a/intern/cycles/blender/addon/osl.py b/intern/cycles/blender/addon/osl.py
new file mode 100644
index 00000000000..6b46fefce92
--- /dev/null
+++ b/intern/cycles/blender/addon/osl.py
@@ -0,0 +1,124 @@
+#
+# Copyright 2011, Blender Foundation.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+# <pep8 compliant>
+
+import bpy, _cycles, os, tempfile
+
+# compile .osl file with given filepath to temporary .oso file
+def osl_compile(input_path, report):
+ output_file = tempfile.NamedTemporaryFile(mode='w', suffix=".oso", delete=False)
+ output_path = output_file.name
+ output_file.close()
+
+ ok = _cycles.osl_compile(input_path, output_path)
+
+ if ok:
+ report({'INFO'}, "OSL shader compilation succeeded")
+
+ return ok, output_path
+
+# compile and update shader script node
+def update_script_node(node, report):
+ import os, shutil
+
+ if node.mode == 'EXTERNAL':
+ # compile external script file
+ script_path = bpy.path.abspath(node.filepath)
+ script_path_noext, script_ext = os.path.splitext(script_path)
+
+ if script_ext == ".oso":
+ # it's a .oso file, no need to compile
+ ok, oso_path = True, script_path
+ oso_file_remove = False
+ elif script_ext == ".osl":
+ # compile .osl file
+ ok, oso_path = osl_compile(script_path, report)
+ oso_file_remove = True
+
+ if ok:
+ # copy .oso from temporary path to .osl directory
+ dst_path = script_path_noext + ".oso"
+ try:
+ shutil.copy2(oso_path, dst_path)
+ except:
+ report({'ERROR'}, "Failed to write .oso file next to external .osl file at " + dst_path)
+ elif os.path.dirname(node.filepath) == "":
+ # module in search path
+ oso_path = node.filepath
+ oso_file_remove = False
+ ok = True
+ else:
+ # unknown
+ report({'ERROR'}, "External shader script must have .osl or .oso extension, or be a module name")
+ ok = False
+
+ if ok:
+ node.bytecode = ""
+ node.bytecode_hash = ""
+
+ elif node.mode == 'INTERNAL' and node.script:
+ # internal script, we will store bytecode in the node
+ script = node.script
+ osl_path = bpy.path.abspath(script.filepath)
+
+ if script.is_in_memory or script.is_dirty or script.is_modified or not os.path.exists(osl_path):
+ # write text datablock contents to temporary file
+ osl_file = tempfile.NamedTemporaryFile(mode='w', suffix=".osl", delete=True)
+ osl_file.write(script.as_string())
+ osl_file.flush()
+ ok, oso_path = osl_compile(osl_file.name, report)
+ oso_file_remove = False
+ osl_file.close()
+ else:
+ # compile text datablock from disk directly
+ ok, oso_path = osl_compile(osl_path, report)
+ oso_file_remove = False
+
+ if ok:
+ # read bytecode
+ try:
+ oso = open(oso_path, 'r')
+ node.bytecode = oso.read()
+ oso.close()
+ except:
+ report({'ERROR'}, "Can't read OSO bytecode to store in node at " + oso_path)
+ ok = False
+
+ else:
+ report({'WARNING'}, "No text or file specified in node, nothing to compile")
+ return
+
+ if ok:
+ # now update node with new sockets
+ ok = _cycles.osl_update_node(node.id_data.as_pointer(), node.as_pointer(), oso_path)
+
+ if not ok:
+ report({'ERROR'}, "OSL query failed to open " + oso_path)
+ else:
+ report({'ERROR'}, "OSL script compilation failed, see console for errors")
+
+ # remove temporary oso file
+ if oso_file_remove:
+ try:
+ os.remove(oso_path)
+ except:
+ pass
+
+ return ok
+
diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp
index d9220b76835..c047805c6ae 100644
--- a/intern/cycles/blender/blender_python.cpp
+++ b/intern/cycles/blender/blender_python.cpp
@@ -24,9 +24,17 @@
#include "blender_session.h"
#include "util_foreach.h"
+#include "util_md5.h"
#include "util_opengl.h"
#include "util_path.h"
+#ifdef WITH_OSL
+#include "osl.h"
+
+#include <OSL/oslquery.h>
+#include <OSL/oslconfig.h>
+#endif
+
CCL_NAMESPACE_BEGIN
static PyObject *init_func(PyObject *self, PyObject *args)
@@ -163,6 +171,170 @@ static PyObject *available_devices_func(PyObject *self, PyObject *args)
return ret;
}
+#ifdef WITH_OSL
+static PyObject *osl_update_node_func(PyObject *self, PyObject *args)
+{
+ PyObject *pynodegroup, *pynode;
+ const char *filepath = NULL;
+
+ if(!PyArg_ParseTuple(args, "OOs", &pynodegroup, &pynode, &filepath))
+ return NULL;
+
+ /* RNA */
+ PointerRNA nodeptr;
+ RNA_pointer_create((ID*)PyLong_AsVoidPtr(pynodegroup), &RNA_ShaderNodeScript, (void*)PyLong_AsVoidPtr(pynode), &nodeptr);
+ BL::ShaderNodeScript b_node(nodeptr);
+
+ /* update bytecode hash */
+ string bytecode = b_node.bytecode();
+
+ if(!bytecode.empty()) {
+ MD5Hash md5;
+ md5.append((const uint8_t*)bytecode.c_str(), bytecode.size());
+ b_node.bytecode_hash(md5.get_hex().c_str());
+ }
+ else
+ b_node.bytecode_hash("");
+
+ /* query from file path */
+ OSL::OSLQuery query;
+
+ if(!OSLShaderManager::osl_query(query, filepath))
+ Py_RETURN_FALSE;
+
+ /* add new sockets from parameters */
+ set<void*> used_sockets;
+
+ for(int i = 0; i < query.nparams(); i++) {
+ const OSL::OSLQuery::Parameter *param = query.getparam(i);
+
+ /* skip unsupported types */
+ if(param->varlenarray || param->isstruct || param->type.arraylen > 1)
+ continue;
+
+ /* determine socket type */
+ BL::NodeSocket::type_enum socket_type;
+ float default_float4[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ float default_float = 0.0f;
+ int default_int = 0;
+
+ if(param->isclosure) {
+ socket_type = BL::NodeSocket::type_SHADER;
+ }
+ else if(param->type.vecsemantics == TypeDesc::COLOR) {
+ socket_type = BL::NodeSocket::type_RGBA;
+
+ if(param->validdefault) {
+ default_float4[0] = param->fdefault[0];
+ default_float4[1] = param->fdefault[1];
+ default_float4[2] = param->fdefault[2];
+ }
+ }
+ else if(param->type.vecsemantics == TypeDesc::POINT ||
+ param->type.vecsemantics == TypeDesc::VECTOR ||
+ param->type.vecsemantics == TypeDesc::NORMAL) {
+ socket_type = BL::NodeSocket::type_VECTOR;
+
+ if(param->validdefault) {
+ default_float4[0] = param->fdefault[0];
+ default_float4[1] = param->fdefault[1];
+ default_float4[2] = param->fdefault[2];
+ }
+ }
+ else if(param->type.aggregate == TypeDesc::SCALAR) {
+ if(param->type.basetype == TypeDesc::INT) {
+ socket_type = BL::NodeSocket::type_INT;
+ if(param->validdefault)
+ default_int = param->idefault[0];
+ }
+ else if(param->type.basetype == TypeDesc::FLOAT) {
+ socket_type = BL::NodeSocket::type_VALUE;
+ if(param->validdefault)
+ default_float = param->fdefault[0];
+ }
+ }
+ else
+ continue;
+
+ /* find socket socket */
+ BL::NodeSocket b_sock = b_node.find_socket(param->name.c_str(), param->isoutput);
+
+ /* remove if type no longer matches */
+ if(b_sock && b_sock.type() != socket_type) {
+ b_node.remove_socket(b_sock);
+ b_sock = BL::NodeSocket(PointerRNA_NULL);
+ }
+
+ /* create new socket */
+ if(!b_sock) {
+ b_sock = b_node.add_socket(param->name.c_str(), socket_type, param->isoutput);
+
+ /* set default value */
+ if(socket_type == BL::NodeSocket::type_VALUE) {
+ BL::NodeSocketFloatNone b_float_sock(b_sock.ptr);
+ b_float_sock.default_value(default_float);
+ }
+ else if(socket_type == BL::NodeSocket::type_INT) {
+ BL::NodeSocketIntNone b_int_sock(b_sock.ptr);
+ b_int_sock.default_value(default_int);
+ }
+ else if(socket_type == BL::NodeSocket::type_RGBA) {
+ BL::NodeSocketRGBA b_rgba_sock(b_sock.ptr);
+ b_rgba_sock.default_value(default_float4);
+ }
+ else if(socket_type == BL::NodeSocket::type_VECTOR) {
+ BL::NodeSocketVectorNone b_vector_sock(b_sock.ptr);
+ b_vector_sock.default_value(default_float4);
+ }
+ }
+
+ used_sockets.insert(b_sock.ptr.data);
+ }
+
+ /* remove unused parameters */
+ bool removed;
+
+ do {
+ BL::Node::inputs_iterator b_input;
+ BL::Node::outputs_iterator b_output;
+
+ removed = false;
+
+ for (b_node.inputs.begin(b_input); b_input != b_node.inputs.end(); ++b_input) {
+ if(used_sockets.find(b_input->ptr.data) == used_sockets.end()) {
+ b_node.remove_socket(*b_input);
+ removed = true;
+ break;
+ }
+ }
+
+ for (b_node.outputs.begin(b_output); b_output != b_node.outputs.end(); ++b_output) {
+ if(used_sockets.find(b_output->ptr.data) == used_sockets.end()) {
+ b_node.remove_socket(*b_output);
+ removed = true;
+ break;
+ }
+ }
+ } while(removed);
+
+ Py_RETURN_TRUE;
+}
+
+static PyObject *osl_compile_func(PyObject *self, PyObject *args)
+{
+ const char *inputfile = NULL, *outputfile = NULL;
+
+ if(!PyArg_ParseTuple(args, "ss", &inputfile, &outputfile))
+ return NULL;
+
+ /* return */
+ if(!OSLShaderManager::osl_compile(inputfile, outputfile))
+ Py_RETURN_FALSE;
+
+ Py_RETURN_TRUE;
+}
+#endif
+
static PyMethodDef methods[] = {
{"init", init_func, METH_VARARGS, ""},
{"create", create_func, METH_VARARGS, ""},
@@ -170,6 +342,10 @@ static PyMethodDef methods[] = {
{"render", render_func, METH_O, ""},
{"draw", draw_func, METH_VARARGS, ""},
{"sync", sync_func, METH_O, ""},
+#ifdef WITH_OSL
+ {"osl_update_node", osl_update_node_func, METH_VARARGS, ""},
+ {"osl_compile", osl_compile_func, METH_VARARGS, ""},
+#endif
{"available_devices", available_devices_func, METH_NOARGS, ""},
{NULL, NULL, 0, NULL},
};
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
index 188996cc34d..9e3380c6f8e 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -20,6 +20,7 @@
#include "graph.h"
#include "light.h"
#include "nodes.h"
+#include "osl.h"
#include "scene.h"
#include "shader.h"
@@ -159,7 +160,7 @@ static void get_tex_mapping(TextureMapping *mapping, BL::ShaderNodeMapping b_map
mapping->max = get_float3(b_mapping.max());
}
-static ShaderNode *add_node(BL::BlendData b_data, BL::Scene b_scene, ShaderGraph *graph, BL::ShaderNode b_node)
+static ShaderNode *add_node(Scene *scene, BL::BlendData b_data, BL::Scene b_scene, ShaderGraph *graph, BL::ShaderNodeTree b_ntree, BL::ShaderNode b_node)
{
ShaderNode *node = NULL;
@@ -413,6 +414,58 @@ static ShaderNode *add_node(BL::BlendData b_data, BL::Scene b_scene, ShaderGraph
node = new BumpNode();
break;
}
+ case BL::ShaderNode::type_SCRIPT: {
+#ifdef WITH_OSL
+ if(scene->params.shadingsystem != SceneParams::OSL)
+ break;
+
+ /* create script node */
+ BL::ShaderNodeScript b_script_node(b_node);
+ OSLScriptNode *script_node = new OSLScriptNode();
+
+ /* Generate inputs/outputs from node sockets
+ *
+ * Note: the node sockets are generated from OSL parameters,
+ * so the names match those of the corresponding parameters exactly.
+ *
+ * Note 2: ShaderInput/ShaderOutput store shallow string copies only!
+ * Socket names must be stored in the extra lists instead. */
+ BL::Node::inputs_iterator b_input;
+
+ for (b_script_node.inputs.begin(b_input); b_input != b_script_node.inputs.end(); ++b_input) {
+ script_node->input_names.push_back(ustring(b_input->name()));
+ ShaderInput *input = script_node->add_input(script_node->input_names.back().c_str(), convert_socket_type(b_input->type()));
+ set_default_value(input, *b_input);
+ }
+
+ BL::Node::outputs_iterator b_output;
+
+ for (b_script_node.outputs.begin(b_output); b_output != b_script_node.outputs.end(); ++b_output) {
+ script_node->output_names.push_back(ustring(b_output->name()));
+ script_node->add_output(script_node->output_names.back().c_str(), convert_socket_type(b_output->type()));
+ }
+
+ /* load bytecode or filepath */
+ OSLShaderManager *manager = (OSLShaderManager*)scene->shader_manager;
+ string bytecode_hash = b_script_node.bytecode_hash();
+
+ if(!bytecode_hash.empty()) {
+ /* loaded bytecode if not already done */
+ if(!manager->shader_test_loaded(bytecode_hash))
+ manager->shader_load_bytecode(bytecode_hash, b_script_node.bytecode());
+
+ script_node->bytecode_hash = bytecode_hash;
+ }
+ else {
+ /* set filepath */
+ script_node->filepath = blender_absolute_path(b_data, b_ntree, b_script_node.filepath());
+ }
+
+ node = script_node;
+#endif
+
+ break;
+ }
case BL::ShaderNode::type_TEX_IMAGE: {
BL::ShaderNodeTexImage b_image_node(b_node);
BL::Image b_image(b_image_node.image());
@@ -576,7 +629,7 @@ static SocketPair node_socket_map_pair(PtrNodeMap& node_map, BL::Node b_node, BL
return SocketPair(node_map[b_node.ptr.data], name);
}
-static void add_nodes(BL::BlendData b_data, BL::Scene b_scene, ShaderGraph *graph, BL::ShaderNodeTree b_ntree, PtrSockMap& sockets_map)
+static void add_nodes(Scene *scene, BL::BlendData b_data, BL::Scene b_scene, ShaderGraph *graph, BL::ShaderNodeTree b_ntree, PtrSockMap& sockets_map)
{
/* add nodes */
BL::ShaderNodeTree::nodes_iterator b_node;
@@ -651,10 +704,10 @@ static void add_nodes(BL::BlendData b_data, BL::Scene b_scene, ShaderGraph *grap
set_default_value(proxy->inputs[0], b_output->group_socket());
}
- add_nodes(b_data, b_scene, graph, b_group_ntree, group_sockmap);
+ add_nodes(scene, b_data, b_scene, graph, b_group_ntree, group_sockmap);
}
else {
- ShaderNode *node = add_node(b_data, b_scene, graph, BL::ShaderNode(*b_node));
+ ShaderNode *node = add_node(scene, b_data, b_scene, graph, b_ntree, BL::ShaderNode(*b_node));
if(node) {
BL::Node::inputs_iterator b_input;
@@ -744,7 +797,7 @@ void BlenderSync::sync_materials()
PtrSockMap sock_to_node;
BL::ShaderNodeTree b_ntree(b_mat->node_tree());
- add_nodes(b_data, b_scene, graph, b_ntree, sock_to_node);
+ add_nodes(scene, b_data, b_scene, graph, b_ntree, sock_to_node);
}
else {
ShaderNode *closure, *out;
@@ -785,7 +838,7 @@ void BlenderSync::sync_world()
PtrSockMap sock_to_node;
BL::ShaderNodeTree b_ntree(b_world.node_tree());
- add_nodes(b_data, b_scene, graph, b_ntree, sock_to_node);
+ add_nodes(scene, b_data, b_scene, graph, b_ntree, sock_to_node);
}
else if(b_world) {
ShaderNode *closure, *out;
@@ -844,7 +897,7 @@ void BlenderSync::sync_lamps()
PtrSockMap sock_to_node;
BL::ShaderNodeTree b_ntree(b_lamp->node_tree());
- add_nodes(b_data, b_scene, graph, b_ntree, sock_to_node);
+ add_nodes(scene, b_data, b_scene, graph, b_ntree, sock_to_node);
}
else {
ShaderNode *closure, *out;
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp
index 2cdab3a6560..42ab3fe17aa 100644
--- a/intern/cycles/render/nodes.cpp
+++ b/intern/cycles/render/nodes.cpp
@@ -2922,5 +2922,25 @@ void SetNormalNode::compile(OSLCompiler& compiler)
compiler.add(this, "node_set_normal");
}
+/* OSLScriptNode */
+
+OSLScriptNode::OSLScriptNode()
+: ShaderNode("osl_script")
+{
+}
+
+void OSLScriptNode::compile(SVMCompiler& compiler)
+{
+ /* doesn't work for SVM, obviously ... */
+}
+
+void OSLScriptNode::compile(OSLCompiler& compiler)
+{
+ if(!filepath.empty())
+ compiler.add(this, filepath.c_str(), true);
+ else
+ compiler.add(this, bytecode_hash.c_str(), false);
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h
index fbc61e12fd4..0508bf6b266 100644
--- a/intern/cycles/render/nodes.h
+++ b/intern/cycles/render/nodes.h
@@ -445,6 +445,18 @@ public:
SHADER_NODE_CLASS(SetNormalNode)
};
+class OSLScriptNode : public ShaderNode {
+public:
+ SHADER_NODE_CLASS(OSLScriptNode)
+ string filepath;
+ string bytecode_hash;
+
+ /* ShaderInput/ShaderOutput only stores a shallow string copy (const char *)!
+ * The actual socket names have to be stored externally to avoid memory errors. */
+ vector<ustring> input_names;
+ vector<ustring> output_names;
+};
+
CCL_NAMESPACE_END
#endif /* __NODES_H__ */
diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp
index 4856a8d4e0c..a8a40a4e596 100644
--- a/intern/cycles/render/osl.cpp
+++ b/intern/cycles/render/osl.cpp
@@ -31,6 +31,7 @@
#include "osl_shader.h"
#include "util_foreach.h"
+#include "util_md5.h"
#include "util_path.h"
#include "util_progress.h"
@@ -46,36 +47,8 @@ OSLShaderManager::OSLShaderManager()
{
services = new OSLRenderServices();
- /* if we let OSL create it, it leaks */
- ts = TextureSystem::create(true);
- ts->attribute("automip", 1);
- ts->attribute("autotile", 64);
-
- ss = OSL::ShadingSystem::create(services, ts, &errhandler);
- ss->attribute("lockgeom", 1);
- ss->attribute("commonspace", "world");
- ss->attribute("optimize", 2);
- //ss->attribute("debug", 1);
- //ss->attribute("statistics:level", 1);
- ss->attribute("searchpath:shader", path_get("shader").c_str());
-
- /* our own ray types */
- static const char *raytypes[] = {
- "camera", /* PATH_RAY_CAMERA */
- "reflection", /* PATH_RAY_REFLECT */
- "refraction", /* PATH_RAY_TRANSMIT */
- "diffuse", /* PATH_RAY_DIFFUSE */
- "glossy", /* PATH_RAY_GLOSSY */
- "singular", /* PATH_RAY_SINGULAR */
- "transparent", /* PATH_RAY_TRANSPARENT */
- "shadow", /* PATH_RAY_SHADOW_OPAQUE */
- "shadow", /* PATH_RAY_SHADOW_TRANSPARENT */
- };
-
- const int nraytypes = sizeof(raytypes)/sizeof(raytypes[0]);
- ss->attribute("raytypes", TypeDesc(TypeDesc::STRING, nraytypes), raytypes);
-
- OSLShader::register_closures(ss);
+ shading_system_init();
+ texture_system_init();
}
OSLShaderManager::~OSLShaderManager()
@@ -87,13 +60,6 @@ OSLShaderManager::~OSLShaderManager()
void OSLShaderManager::device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress)
{
- /* test if we need to update */
- bool need_update = false;
-
- foreach(Shader *shader, scene->shaders)
- if(shader->need_update)
- need_update = true;
-
if(!need_update)
return;
@@ -113,7 +79,7 @@ void OSLShaderManager::device_update(Device *device, DeviceScene *dscene, Scene
if(shader->sample_as_light && shader->has_surface_emission)
scene->light_manager->need_update = true;
- OSLCompiler compiler((void*)ss);
+ OSLCompiler compiler((void*)this, (void*)ss);
compiler.background = (shader == scene->shaders[scene->default_background]);
compiler.compile(og, shader);
}
@@ -129,6 +95,8 @@ void OSLShaderManager::device_update(Device *device, DeviceScene *dscene, Scene
foreach(Shader *shader, scene->shaders)
shader->need_update = false;
+
+ need_update = false;
/* set texture system */
scene->image_manager->set_osl_texture_system((void*)ts);
@@ -154,10 +122,165 @@ void OSLShaderManager::device_free(Device *device, DeviceScene *dscene)
og->background_state.reset();
}
+void OSLShaderManager::texture_system_init()
+{
+ /* if we let OSL create it, it leaks */
+ ts = TextureSystem::create(true);
+ ts->attribute("automip", 1);
+ ts->attribute("autotile", 64);
+
+ /* effectively unlimited for now, until we support proper mipmap lookups */
+ ts->attribute("max_memory_MB", 16384);
+}
+
+void OSLShaderManager::shading_system_init()
+{
+ ss = OSL::ShadingSystem::create(services, ts, &errhandler);
+ ss->attribute("lockgeom", 1);
+ ss->attribute("commonspace", "world");
+ ss->attribute("optimize", 2);
+ //ss->attribute("debug", 1);
+ //ss->attribute("statistics:level", 1);
+ ss->attribute("searchpath:shader", path_get("shader"));
+
+ /* our own ray types */
+ static const char *raytypes[] = {
+ "camera", /* PATH_RAY_CAMERA */
+ "reflection", /* PATH_RAY_REFLECT */
+ "refraction", /* PATH_RAY_TRANSMIT */
+ "diffuse", /* PATH_RAY_DIFFUSE */
+ "glossy", /* PATH_RAY_GLOSSY */
+ "singular", /* PATH_RAY_SINGULAR */
+ "transparent", /* PATH_RAY_TRANSPARENT */
+ "shadow", /* PATH_RAY_SHADOW_OPAQUE */
+ "shadow", /* PATH_RAY_SHADOW_TRANSPARENT */
+ };
+
+ const int nraytypes = sizeof(raytypes)/sizeof(raytypes[0]);
+ ss->attribute("raytypes", TypeDesc(TypeDesc::STRING, nraytypes), raytypes);
+
+ OSLShader::register_closures(ss);
+
+ loaded_shaders.clear();
+}
+
+bool OSLShaderManager::osl_compile(const string& inputfile, const string& outputfile)
+{
+ vector<string> options;
+ string stdosl_path;
+
+ /* specify output file name */
+ options.push_back("-o");
+ options.push_back(outputfile);
+
+ /* specify standard include path */
+ options.push_back("-I" + path_get("shader"));
+ stdosl_path = path_get("shader/stdosl.h");
+
+ /* compile */
+ OSL::OSLCompiler *compiler = OSL::OSLCompiler::create();
+ bool ok = compiler->compile(inputfile, options, stdosl_path);
+ delete compiler;
+
+ return ok;
+}
+
+bool OSLShaderManager::osl_query(OSL::OSLQuery& query, const string& filepath)
+{
+ string searchpath = path_user_get("shaders");
+ return query.open(filepath, searchpath);
+}
+
+static string shader_filepath_hash(const string& filepath, uint64_t modified_time)
+{
+ /* compute a hash from filepath and modified time to detect changes */
+ MD5Hash md5;
+ md5.append((const uint8_t*)filepath.c_str(), filepath.size());
+ md5.append((const uint8_t*)&modified_time, sizeof(modified_time));
+
+ return md5.get_hex();
+}
+
+const char *OSLShaderManager::shader_test_loaded(const string& hash)
+{
+ set<string>::iterator it = loaded_shaders.find(hash);
+ return (it == loaded_shaders.end())? NULL: it->c_str();
+}
+
+const char *OSLShaderManager::shader_load_filepath(string filepath)
+{
+ size_t len = filepath.size();
+ string extension = filepath.substr(len - 4);
+ uint64_t modified_time = path_modified_time(filepath);
+
+ if(extension == ".osl") {
+ /* .OSL File */
+ string osopath = filepath.substr(0, len - 4) + ".oso";
+ uint64_t oso_modified_time = path_modified_time(osopath);
+
+ /* test if we have loaded the corresponding .OSO already */
+ if(oso_modified_time != 0) {
+ const char *hash = shader_test_loaded(shader_filepath_hash(osopath, oso_modified_time));
+
+ if(hash)
+ return hash;
+ }
+
+ /* autocompile .OSL to .OSO if needed */
+ if(oso_modified_time == 0 || (oso_modified_time < modified_time)) {
+ OSLShaderManager::osl_compile(filepath, osopath);
+ modified_time = path_modified_time(osopath);
+ }
+ else
+ modified_time = oso_modified_time;
+
+ filepath = osopath;
+ }
+ else {
+ if(extension == ".oso") {
+ /* .OSO File, nothing to do */
+ }
+ else if(path_dirname(filepath) == "") {
+ /* .OSO File in search path */
+ filepath = path_join(path_user_get("shaders"), filepath + ".oso");
+ }
+ else {
+ /* unknown file */
+ return NULL;
+ }
+
+ /* test if we have loaded this .OSO already */
+ const char *hash = shader_test_loaded(shader_filepath_hash(filepath, modified_time));
+
+ if(hash)
+ return hash;
+ }
+
+ /* read oso bytecode from file */
+ string bytecode_hash = shader_filepath_hash(filepath, modified_time);
+ string bytecode;
+
+ if(!path_read_text(filepath, bytecode)) {
+ fprintf(stderr, "Cycles shader graph: failed to read file %s\n", filepath.c_str());
+ loaded_shaders.insert(bytecode_hash); /* to avoid repeat tries */
+ return NULL;
+ }
+
+ return shader_load_bytecode(bytecode_hash, bytecode);
+}
+
+const char *OSLShaderManager::shader_load_bytecode(const string& hash, const string& bytecode)
+{
+ ss->LoadMemoryShader(hash.c_str(), bytecode.c_str());
+
+ return loaded_shaders.insert(hash).first->c_str();
+}
+
/* Graph Compiler */
-OSLCompiler::OSLCompiler(void *shadingsys_)
+OSLCompiler::OSLCompiler(void *manager_, void *shadingsys_)
{
+ manager = manager_;
shadingsys = shadingsys_;
current_type = SHADER_TYPE_SURFACE;
current_shader = NULL;
@@ -238,10 +361,18 @@ bool OSLCompiler::node_skip_input(ShaderNode *node, ShaderInput *input)
return false;
}
-void OSLCompiler::add(ShaderNode *node, const char *name)
+void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath)
{
OSL::ShadingSystem *ss = (OSL::ShadingSystem*)shadingsys;
+ /* load filepath */
+ if(isfilepath) {
+ name = ((OSLShaderManager*)manager)->shader_load_filepath(name);
+
+ if(name == NULL)
+ return;
+ }
+
/* pass in fixed parameter values */
foreach(ShaderInput *input, node->inputs) {
if(!input->link) {
@@ -510,82 +641,85 @@ void OSLCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
{
- OSL::ShadingSystem *ss = (OSL::ShadingSystem*)shadingsys;
- ShaderGraph *graph = shader->graph;
- ShaderNode *output = (graph)? graph->output(): NULL;
+ if(shader->need_update) {
+ OSL::ShadingSystem *ss = (OSL::ShadingSystem*)shadingsys;
+ ShaderGraph *graph = shader->graph;
+ ShaderNode *output = (graph)? graph->output(): NULL;
- /* copy graph for shader with bump mapping */
- if(output->input("Surface")->link && output->input("Displacement")->link)
- if(!shader->graph_bump)
- shader->graph_bump = shader->graph->copy();
+ /* copy graph for shader with bump mapping */
+ if(output->input("Surface")->link && output->input("Displacement")->link)
+ if(!shader->graph_bump)
+ shader->graph_bump = shader->graph->copy();
- /* finalize */
- shader->graph->finalize(false, true);
- if(shader->graph_bump)
- shader->graph_bump->finalize(true, true);
+ /* finalize */
+ shader->graph->finalize(false, true);
+ if(shader->graph_bump)
+ shader->graph_bump->finalize(true, true);
- current_shader = shader;
+ current_shader = shader;
- shader->has_surface = false;
- shader->has_surface_emission = false;
- shader->has_surface_transparent = false;
- shader->has_volume = false;
- shader->has_displacement = false;
+ shader->has_surface = false;
+ shader->has_surface_emission = false;
+ shader->has_surface_transparent = false;
+ shader->has_volume = false;
+ shader->has_displacement = false;
- /* generate surface shader */
- if(shader->used && graph && output->input("Surface")->link) {
- compile_type(shader, shader->graph, SHADER_TYPE_SURFACE);
- og->surface_state.push_back(ss->state());
+ /* generate surface shader */
+ if(shader->used && graph && output->input("Surface")->link) {
+ compile_type(shader, shader->graph, SHADER_TYPE_SURFACE);
+ shader->osl_surface_ref = ss->state();
+
+ if(shader->graph_bump) {
+ ss->clear_state();
+ compile_type(shader, shader->graph_bump, SHADER_TYPE_SURFACE);
+ }
- if(shader->graph_bump) {
+ shader->osl_surface_bump_ref = ss->state();
ss->clear_state();
- compile_type(shader, shader->graph_bump, SHADER_TYPE_SURFACE);
- og->surface_state.push_back(ss->state());
- }
- else
- og->surface_state.push_back(ss->state());
- ss->clear_state();
+ shader->has_surface = true;
+ }
+ else {
+ shader->osl_surface_ref = OSL::ShadingAttribStateRef();
+ shader->osl_surface_bump_ref = OSL::ShadingAttribStateRef();
+ }
- shader->has_surface = true;
- }
- else {
- og->surface_state.push_back(OSL::ShadingAttribStateRef());
- og->surface_state.push_back(OSL::ShadingAttribStateRef());
- }
+ /* generate volume shader */
+ if(shader->used && graph && output->input("Volume")->link) {
+ compile_type(shader, shader->graph, SHADER_TYPE_VOLUME);
+ shader->has_volume = true;
- /* generate volume shader */
- if(shader->used && graph && output->input("Volume")->link) {
- compile_type(shader, shader->graph, SHADER_TYPE_VOLUME);
- shader->has_volume = true;
+ shader->osl_volume_ref = ss->state();
+ ss->clear_state();
+ }
+ else
+ shader->osl_volume_ref = OSL::ShadingAttribStateRef();
- og->volume_state.push_back(ss->state());
- og->volume_state.push_back(ss->state());
- ss->clear_state();
- }
- else {
- og->volume_state.push_back(OSL::ShadingAttribStateRef());
- og->volume_state.push_back(OSL::ShadingAttribStateRef());
+ /* generate displacement shader */
+ if(shader->used && graph && output->input("Displacement")->link) {
+ compile_type(shader, shader->graph, SHADER_TYPE_DISPLACEMENT);
+ shader->has_displacement = true;
+ shader->osl_displacement_ref = ss->state();
+ ss->clear_state();
+ }
+ else
+ shader->osl_displacement_ref = OSL::ShadingAttribStateRef();
}
- /* generate displacement shader */
- if(shader->used && graph && output->input("Displacement")->link) {
- compile_type(shader, shader->graph, SHADER_TYPE_DISPLACEMENT);
- shader->has_displacement = true;
+ /* push state to array for lookup */
+ og->surface_state.push_back(shader->osl_surface_ref);
+ og->surface_state.push_back(shader->osl_surface_bump_ref);
- og->displacement_state.push_back(ss->state());
- og->displacement_state.push_back(ss->state());
- ss->clear_state();
- }
- else {
- og->displacement_state.push_back(OSL::ShadingAttribStateRef());
- og->displacement_state.push_back(OSL::ShadingAttribStateRef());
- }
+ og->volume_state.push_back(shader->osl_volume_ref);
+ og->volume_state.push_back(shader->osl_volume_ref);
+
+ og->displacement_state.push_back(shader->osl_displacement_ref);
+ og->displacement_state.push_back(shader->osl_displacement_ref);
}
#else
-void OSLCompiler::add(ShaderNode *node, const char *name)
+void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath)
{
}
diff --git a/intern/cycles/render/osl.h b/intern/cycles/render/osl.h
index 90107a34a98..cee37c58d74 100644
--- a/intern/cycles/render/osl.h
+++ b/intern/cycles/render/osl.h
@@ -20,11 +20,14 @@
#define __OSL_H__
#include "util_set.h"
+#include "util_string.h"
#include "shader.h"
#ifdef WITH_OSL
+#include <OSL/oslcomp.h>
#include <OSL/oslexec.h>
+#include <OSL/oslquery.h>
#endif
CCL_NAMESPACE_BEGIN
@@ -52,11 +55,24 @@ public:
void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress);
void device_free(Device *device, DeviceScene *dscene);
-private:
+ /* osl compile and query */
+ static bool osl_compile(const string& inputfile, const string& outputfile);
+ static bool osl_query(OSL::OSLQuery& query, const string& filepath);
+
+ /* shader file loading, all functions return pointer to hash string if found */
+ const char *shader_test_loaded(const string& hash);
+ const char *shader_load_bytecode(const string& hash, const string& bytecode);
+ const char *shader_load_filepath(string filepath);
+
+protected:
+ void texture_system_init();
+ void shading_system_init();
+
OSL::ShadingSystem *ss;
OSL::TextureSystem *ts;
OSLRenderServices *services;
OSL::ErrorHandler errhandler;
+ set<string> loaded_shaders;
};
#endif
@@ -65,10 +81,10 @@ private:
class OSLCompiler {
public:
- OSLCompiler(void *shadingsys);
+ OSLCompiler(void *manager, void *shadingsys);
void compile(OSLGlobals *og, Shader *shader);
- void add(ShaderNode *node, const char *name);
+ void add(ShaderNode *node, const char *name, bool isfilepath = false);
void parameter(const char *name, float f);
void parameter_color(const char *name, float3 f);
@@ -104,6 +120,7 @@ private:
void generate_nodes(const set<ShaderNode*>& nodes);
void *shadingsys;
+ void *manager;
ShaderType current_type;
Shader *current_shader;
};
diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h
index 90ae67eecff..373b3356f51 100644
--- a/intern/cycles/render/shader.h
+++ b/intern/cycles/render/shader.h
@@ -27,6 +27,10 @@
#include "util_string.h"
#include "util_types.h"
+#ifdef WITH_OSL
+#include <OSL/oslexec.h>
+#endif
+
CCL_NAMESPACE_BEGIN
class Device;
@@ -78,6 +82,14 @@ public:
/* determined before compiling */
bool used;
+#ifdef WITH_OSL
+ /* osl shading state references */
+ OSL::ShadingAttribStateRef osl_surface_ref;
+ OSL::ShadingAttribStateRef osl_surface_bump_ref;
+ OSL::ShadingAttribStateRef osl_volume_ref;
+ OSL::ShadingAttribStateRef osl_displacement_ref;
+#endif
+
Shader();
~Shader();
diff --git a/intern/cycles/util/util_path.cpp b/intern/cycles/util/util_path.cpp
index a571fe81118..8cf23bc6a76 100644
--- a/intern/cycles/util/util_path.cpp
+++ b/intern/cycles/util/util_path.cpp
@@ -170,7 +170,7 @@ bool path_read_binary(const string& path, vector<uint8_t>& binary)
return true;
}
-static bool path_read_text(const string& path, string& text)
+bool path_read_text(const string& path, string& text)
{
vector<uint8_t> binary;
@@ -184,6 +184,14 @@ static bool path_read_text(const string& path, string& text)
return true;
}
+uint64_t path_modified_time(const string& path)
+{
+ if(boost::filesystem::exists(path))
+ return (uint64_t)boost::filesystem::last_write_time(path);
+
+ return 0;
+}
+
string path_source_replace_includes(const string& source_, const string& path)
{
/* our own little c preprocessor that replaces #includes with the file
diff --git a/intern/cycles/util/util_path.h b/intern/cycles/util/util_path.h
index 6cba6a3158f..89e4452ecd9 100644
--- a/intern/cycles/util/util_path.h
+++ b/intern/cycles/util/util_path.h
@@ -45,6 +45,9 @@ string path_files_md5_hash(const string& dir);
void path_create_directories(const string& path);
bool path_write_binary(const string& path, const vector<uint8_t>& binary);
bool path_read_binary(const string& path, vector<uint8_t>& binary);
+bool path_read_text(const string& path, string& text);
+
+uint64_t path_modified_time(const string& path);
string path_source_replace_includes(const string& source, const string& path);