
There is one legit place in the code where memcpy was used as an optimization trick. Was needed for older version of GCC, but now it should be re-evaluated and checked if it still helps to have that trick. In other places it's somewhat lazy programming to zero out all object members. That is absolutely unsafe, at the moment when less trivial class is used as a member in that object things will break. Other cases were using memcpy into an object which comes from an external library. We don't control that object, and we can not guarantee it will always be safe for such memory tricks and debugging bugs caused by such low level access is far fun. Ideally we need to use more proper C++, but needs to be done with big care, including benchmarks of each change, For now do annoying but simple cast to void*.
409 lines
12 KiB
C++
409 lines
12 KiB
C++
/*
|
|
* Copyright 2011-2013 Blender Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <OSL/oslexec.h>
|
|
|
|
#include "kernel/kernel_compat_cpu.h"
|
|
#include "kernel/kernel_montecarlo.h"
|
|
#include "kernel/kernel_types.h"
|
|
#include "kernel/split/kernel_split_data_types.h"
|
|
#include "kernel/kernel_globals.h"
|
|
|
|
#include "kernel/geom/geom_object.h"
|
|
|
|
#include "kernel/osl/osl_closures.h"
|
|
#include "kernel/osl/osl_globals.h"
|
|
#include "kernel/osl/osl_services.h"
|
|
#include "kernel/osl/osl_shader.h"
|
|
|
|
#include "util/util_foreach.h"
|
|
|
|
#include "render/attribute.h"
|
|
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
/* Threads */
|
|
|
|
void OSLShader::thread_init(KernelGlobals *kg, KernelGlobals *kernel_globals, OSLGlobals *osl_globals)
|
|
{
|
|
/* no osl used? */
|
|
if(!osl_globals->use) {
|
|
kg->osl = NULL;
|
|
return;
|
|
}
|
|
|
|
/* per thread kernel data init*/
|
|
kg->osl = osl_globals;
|
|
kg->osl->services->thread_init(kernel_globals, osl_globals->ts);
|
|
|
|
OSL::ShadingSystem *ss = kg->osl->ss;
|
|
OSLThreadData *tdata = new OSLThreadData();
|
|
|
|
memset((void *)&tdata->globals, 0, sizeof(OSL::ShaderGlobals));
|
|
tdata->globals.tracedata = &tdata->tracedata;
|
|
tdata->globals.flipHandedness = false;
|
|
tdata->osl_thread_info = ss->create_thread_info();
|
|
tdata->context = ss->get_context(tdata->osl_thread_info);
|
|
|
|
tdata->oiio_thread_info = osl_globals->ts->get_perthread_info();
|
|
|
|
kg->osl_ss = (OSLShadingSystem*)ss;
|
|
kg->osl_tdata = tdata;
|
|
}
|
|
|
|
void OSLShader::thread_free(KernelGlobals *kg)
|
|
{
|
|
if(!kg->osl)
|
|
return;
|
|
|
|
OSL::ShadingSystem *ss = (OSL::ShadingSystem*)kg->osl_ss;
|
|
OSLThreadData *tdata = kg->osl_tdata;
|
|
ss->release_context(tdata->context);
|
|
|
|
ss->destroy_thread_info(tdata->osl_thread_info);
|
|
|
|
delete tdata;
|
|
|
|
kg->osl = NULL;
|
|
kg->osl_ss = NULL;
|
|
kg->osl_tdata = NULL;
|
|
}
|
|
|
|
/* Globals */
|
|
|
|
static void shaderdata_to_shaderglobals(KernelGlobals *kg, ShaderData *sd, PathState *state,
|
|
int path_flag, OSLThreadData *tdata)
|
|
{
|
|
OSL::ShaderGlobals *globals = &tdata->globals;
|
|
|
|
/* copy from shader data to shader globals */
|
|
globals->P = TO_VEC3(sd->P);
|
|
globals->dPdx = TO_VEC3(sd->dP.dx);
|
|
globals->dPdy = TO_VEC3(sd->dP.dy);
|
|
globals->I = TO_VEC3(sd->I);
|
|
globals->dIdx = TO_VEC3(sd->dI.dx);
|
|
globals->dIdy = TO_VEC3(sd->dI.dy);
|
|
globals->N = TO_VEC3(sd->N);
|
|
globals->Ng = TO_VEC3(sd->Ng);
|
|
globals->u = sd->u;
|
|
globals->dudx = sd->du.dx;
|
|
globals->dudy = sd->du.dy;
|
|
globals->v = sd->v;
|
|
globals->dvdx = sd->dv.dx;
|
|
globals->dvdy = sd->dv.dy;
|
|
globals->dPdu = TO_VEC3(sd->dPdu);
|
|
globals->dPdv = TO_VEC3(sd->dPdv);
|
|
globals->surfacearea = (sd->object == OBJECT_NONE) ? 1.0f : object_surface_area(kg, sd->object);
|
|
globals->time = sd->time;
|
|
|
|
/* booleans */
|
|
globals->raytype = path_flag;
|
|
globals->backfacing = (sd->flag & SD_BACKFACING);
|
|
|
|
/* shader data to be used in services callbacks */
|
|
globals->renderstate = sd;
|
|
|
|
/* hacky, we leave it to services to fetch actual object matrix */
|
|
globals->shader2common = sd;
|
|
globals->object2common = sd;
|
|
|
|
/* must be set to NULL before execute */
|
|
globals->Ci = NULL;
|
|
|
|
/* clear trace data */
|
|
tdata->tracedata.init = false;
|
|
|
|
/* used by renderservices */
|
|
sd->osl_globals = kg;
|
|
sd->osl_path_state = state;
|
|
}
|
|
|
|
/* Surface */
|
|
|
|
static void flatten_surface_closure_tree(ShaderData *sd,
|
|
int path_flag,
|
|
const OSL::ClosureColor *closure,
|
|
float3 weight = make_float3(1.0f, 1.0f, 1.0f))
|
|
{
|
|
/* OSL gives us a closure tree, we flatten it into arrays per
|
|
* closure type, for evaluation, sampling, etc later on. */
|
|
|
|
switch(closure->id) {
|
|
case OSL::ClosureColor::MUL: {
|
|
OSL::ClosureMul *mul = (OSL::ClosureMul *)closure;
|
|
flatten_surface_closure_tree(sd, path_flag, mul->closure, TO_FLOAT3(mul->weight) * weight);
|
|
break;
|
|
}
|
|
case OSL::ClosureColor::ADD: {
|
|
OSL::ClosureAdd *add = (OSL::ClosureAdd *)closure;
|
|
flatten_surface_closure_tree(sd, path_flag, add->closureA, weight);
|
|
flatten_surface_closure_tree(sd, path_flag, add->closureB, weight);
|
|
break;
|
|
}
|
|
default: {
|
|
OSL::ClosureComponent *comp = (OSL::ClosureComponent *)closure;
|
|
CClosurePrimitive *prim = (CClosurePrimitive *)comp->data();
|
|
|
|
if(prim) {
|
|
#ifdef OSL_SUPPORTS_WEIGHTED_CLOSURE_COMPONENTS
|
|
weight = weight*TO_FLOAT3(comp->w);
|
|
#endif
|
|
prim->setup(sd, path_flag, weight);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void OSLShader::eval_surface(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag)
|
|
{
|
|
/* setup shader globals from shader data */
|
|
OSLThreadData *tdata = kg->osl_tdata;
|
|
shaderdata_to_shaderglobals(kg, sd, state, path_flag, tdata);
|
|
|
|
/* execute shader for this point */
|
|
OSL::ShadingSystem *ss = (OSL::ShadingSystem*)kg->osl_ss;
|
|
OSL::ShaderGlobals *globals = &tdata->globals;
|
|
OSL::ShadingContext *octx = tdata->context;
|
|
int shader = sd->shader & SHADER_MASK;
|
|
|
|
/* automatic bump shader */
|
|
if(kg->osl->bump_state[shader]) {
|
|
/* save state */
|
|
float3 P = sd->P;
|
|
float3 dPdx = sd->dP.dx;
|
|
float3 dPdy = sd->dP.dy;
|
|
|
|
/* set state as if undisplaced */
|
|
if(sd->flag & SD_HAS_DISPLACEMENT) {
|
|
float data[9];
|
|
bool found = kg->osl->services->get_attribute(sd, true, OSLRenderServices::u_empty, TypeDesc::TypeVector,
|
|
OSLRenderServices::u_geom_undisplaced, data);
|
|
(void)found;
|
|
assert(found);
|
|
|
|
memcpy(&sd->P, data, sizeof(float)*3);
|
|
memcpy(&sd->dP.dx, data+3, sizeof(float)*3);
|
|
memcpy(&sd->dP.dy, data+6, sizeof(float)*3);
|
|
|
|
object_position_transform(kg, sd, &sd->P);
|
|
object_dir_transform(kg, sd, &sd->dP.dx);
|
|
object_dir_transform(kg, sd, &sd->dP.dy);
|
|
|
|
globals->P = TO_VEC3(sd->P);
|
|
globals->dPdx = TO_VEC3(sd->dP.dx);
|
|
globals->dPdy = TO_VEC3(sd->dP.dy);
|
|
}
|
|
|
|
/* execute bump shader */
|
|
ss->execute(octx, *(kg->osl->bump_state[shader]), *globals);
|
|
|
|
/* reset state */
|
|
sd->P = P;
|
|
sd->dP.dx = dPdx;
|
|
sd->dP.dy = dPdy;
|
|
|
|
globals->P = TO_VEC3(P);
|
|
globals->dPdx = TO_VEC3(dPdx);
|
|
globals->dPdy = TO_VEC3(dPdy);
|
|
}
|
|
|
|
/* surface shader */
|
|
if(kg->osl->surface_state[shader]) {
|
|
ss->execute(octx, *(kg->osl->surface_state[shader]), *globals);
|
|
}
|
|
|
|
/* flatten closure tree */
|
|
if(globals->Ci)
|
|
flatten_surface_closure_tree(sd, path_flag, globals->Ci);
|
|
}
|
|
|
|
/* Background */
|
|
|
|
static void flatten_background_closure_tree(ShaderData *sd,
|
|
const OSL::ClosureColor *closure,
|
|
float3 weight = make_float3(1.0f, 1.0f, 1.0f))
|
|
{
|
|
/* OSL gives us a closure tree, if we are shading for background there
|
|
* is only one supported closure type at the moment, which has no evaluation
|
|
* functions, so we just sum the weights */
|
|
|
|
switch(closure->id) {
|
|
case OSL::ClosureColor::MUL: {
|
|
OSL::ClosureMul *mul = (OSL::ClosureMul *)closure;
|
|
flatten_background_closure_tree(sd, mul->closure, weight * TO_FLOAT3(mul->weight));
|
|
break;
|
|
}
|
|
case OSL::ClosureColor::ADD: {
|
|
OSL::ClosureAdd *add = (OSL::ClosureAdd *)closure;
|
|
|
|
flatten_background_closure_tree(sd, add->closureA, weight);
|
|
flatten_background_closure_tree(sd, add->closureB, weight);
|
|
break;
|
|
}
|
|
default: {
|
|
OSL::ClosureComponent *comp = (OSL::ClosureComponent *)closure;
|
|
CClosurePrimitive *prim = (CClosurePrimitive *)comp->data();
|
|
|
|
if(prim) {
|
|
#ifdef OSL_SUPPORTS_WEIGHTED_CLOSURE_COMPONENTS
|
|
weight = weight*TO_FLOAT3(comp->w);
|
|
#endif
|
|
prim->setup(sd, 0, weight);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void OSLShader::eval_background(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag)
|
|
{
|
|
/* setup shader globals from shader data */
|
|
OSLThreadData *tdata = kg->osl_tdata;
|
|
shaderdata_to_shaderglobals(kg, sd, state, path_flag, tdata);
|
|
|
|
/* execute shader for this point */
|
|
OSL::ShadingSystem *ss = (OSL::ShadingSystem*)kg->osl_ss;
|
|
OSL::ShaderGlobals *globals = &tdata->globals;
|
|
OSL::ShadingContext *octx = tdata->context;
|
|
|
|
if(kg->osl->background_state) {
|
|
ss->execute(octx, *(kg->osl->background_state), *globals);
|
|
}
|
|
|
|
/* return background color immediately */
|
|
if(globals->Ci)
|
|
flatten_background_closure_tree(sd, globals->Ci);
|
|
}
|
|
|
|
/* Volume */
|
|
|
|
static void flatten_volume_closure_tree(ShaderData *sd,
|
|
const OSL::ClosureColor *closure,
|
|
float3 weight = make_float3(1.0f, 1.0f, 1.0f))
|
|
{
|
|
/* OSL gives us a closure tree, we flatten it into arrays per
|
|
* closure type, for evaluation, sampling, etc later on. */
|
|
|
|
switch(closure->id) {
|
|
case OSL::ClosureColor::MUL: {
|
|
OSL::ClosureMul *mul = (OSL::ClosureMul *)closure;
|
|
flatten_volume_closure_tree(sd, mul->closure, TO_FLOAT3(mul->weight) * weight);
|
|
break;
|
|
}
|
|
case OSL::ClosureColor::ADD: {
|
|
OSL::ClosureAdd *add = (OSL::ClosureAdd *)closure;
|
|
flatten_volume_closure_tree(sd, add->closureA, weight);
|
|
flatten_volume_closure_tree(sd, add->closureB, weight);
|
|
break;
|
|
}
|
|
default: {
|
|
OSL::ClosureComponent *comp = (OSL::ClosureComponent *)closure;
|
|
CClosurePrimitive *prim = (CClosurePrimitive *)comp->data();
|
|
|
|
if(prim) {
|
|
#ifdef OSL_SUPPORTS_WEIGHTED_CLOSURE_COMPONENTS
|
|
weight = weight*TO_FLOAT3(comp->w);
|
|
#endif
|
|
prim->setup(sd, 0, weight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void OSLShader::eval_volume(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag)
|
|
{
|
|
/* setup shader globals from shader data */
|
|
OSLThreadData *tdata = kg->osl_tdata;
|
|
shaderdata_to_shaderglobals(kg, sd, state, path_flag, tdata);
|
|
|
|
/* execute shader */
|
|
OSL::ShadingSystem *ss = (OSL::ShadingSystem*)kg->osl_ss;
|
|
OSL::ShaderGlobals *globals = &tdata->globals;
|
|
OSL::ShadingContext *octx = tdata->context;
|
|
int shader = sd->shader & SHADER_MASK;
|
|
|
|
if(kg->osl->volume_state[shader]) {
|
|
ss->execute(octx, *(kg->osl->volume_state[shader]), *globals);
|
|
}
|
|
|
|
/* flatten closure tree */
|
|
if(globals->Ci)
|
|
flatten_volume_closure_tree(sd, globals->Ci);
|
|
}
|
|
|
|
/* Displacement */
|
|
|
|
void OSLShader::eval_displacement(KernelGlobals *kg, ShaderData *sd, PathState *state)
|
|
{
|
|
/* setup shader globals from shader data */
|
|
OSLThreadData *tdata = kg->osl_tdata;
|
|
|
|
shaderdata_to_shaderglobals(kg, sd, state, 0, tdata);
|
|
|
|
/* execute shader */
|
|
OSL::ShadingSystem *ss = (OSL::ShadingSystem*)kg->osl_ss;
|
|
OSL::ShaderGlobals *globals = &tdata->globals;
|
|
OSL::ShadingContext *octx = tdata->context;
|
|
int shader = sd->shader & SHADER_MASK;
|
|
|
|
if(kg->osl->displacement_state[shader]) {
|
|
ss->execute(octx, *(kg->osl->displacement_state[shader]), *globals);
|
|
}
|
|
|
|
/* get back position */
|
|
sd->P = TO_FLOAT3(globals->P);
|
|
}
|
|
|
|
/* Attributes */
|
|
|
|
int OSLShader::find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id, AttributeDescriptor *desc)
|
|
{
|
|
/* for OSL, a hash map is used to lookup the attribute by name. */
|
|
int object = sd->object*ATTR_PRIM_TYPES;
|
|
#ifdef __HAIR__
|
|
if(sd->type & PRIMITIVE_ALL_CURVE) object += ATTR_PRIM_CURVE;
|
|
#endif
|
|
|
|
OSLGlobals::AttributeMap &attr_map = kg->osl->attribute_map[object];
|
|
ustring stdname(std::string("geom:") + std::string(Attribute::standard_name((AttributeStandard)id)));
|
|
OSLGlobals::AttributeMap::const_iterator it = attr_map.find(stdname);
|
|
|
|
if(it != attr_map.end()) {
|
|
const OSLGlobals::Attribute &osl_attr = it->second;
|
|
*desc = osl_attr.desc;
|
|
|
|
if(sd->prim == PRIM_NONE && (AttributeElement)osl_attr.desc.element != ATTR_ELEMENT_MESH) {
|
|
desc->offset = ATTR_STD_NOT_FOUND;
|
|
return ATTR_STD_NOT_FOUND;
|
|
}
|
|
|
|
/* return result */
|
|
if(osl_attr.desc.element == ATTR_ELEMENT_NONE) {
|
|
desc->offset = ATTR_STD_NOT_FOUND;
|
|
}
|
|
return desc->offset;
|
|
}
|
|
else {
|
|
desc->offset = ATTR_STD_NOT_FOUND;
|
|
return (int)ATTR_STD_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
CCL_NAMESPACE_END
|
|
|