Compare commits
81 Commits
cycles_tex
...
temp-multi
Author | SHA1 | Date | |
---|---|---|---|
eed93aaa07 | |||
fc0bb6cdee | |||
10f2ad1556 | |||
6d77b87b13 | |||
2101b46802 | |||
34f6765630 | |||
721fad37a1 | |||
c58d1acba8 | |||
0ee79f304e | |||
e642de3d6f | |||
eca5a8b695 | |||
79c79f3c70 | |||
a9970d3cb9 | |||
b812f289f5 | |||
215ce0fb57 | |||
5154598845 | |||
1ee80d792c | |||
95284d2f1e | |||
7281f3eb56 | |||
607ef8f6c5 | |||
1ce640cc0b | |||
7bed18fdb1 | |||
c827b50d40 | |||
3c7e3c8e44 | |||
98e38ce4f3 | |||
132cf268c0 | |||
fd7edc9b05 | |||
ecf7c90840 | |||
d78a530af1 | |||
3ebe61db9f | |||
86c2f139c6 | |||
3596c348eb | |||
41a81474e4 | |||
aa2822d137 | |||
55b333d3e3 | |||
00cfad8578 | |||
1891c956e5 | |||
249c050757 | |||
a0081046b6 | |||
635f73b7f1 | |||
74fcd50e2f | |||
b04a2a7be7 | |||
083671e8ac | |||
78ea401e19 | |||
f3ca987bce | |||
2245add9f8 | |||
31004d7fac | |||
3d3f66ed41 | |||
c9c0195da5 | |||
70c0403858 | |||
8d4de82c7f | |||
22c51c2d51 | |||
158bd7c6a0 | |||
4a28d0b583 | |||
0501e6e693 | |||
8450ac09c1 | |||
1af00015e8 | |||
b44c3a3125 | |||
d729f1ca37 | |||
0fc9f00c14 | |||
6c9b339af7 | |||
b7a976af01 | |||
a689037917 | |||
313403c1f1 | |||
60409b8823 | |||
773dc2ec94 | |||
2a98c5d06b | |||
6d1b4ce3c6 | |||
0b2d961b70 | |||
d553b70470 | |||
6954f2cdd7 | |||
8cc832110a | |||
7b8c54b5a1 | |||
e850d175b5 | |||
326f79d59b | |||
ec4954ece2 | |||
b30e782c82 | |||
e34fe5d28e | |||
8581a062f1 | |||
b43971e5e9 | |||
855382170e |
@@ -808,82 +808,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
|
||||
items=enum_texture_limit
|
||||
)
|
||||
|
||||
use_texture_cache: BoolProperty(
|
||||
name="Use Texture Cache",
|
||||
default=False,
|
||||
description="Enables out-of-core texturing to conserve RAM"
|
||||
)
|
||||
|
||||
texture_cache_size: IntProperty(
|
||||
name="Texture Cache Size (MB)",
|
||||
default=1024,
|
||||
description="The size of the OpenImageIO texture cache in MB",
|
||||
min=0
|
||||
)
|
||||
|
||||
texture_auto_convert: BoolProperty(
|
||||
name="Auto Convert Textures",
|
||||
default=True,
|
||||
description="Automatically convert textures to .tx files for optimal texture cache performance"
|
||||
)
|
||||
|
||||
texture_accept_unmipped: BoolProperty(
|
||||
name="Accept Unmipped",
|
||||
default=True,
|
||||
description="Texture cached rendering without mip mapping is very expensive. Uncheck to prevent Cycles from using textures that are not mip mapped"
|
||||
)
|
||||
|
||||
texture_accept_untiled: BoolProperty(
|
||||
name="Accept Untiled",
|
||||
default=True,
|
||||
description="Texture cached rendering without tiled textures is very expensive. Uncheck to prevent Cycles from using textures that are not tiled"
|
||||
)
|
||||
|
||||
texture_auto_tile: BoolProperty(
|
||||
name="Auto Tile",
|
||||
default=True,
|
||||
description="On the fly creation of tiled versions of textures that are not tiled. This can increase render time but helps reduce memory usage"
|
||||
)
|
||||
|
||||
texture_auto_mip: BoolProperty(
|
||||
name="Auto Mip",
|
||||
default=True,
|
||||
description="On the fly creation of mip maps of textures that are not mip mapped. This can increase render time but helps reduce memory usage"
|
||||
)
|
||||
|
||||
texture_tile_size: IntProperty(
|
||||
name="Tile Size",
|
||||
default=64,
|
||||
description="The size of tiles that Cycles uses for auto tiling"
|
||||
)
|
||||
|
||||
texture_blur_diffuse: FloatProperty(
|
||||
name="Diffuse Blur",
|
||||
default=0.0156,
|
||||
description="The amount of texture blur applied to diffuse bounces",
|
||||
min = 0.0, max = 1.0
|
||||
)
|
||||
|
||||
texture_blur_glossy: FloatProperty(
|
||||
name="Glossy Blur",
|
||||
default=0.0,
|
||||
description="The amount of texture blur applied to glossy bounces",
|
||||
min = 0.0, max = 1.0
|
||||
)
|
||||
|
||||
use_custom_cache_path: BoolProperty(
|
||||
name="Use Custom Cache Path",
|
||||
default=False,
|
||||
description="Use a custom path for the texture cache, as oppoosed to placing cache files next to the original file"
|
||||
)
|
||||
|
||||
custom_cache_path: StringProperty(
|
||||
name="Custom Cache Path",
|
||||
default="",
|
||||
subtype="DIR_PATH",
|
||||
description="Custom path for the texture cache"
|
||||
)
|
||||
|
||||
use_fast_gi: BoolProperty(
|
||||
name="Fast GI Approximation",
|
||||
description="Approximate diffuse indirect light with background tinted ambient occlusion. This provides fast alternative to full global illumination, for interactive viewport rendering or final renders with reduced quality",
|
||||
|
@@ -781,40 +781,6 @@ class CYCLES_RENDER_PT_performance_final_render(CyclesButtonsPanel, Panel):
|
||||
col.prop(rd, "use_save_buffers")
|
||||
col.prop(rd, "use_persistent_data", text="Persistent Data")
|
||||
|
||||
class CYCLES_RENDER_PT_texture_cache(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Texture Cache"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw_header(self, context):
|
||||
cscene = context.scene.cycles
|
||||
|
||||
self.layout.prop(cscene, "use_texture_cache", text="")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
scene = context.scene
|
||||
cscene = scene.cycles
|
||||
rd = scene.render
|
||||
layout.active = cscene.use_texture_cache
|
||||
|
||||
split = layout.split()
|
||||
col = split.column()
|
||||
col.prop(cscene, "texture_auto_convert")
|
||||
col.prop(cscene, "texture_accept_unmipped")
|
||||
col.prop(cscene, "texture_accept_untiled")
|
||||
col.prop(cscene, "texture_auto_mip")
|
||||
col.prop(cscene, "texture_auto_tile")
|
||||
col = split.column()
|
||||
col.prop(cscene, "texture_cache_size")
|
||||
col.prop(cscene, "texture_tile_size")
|
||||
col.prop(cscene, "texture_blur_diffuse")
|
||||
col.prop(cscene, "texture_blur_glossy")
|
||||
row = layout.row()
|
||||
row.prop(cscene, "use_custom_cache_path")
|
||||
row = layout.row()
|
||||
row.active = cscene.use_custom_cache_path
|
||||
row.prop(cscene, "custom_cache_path")
|
||||
|
||||
class CYCLES_RENDER_PT_performance_viewport(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Viewport"
|
||||
@@ -2341,7 +2307,6 @@ classes = (
|
||||
CYCLES_RENDER_PT_performance_acceleration_structure,
|
||||
CYCLES_RENDER_PT_performance_final_render,
|
||||
CYCLES_RENDER_PT_performance_viewport,
|
||||
CYCLES_RENDER_PT_texture_cache,
|
||||
CYCLES_RENDER_PT_passes,
|
||||
CYCLES_RENDER_PT_passes_data,
|
||||
CYCLES_RENDER_PT_passes_light,
|
||||
|
@@ -24,7 +24,6 @@
|
||||
#include "blender/blender_util.h"
|
||||
|
||||
#include "render/denoising.h"
|
||||
#include "render/image_oiio.h"
|
||||
#include "render/merge.h"
|
||||
|
||||
#include "util/util_debug.h"
|
||||
@@ -692,22 +691,6 @@ static PyObject *osl_compile_func(PyObject * /*self*/, PyObject *args)
|
||||
}
|
||||
#endif
|
||||
|
||||
static PyObject *oiio_make_tx(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
const char *inputfile = NULL, *outputfile = NULL, *colorspace = NULL;
|
||||
int extension = EXTENSION_CLIP;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "sssi", &inputfile, &outputfile, &colorspace, &extension))
|
||||
return NULL;
|
||||
|
||||
/* return */
|
||||
if (!OIIOImageLoader::make_tx(
|
||||
inputfile, outputfile, ustring(colorspace), (ExtensionType)extension))
|
||||
Py_RETURN_FALSE;
|
||||
|
||||
Py_RETURN_TRUE;
|
||||
}
|
||||
|
||||
static PyObject *system_info_func(PyObject * /*self*/, PyObject * /*value*/)
|
||||
{
|
||||
string system_info = Device::device_capabilities();
|
||||
@@ -1097,7 +1080,6 @@ static PyMethodDef methods[] = {
|
||||
{"osl_update_node", osl_update_node_func, METH_VARARGS, ""},
|
||||
{"osl_compile", osl_compile_func, METH_VARARGS, ""},
|
||||
#endif
|
||||
{"oiio_make_tx", oiio_make_tx, METH_VARARGS, ""},
|
||||
{"available_devices", available_devices_func, METH_VARARGS, ""},
|
||||
{"system_info", system_info_func, METH_NOARGS, ""},
|
||||
#ifdef WITH_OPENCL
|
||||
|
@@ -802,27 +802,6 @@ SceneParams BlenderSync::get_scene_params(BL::Scene &b_scene, bool background)
|
||||
|
||||
params.bvh_layout = DebugFlags().cpu.bvh_layout;
|
||||
|
||||
params.texture.use_cache = RNA_boolean_get(&cscene, "use_texture_cache");
|
||||
params.texture.cache_size = RNA_int_get(&cscene, "texture_cache_size");
|
||||
params.texture.auto_convert = RNA_boolean_get(&cscene, "texture_auto_convert");
|
||||
params.texture.accept_unmipped = RNA_boolean_get(&cscene, "texture_accept_unmipped");
|
||||
params.texture.accept_untiled = RNA_boolean_get(&cscene, "texture_accept_untiled");
|
||||
params.texture.tile_size = RNA_int_get(&cscene, "texture_tile_size");
|
||||
params.texture.auto_mip = RNA_boolean_get(&cscene, "texture_auto_mip");
|
||||
params.texture.auto_tile = RNA_boolean_get(&cscene, "texture_auto_tile");
|
||||
params.texture.diffuse_blur = RNA_float_get(&cscene, "texture_blur_diffuse");
|
||||
params.texture.glossy_blur = RNA_float_get(&cscene, "texture_blur_glossy");
|
||||
params.texture.use_custom_cache_path = RNA_boolean_get(&cscene, "use_custom_cache_path");
|
||||
if (params.texture.use_custom_cache_path) {
|
||||
char *path = RNA_string_get_alloc(&cscene, "custom_cache_path", NULL, 0);
|
||||
if (path) {
|
||||
params.texture.custom_cache_path = path;
|
||||
MEM_freeN(path);
|
||||
}
|
||||
}
|
||||
else {
|
||||
params.texture.custom_cache_path.clear();
|
||||
}
|
||||
params.background = background;
|
||||
|
||||
return params;
|
||||
|
@@ -376,12 +376,6 @@ class Device {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* open image io, only for CPU device */
|
||||
virtual void *oiio_memory()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* load/compile kernels, must be called before adding tasks */
|
||||
virtual bool load_kernels(const DeviceRequestedFeatures & /*requested_features*/)
|
||||
{
|
||||
|
@@ -39,7 +39,6 @@
|
||||
#include "kernel/kernel_types.h"
|
||||
#include "kernel/split/kernel_split_data.h"
|
||||
#include "kernel/kernel_globals.h"
|
||||
#include "kernel/kernel_oiio_globals.h"
|
||||
#include "kernel/kernel_adaptive_sampling.h"
|
||||
|
||||
#include "kernel/filter/filter.h"
|
||||
@@ -185,9 +184,6 @@ class CPUDevice : public Device {
|
||||
#ifdef WITH_OSL
|
||||
OSLGlobals osl_globals;
|
||||
#endif
|
||||
|
||||
OIIOGlobals oiio_globals;
|
||||
|
||||
#ifdef WITH_OPENIMAGEDENOISE
|
||||
oidn::DeviceRef oidn_device;
|
||||
oidn::FilterRef oidn_filter;
|
||||
@@ -316,9 +312,6 @@ class CPUDevice : public Device {
|
||||
#ifdef WITH_OSL
|
||||
kernel_globals.osl = &osl_globals;
|
||||
#endif
|
||||
oiio_globals.tex_sys = NULL;
|
||||
kernel_globals.oiio = &oiio_globals;
|
||||
|
||||
#ifdef WITH_EMBREE
|
||||
embree_device = rtcNewDevice("verbose=0");
|
||||
#endif
|
||||
@@ -364,12 +357,6 @@ class CPUDevice : public Device {
|
||||
#endif
|
||||
task_pool.cancel();
|
||||
texture_info.free();
|
||||
if (oiio_globals.tex_sys) {
|
||||
VLOG(1) << oiio_globals.tex_sys->getstats();
|
||||
oiio_globals.tex_sys->reset_stats();
|
||||
TextureSystem::destroy(oiio_globals.tex_sys);
|
||||
}
|
||||
kernel_globals.oiio = NULL;
|
||||
}
|
||||
|
||||
virtual bool show_samples() const override
|
||||
@@ -561,11 +548,6 @@ class CPUDevice : public Device {
|
||||
#endif
|
||||
}
|
||||
|
||||
void *oiio_memory() override
|
||||
{
|
||||
return &oiio_globals;
|
||||
}
|
||||
|
||||
void build_bvh(BVH *bvh, Progress &progress, bool refit) override
|
||||
{
|
||||
#ifdef WITH_EMBREE
|
||||
@@ -1504,10 +1486,6 @@ class CPUDevice : public Device {
|
||||
#ifdef WITH_OSL
|
||||
OSLShader::thread_init(&kg, &kernel_globals, &osl_globals);
|
||||
#endif
|
||||
if (kg.oiio && kg.oiio->tex_sys) {
|
||||
kg.oiio_tdata = kg.oiio->tex_sys->get_perthread_info();
|
||||
}
|
||||
|
||||
return kg;
|
||||
}
|
||||
|
||||
|
@@ -231,12 +231,6 @@ device_texture::device_texture(Device *device,
|
||||
data_type = TYPE_UINT16;
|
||||
data_elements = 1;
|
||||
break;
|
||||
case IMAGE_DATA_TYPE_OIIO:
|
||||
/* Assumes 64 bit pointers to be stored as uint. */
|
||||
static_assert(sizeof(void*) == sizeof(uint64_t));
|
||||
data_type = TYPE_UINT64;
|
||||
data_elements = 1;
|
||||
break;
|
||||
case IMAGE_DATA_NUM_TYPES:
|
||||
assert(0);
|
||||
return;
|
||||
|
@@ -79,10 +79,7 @@ struct SocketType {
|
||||
LINK_NORMAL = (1 << 8),
|
||||
LINK_POSITION = (1 << 9),
|
||||
LINK_TANGENT = (1 << 10),
|
||||
LINK_TEXTURE_DX = (1 << 11),
|
||||
LINK_TEXTURE_DY = (1 << 12),
|
||||
DEFAULT_LINK_MASK = (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) |
|
||||
(1 << 10) | (1 << 11) | (1 << 12)
|
||||
DEFAULT_LINK_MASK = (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10)
|
||||
};
|
||||
|
||||
ustring name;
|
||||
|
@@ -211,8 +211,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
|
||||
omega_in,
|
||||
&domega_in->dx,
|
||||
&domega_in->dy,
|
||||
pdf,
|
||||
sd);
|
||||
pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_REFRACTION_ID:
|
||||
label = bsdf_refraction_sample(sc,
|
||||
@@ -226,8 +225,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
|
||||
omega_in,
|
||||
&domega_in->dx,
|
||||
&domega_in->dy,
|
||||
pdf,
|
||||
sd);
|
||||
pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_TRANSPARENT_ID:
|
||||
label = bsdf_transparent_sample(sc,
|
||||
@@ -259,8 +257,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
|
||||
omega_in,
|
||||
&domega_in->dx,
|
||||
&domega_in->dy,
|
||||
pdf,
|
||||
sd);
|
||||
pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID:
|
||||
@@ -294,8 +291,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
|
||||
&domega_in->dx,
|
||||
&domega_in->dy,
|
||||
pdf,
|
||||
&sd->lcg_state,
|
||||
sd);
|
||||
&sd->lcg_state);
|
||||
break;
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
|
||||
@@ -311,8 +307,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
|
||||
omega_in,
|
||||
&domega_in->dx,
|
||||
&domega_in->dy,
|
||||
pdf,
|
||||
sd);
|
||||
pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
|
||||
label = bsdf_ashikhmin_shirley_sample(sc,
|
||||
|
@@ -161,10 +161,8 @@ ccl_device int bsdf_ashikhmin_velvet_sample(const ShaderClosure *sc,
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
// TODO: find a better approximation for the retroreflective bounce
|
||||
*domega_in_dx = (2.0f * dot(N, dIdx)) * N - dIdx;
|
||||
*domega_in_dy = (2.0f * dot(N, dIdy)) * N - dIdy;
|
||||
*domega_in_dx *= 125.0f;
|
||||
*domega_in_dy *= 125.0f;
|
||||
*domega_in_dx = (2 * dot(N, dIdx)) * N - dIdx;
|
||||
*domega_in_dy = (2 * dot(N, dIdy)) * N - dIdy;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
|
@@ -101,10 +101,8 @@ ccl_device int bsdf_diffuse_sample(const ShaderClosure *sc,
|
||||
*eval = make_float3(*pdf, *pdf, *pdf);
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
// TODO: find a better approximation for the diffuse bounce
|
||||
*domega_in_dx = (2.0f * dot(N, dIdx)) * N - dIdx;
|
||||
*domega_in_dy = (2.0f * dot(N, dIdy)) * N - dIdy;
|
||||
*domega_in_dx *= 125.0f;
|
||||
*domega_in_dy *= 125.0f;
|
||||
*domega_in_dx = (2 * dot(N, dIdx)) * N - dIdx;
|
||||
*domega_in_dy = (2 * dot(N, dIdy)) * N - dIdy;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
@@ -165,10 +163,8 @@ ccl_device int bsdf_translucent_sample(const ShaderClosure *sc,
|
||||
*eval = make_float3(*pdf, *pdf, *pdf);
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
// TODO: find a better approximation for the diffuse bounce
|
||||
*domega_in_dx = -((2.0f * dot(N, dIdx)) * N - dIdx);
|
||||
*domega_in_dy = -((2.0f * dot(N, dIdy)) * N - dIdy);
|
||||
*domega_in_dx *= 125.0f;
|
||||
*domega_in_dy *= 125.0f;
|
||||
*domega_in_dx = -((2 * dot(N, dIdx)) * N - dIdx);
|
||||
*domega_in_dy = -((2 * dot(N, dIdy)) * N - dIdy);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
|
@@ -112,10 +112,8 @@ ccl_device int bsdf_diffuse_ramp_sample(const ShaderClosure *sc,
|
||||
if (dot(Ng, *omega_in) > 0.0f) {
|
||||
*eval = bsdf_diffuse_ramp_get_color(bsdf->colors, *pdf * M_PI_F) * M_1_PI_F;
|
||||
# ifdef __RAY_DIFFERENTIALS__
|
||||
*domega_in_dx = (2.0f * dot(N, dIdx)) * N - dIdx;
|
||||
*domega_in_dy = (2.0f * dot(N, dIdy)) * N - dIdy;
|
||||
*domega_in_dx *= 125.0f;
|
||||
*domega_in_dy *= 125.0f;
|
||||
*domega_in_dx = (2 * dot(N, dIdx)) * N - dIdx;
|
||||
*domega_in_dy = (2 * dot(N, dIdy)) * N - dIdy;
|
||||
# endif
|
||||
}
|
||||
else
|
||||
|
@@ -228,10 +228,8 @@ ccl_device int bsdf_hair_reflection_sample(const ShaderClosure *sc,
|
||||
|
||||
// differentials - TODO: find a better approximation for the reflective bounce
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
*domega_in_dx = 2.0f * dot(locy, dIdx) * locy - dIdx;
|
||||
*domega_in_dy = 2.0f * dot(locy, dIdy) * locy - dIdy;
|
||||
*domega_in_dx *= 10.0f;
|
||||
*domega_in_dy *= 10.0f;
|
||||
*domega_in_dx = 2 * dot(locy, dIdx) * locy - dIdx;
|
||||
*domega_in_dy = 2 * dot(locy, dIdy) * locy - dIdy;
|
||||
#endif
|
||||
|
||||
*pdf = fabsf(phi_pdf * theta_pdf);
|
||||
|
@@ -570,8 +570,7 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals *kg,
|
||||
float3 *omega_in,
|
||||
float3 *domega_in_dx,
|
||||
float3 *domega_in_dy,
|
||||
float *pdf,
|
||||
const ShaderData *sd)
|
||||
float *pdf)
|
||||
{
|
||||
const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc;
|
||||
float alpha_x = bsdf->alpha_x;
|
||||
@@ -700,20 +699,8 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals *kg,
|
||||
}
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
# ifdef __DNDU__
|
||||
float3 dwodx = -dIdx;
|
||||
float3 dwody = -dIdy;
|
||||
float dDNdx = dot(dwodx, N) + dot(I, sd->dNdx);
|
||||
float dDNdy = dot(dwody, N) + dot(I, sd->dNdy);
|
||||
*domega_in_dx = dwodx + 2.0f * (dot(I, N) * sd->dNdx + dDNdx * N);
|
||||
*domega_in_dy = dwody + 2.0f * (dot(I, N) * sd->dNdy + dDNdy * N);
|
||||
# else
|
||||
*domega_in_dx = (2.0f * dot(m, dIdx)) * m - dIdx;
|
||||
*domega_in_dy = (2.0f * dot(m, dIdy)) * m - dIdy;
|
||||
# endif
|
||||
const float softness = min(alpha_x, alpha_y) * 10.0f;
|
||||
*domega_in_dx *= (1.0f + softness);
|
||||
*domega_in_dy *= (1.0f + softness);
|
||||
*domega_in_dx = (2 * dot(m, dIdx)) * m - dIdx;
|
||||
*domega_in_dy = (2 * dot(m, dIdy)) * m - dIdy;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -736,10 +723,6 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals *kg,
|
||||
&R,
|
||||
&T,
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
# ifdef __DNDU__
|
||||
sd->dNdx,
|
||||
sd->dNdy,
|
||||
# endif
|
||||
dIdx,
|
||||
dIdy,
|
||||
&dRdx,
|
||||
@@ -755,9 +738,6 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals *kg,
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
*domega_in_dx = dTdx;
|
||||
*domega_in_dy = dTdy;
|
||||
const float softness = min(alpha_x, alpha_y) * 10.0f;
|
||||
*domega_in_dx *= (1.0f + softness);
|
||||
*domega_in_dy *= (1.0f + softness);
|
||||
#endif
|
||||
|
||||
if (alpha_x * alpha_y <= 1e-7f || fabsf(m_eta - 1.0f) < 1e-4f) {
|
||||
@@ -1018,8 +998,7 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals *kg,
|
||||
float3 *omega_in,
|
||||
float3 *domega_in_dx,
|
||||
float3 *domega_in_dy,
|
||||
float *pdf,
|
||||
const ShaderData *sd)
|
||||
float *pdf)
|
||||
{
|
||||
const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc;
|
||||
float alpha_x = bsdf->alpha_x;
|
||||
@@ -1111,20 +1090,8 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals *kg,
|
||||
}
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
# ifdef __DNDU__
|
||||
float3 dwodx = -dIdx;
|
||||
float3 dwody = -dIdy;
|
||||
float dDNdx = dot(dwodx, N) + dot(I, sd->dNdx);
|
||||
float dDNdy = dot(dwody, N) + dot(I, sd->dNdy);
|
||||
*domega_in_dx = dwodx + 2.f * (dot(I, N) * sd->dNdx + dDNdx * N);
|
||||
*domega_in_dy = dwody + 2.f * (dot(I, N) * sd->dNdy + dDNdy * N);
|
||||
# else
|
||||
*domega_in_dx = (2.0f * dot(m, dIdx)) * m - dIdx;
|
||||
*domega_in_dy = (2.0f * dot(m, dIdy)) * m - dIdy;
|
||||
# endif
|
||||
const float softness = min(alpha_x, alpha_y) * 10.0f;
|
||||
*domega_in_dx *= (1.0f + softness);
|
||||
*domega_in_dy *= (1.0f + softness);
|
||||
*domega_in_dx = (2 * dot(m, dIdx)) * m - dIdx;
|
||||
*domega_in_dy = (2 * dot(m, dIdy)) * m - dIdy;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1147,10 +1114,6 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals *kg,
|
||||
&R,
|
||||
&T,
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
# ifdef __DNDU__
|
||||
sd->dNdx,
|
||||
sd->dNdy,
|
||||
# endif
|
||||
dIdx,
|
||||
dIdy,
|
||||
&dRdx,
|
||||
@@ -1166,9 +1129,6 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals *kg,
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
*domega_in_dx = dTdx;
|
||||
*domega_in_dy = dTdy;
|
||||
const float softness = min(alpha_x, alpha_y) * 10.0f;
|
||||
*domega_in_dx *= (1.0f + softness);
|
||||
*domega_in_dy *= (1.0f + softness);
|
||||
#endif
|
||||
|
||||
if (alpha_x * alpha_y <= 1e-7f || fabsf(m_eta - 1.0f) < 1e-4f) {
|
||||
|
@@ -526,11 +526,8 @@ ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals *kg,
|
||||
*omega_in = X * localO.x + Y * localO.y + Z * localO.z;
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
*domega_in_dx = (2.0f * dot(Z, dIdx)) * Z - dIdx;
|
||||
*domega_in_dy = (2.0f * dot(Z, dIdy)) * Z - dIdy;
|
||||
const float softness = min(bsdf->alpha_x, bsdf->alpha_y) * 10.0f;
|
||||
*domega_in_dx *= (1.0f + softness);
|
||||
*domega_in_dy *= (1.0f + softness);
|
||||
*domega_in_dx = (2 * dot(Z, dIdx)) * Z - dIdx;
|
||||
*domega_in_dy = (2 * dot(Z, dIdy)) * Z - dIdy;
|
||||
#endif
|
||||
return LABEL_REFLECT | LABEL_GLOSSY;
|
||||
}
|
||||
@@ -644,8 +641,7 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals *kg,
|
||||
float3 *domega_in_dx,
|
||||
float3 *domega_in_dy,
|
||||
float *pdf,
|
||||
ccl_addr_space uint *lcg_state,
|
||||
const ShaderData *sd)
|
||||
ccl_addr_space uint *lcg_state)
|
||||
{
|
||||
const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc;
|
||||
|
||||
@@ -664,10 +660,6 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals *kg,
|
||||
&R,
|
||||
&T,
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
# ifdef __DNDU__
|
||||
sd->dNdx,
|
||||
sd->dNdy,
|
||||
# endif
|
||||
dIdx,
|
||||
dIdy,
|
||||
&dRdx,
|
||||
@@ -721,9 +713,6 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals *kg,
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
*domega_in_dx = (2 * dot(Z, dIdx)) * Z - dIdx;
|
||||
*domega_in_dy = (2 * dot(Z, dIdy)) * Z - dIdy;
|
||||
const float softness = min(bsdf->alpha_x, bsdf->alpha_y) * 10.0f;
|
||||
*domega_in_dx *= (1.0f + softness);
|
||||
*domega_in_dy *= (1.0f + softness);
|
||||
#endif
|
||||
return LABEL_REFLECT | LABEL_GLOSSY;
|
||||
}
|
||||
@@ -735,9 +724,6 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals *kg,
|
||||
((bsdf->ior - bsdf->ior * bsdf->ior * cosI / dnp) * dot(dIdx, Z)) * Z;
|
||||
*domega_in_dy = -(bsdf->ior * dIdy) +
|
||||
((bsdf->ior - bsdf->ior * bsdf->ior * cosI / dnp) * dot(dIdy, Z)) * Z;
|
||||
const float softness = min(bsdf->alpha_x, bsdf->alpha_y) * 10.0f;
|
||||
*domega_in_dx *= (1.0f + softness);
|
||||
*domega_in_dy *= (1.0f + softness);
|
||||
#endif
|
||||
|
||||
return LABEL_TRANSMIT | LABEL_GLOSSY;
|
||||
|
@@ -116,8 +116,6 @@ ccl_device int bsdf_oren_nayar_sample(const ShaderClosure *sc,
|
||||
// TODO: find a better approximation for the bounce
|
||||
*domega_in_dx = (2.0f * dot(bsdf->N, dIdx)) * bsdf->N - dIdx;
|
||||
*domega_in_dy = (2.0f * dot(bsdf->N, dIdy)) * bsdf->N - dIdy;
|
||||
*domega_in_dx *= 125.0f;
|
||||
*domega_in_dy *= 125.0f;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
|
@@ -123,10 +123,8 @@ ccl_device int bsdf_phong_ramp_sample(const ShaderClosure *sc,
|
||||
float3 R = (2 * cosNO) * bsdf->N - I;
|
||||
|
||||
# ifdef __RAY_DIFFERENTIALS__
|
||||
*domega_in_dx = (2.0f * dot(bsdf->N, dIdx)) * bsdf->N - dIdx;
|
||||
*domega_in_dy = (2.0f * dot(bsdf->N, dIdy)) * bsdf->N - dIdy;
|
||||
*domega_in_dx *= 10.0f;
|
||||
*domega_in_dy *= 10.0f;
|
||||
*domega_in_dx = (2 * dot(bsdf->N, dIdx)) * bsdf->N - dIdx;
|
||||
*domega_in_dy = (2 * dot(bsdf->N, dIdy)) * bsdf->N - dIdy;
|
||||
# endif
|
||||
|
||||
float3 T, B;
|
||||
|
@@ -125,10 +125,8 @@ ccl_device int bsdf_principled_diffuse_sample(const ShaderClosure *sc,
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
// TODO: find a better approximation for the diffuse bounce
|
||||
*domega_in_dx = (2.0f * dot(N, dIdx)) * N - dIdx;
|
||||
*domega_in_dy = (2.0f * dot(N, dIdy)) * N - dIdy;
|
||||
*domega_in_dx *= 125.0f;
|
||||
*domega_in_dy *= 125.0f;
|
||||
*domega_in_dx = -((2 * dot(N, dIdx)) * N - dIdx);
|
||||
*domega_in_dy = -((2 * dot(N, dIdy)) * N - dIdy);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
|
@@ -126,10 +126,8 @@ ccl_device int bsdf_principled_sheen_sample(const ShaderClosure *sc,
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
// TODO: find a better approximation for the diffuse bounce
|
||||
*domega_in_dx = (2.0f * dot(N, dIdx)) * N - dIdx;
|
||||
*domega_in_dy = (2.0f * dot(N, dIdy)) * N - dIdy;
|
||||
*domega_in_dx *= 125.0f;
|
||||
*domega_in_dy *= 125.0f;
|
||||
*domega_in_dx = -((2 * dot(N, dIdx)) * N - dIdx);
|
||||
*domega_in_dy = -((2 * dot(N, dIdy)) * N - dIdy);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
|
@@ -70,8 +70,7 @@ ccl_device int bsdf_reflection_sample(const ShaderClosure *sc,
|
||||
float3 *omega_in,
|
||||
float3 *domega_in_dx,
|
||||
float3 *domega_in_dy,
|
||||
float *pdf,
|
||||
const ShaderData *sd)
|
||||
float *pdf)
|
||||
{
|
||||
const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc;
|
||||
float3 N = bsdf->N;
|
||||
@@ -82,18 +81,8 @@ ccl_device int bsdf_reflection_sample(const ShaderClosure *sc,
|
||||
*omega_in = (2 * cosNO) * N - I;
|
||||
if (dot(Ng, *omega_in) > 0) {
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
# ifdef __DNDU__
|
||||
/* as described in pbrt */
|
||||
float3 dwodx = -dIdx;
|
||||
float3 dwody = -dIdy;
|
||||
float dDNdx = dot(dwodx, N) + dot(I, sd->dNdx);
|
||||
float dDNdy = dot(dwody, N) + dot(I, sd->dNdy);
|
||||
*domega_in_dx = dwodx + 2.f * (dot(I, N) * sd->dNdx + dDNdx * N);
|
||||
*domega_in_dy = dwody + 2.f * (dot(I, N) * sd->dNdy + dDNdy * N);
|
||||
# else
|
||||
*domega_in_dx = 2.0f * dot(N, dIdx) * N - dIdx;
|
||||
*domega_in_dy = 2.0f * dot(N, dIdy) * N - dIdy;
|
||||
# endif
|
||||
*domega_in_dx = 2 * dot(N, dIdx) * N - dIdx;
|
||||
*domega_in_dy = 2 * dot(N, dIdy) * N - dIdy;
|
||||
#endif
|
||||
/* Some high number for MIS. */
|
||||
*pdf = 1e6f;
|
||||
|
@@ -70,8 +70,7 @@ ccl_device int bsdf_refraction_sample(const ShaderClosure *sc,
|
||||
float3 *omega_in,
|
||||
float3 *domega_in_dx,
|
||||
float3 *domega_in_dy,
|
||||
float *pdf,
|
||||
ShaderData *sd)
|
||||
float *pdf)
|
||||
{
|
||||
const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc;
|
||||
float m_eta = bsdf->ior;
|
||||
@@ -89,10 +88,6 @@ ccl_device int bsdf_refraction_sample(const ShaderClosure *sc,
|
||||
&R,
|
||||
&T,
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
# ifdef __DNDU__
|
||||
sd->dNdx,
|
||||
sd->dNdy,
|
||||
# endif
|
||||
dIdx,
|
||||
dIdy,
|
||||
&dRdx,
|
||||
|
@@ -142,8 +142,6 @@ ccl_device int bsdf_diffuse_toon_sample(const ShaderClosure *sc,
|
||||
// TODO: find a better approximation for the bounce
|
||||
*domega_in_dx = (2.0f * dot(bsdf->N, dIdx)) * bsdf->N - dIdx;
|
||||
*domega_in_dy = (2.0f * dot(bsdf->N, dIdy)) * bsdf->N - dIdy;
|
||||
*domega_in_dx *= 125.0f;
|
||||
*domega_in_dy *= 125.0f;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
@@ -235,10 +233,8 @@ ccl_device int bsdf_glossy_toon_sample(const ShaderClosure *sc,
|
||||
*eval = *pdf * bsdf_toon_get_intensity(max_angle, smooth, angle);
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
*domega_in_dx = (2.0f * dot(bsdf->N, dIdx)) * bsdf->N - dIdx;
|
||||
*domega_in_dy = (2.0f * dot(bsdf->N, dIdy)) * bsdf->N - dIdy;
|
||||
*domega_in_dx *= 10.0f;
|
||||
*domega_in_dy *= 10.0f;
|
||||
*domega_in_dx = (2 * dot(bsdf->N, dIdx)) * bsdf->N - dIdx;
|
||||
*domega_in_dy = (2 * dot(bsdf->N, dIdy)) * bsdf->N - dIdy;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
|
@@ -41,10 +41,6 @@ ccl_device float fresnel_dielectric(float eta,
|
||||
float3 *R,
|
||||
float3 *T,
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
# ifdef __DNDU__
|
||||
const float3 dNdx,
|
||||
const float3 dNdy,
|
||||
# endif
|
||||
const float3 dIdx,
|
||||
const float3 dIdy,
|
||||
float3 *dRdx,
|
||||
@@ -73,14 +69,14 @@ ccl_device float fresnel_dielectric(float eta,
|
||||
}
|
||||
|
||||
// compute reflection
|
||||
*R = (2.0f * cos) * Nn - I;
|
||||
*R = (2 * cos) * Nn - I;
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
*dRdx = (2.0f * dot(Nn, dIdx)) * Nn - dIdx;
|
||||
*dRdy = (2.0f * dot(Nn, dIdy)) * Nn - dIdy;
|
||||
*dRdx = (2 * dot(Nn, dIdx)) * Nn - dIdx;
|
||||
*dRdy = (2 * dot(Nn, dIdy)) * Nn - dIdy;
|
||||
#endif
|
||||
|
||||
float arg = 1 - (neta * neta * (1 - (cos * cos)));
|
||||
if (arg < 0.0f) {
|
||||
if (arg < 0) {
|
||||
*T = make_float3(0.0f, 0.0f, 0.0f);
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
*dTdx = make_float3(0.0f, 0.0f, 0.0f);
|
||||
@@ -93,16 +89,8 @@ ccl_device float fresnel_dielectric(float eta,
|
||||
float nK = (neta * cos) - dnp;
|
||||
*T = -(neta * I) + (nK * Nn);
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
# ifndef __DNDU__
|
||||
# define dNdx make_float3(0.0f, 0.0f, 0.0f)
|
||||
# define dNdy make_float3(0.0f, 0.0f, 0.0f)
|
||||
# endif
|
||||
float dDNdx = dot(dIdx, Nn) - dot(I, dNdx);
|
||||
float dDNdy = dot(dIdy, Nn) - dot(I, dNdy);
|
||||
float dmudx = (neta - neta * neta * cos / dnp) * dDNdx;
|
||||
float dmudy = (neta - neta * neta * cos / dnp) * dDNdy;
|
||||
*dTdx = -(neta * dIdx) + (nK * dNdx + dmudx * Nn);
|
||||
*dTdy = -(neta * dIdy) + (nK * dNdy + dmudy * Nn);
|
||||
*dTdx = -(neta * dIdx) + ((neta - neta * neta * cos / dnp) * dot(dIdx, Nn)) * Nn;
|
||||
*dTdy = -(neta * dIdy) + ((neta - neta * neta * cos / dnp) * dot(dIdy, Nn)) * Nn;
|
||||
#endif
|
||||
// compute Fresnel terms
|
||||
float cosTheta1 = cos; // N.R
|
||||
|
@@ -90,15 +90,8 @@ ccl_device float3 volume_henyey_greenstein_eval_phase(const ShaderClosure *sc,
|
||||
return make_float3(*pdf, *pdf, *pdf);
|
||||
}
|
||||
|
||||
ccl_device float3 henyey_greenstrein_sample(float3 D,
|
||||
float g,
|
||||
float randu,
|
||||
float randv,
|
||||
float *pdf,
|
||||
float3 dIdx,
|
||||
float3 dIdy,
|
||||
float3 *domega_in_dx,
|
||||
float3 *domega_in_dy)
|
||||
ccl_device float3
|
||||
henyey_greenstrein_sample(float3 D, float g, float randu, float randv, float *pdf)
|
||||
{
|
||||
/* match pdf for small g */
|
||||
float cos_theta;
|
||||
@@ -120,33 +113,12 @@ ccl_device float3 henyey_greenstrein_sample(float3 D,
|
||||
|
||||
float sin_theta = safe_sqrtf(1.0f - cos_theta * cos_theta);
|
||||
float phi = M_2PI_F * randv;
|
||||
float cos_phi = cosf(phi);
|
||||
float sin_phi = sinf(phi);
|
||||
float3 dir = make_float3(sin_theta * cos_phi, sin_theta * sin_phi, cos_theta);
|
||||
float3 dir = make_float3(sin_theta * cosf(phi), sin_theta * sinf(phi), cos_theta);
|
||||
|
||||
float3 T, B;
|
||||
make_orthonormals(D, &T, &B);
|
||||
dir = dir.x * T + dir.y * B + dir.z * D;
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
if (domega_in_dx && domega_in_dy) {
|
||||
if (pdf && *pdf < 1.0f) {
|
||||
float spread = 0.125f / sqrtf(*pdf);
|
||||
make_orthonormals(dir, &T, &B);
|
||||
*domega_in_dx = spread * T;
|
||||
*domega_in_dy = spread * B;
|
||||
}
|
||||
else {
|
||||
make_orthonormals(D - dIdx, &T, &B);
|
||||
*domega_in_dx = sin_theta * cos_phi * T + sin_theta * sin_phi * B + cos_theta * (D - dIdx) -
|
||||
dir;
|
||||
make_orthonormals(D - dIdy, &T, &B);
|
||||
*domega_in_dy = sin_theta * cos_phi * T + sin_theta * sin_phi * B + cos_theta * (D - dIdy) -
|
||||
dir;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
@@ -166,10 +138,15 @@ ccl_device int volume_henyey_greenstein_sample(const ShaderClosure *sc,
|
||||
float g = volume->g;
|
||||
|
||||
/* note that I points towards the viewer and so is used negated */
|
||||
*omega_in = henyey_greenstrein_sample(
|
||||
-I, g, randu, randv, pdf, dIdx, dIdy, domega_in_dx, domega_in_dy);
|
||||
*omega_in = henyey_greenstrein_sample(-I, g, randu, randv, pdf);
|
||||
*eval = make_float3(*pdf, *pdf, *pdf); /* perfect importance sampling */
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
/* todo: implement ray differential estimation */
|
||||
*domega_in_dx = make_float3(0.0f, 0.0f, 0.0f);
|
||||
*domega_in_dy = make_float3(0.0f, 0.0f, 0.0f);
|
||||
#endif
|
||||
|
||||
return LABEL_VOLUME_SCATTER;
|
||||
}
|
||||
|
||||
|
@@ -778,10 +778,6 @@ ccl_device_inline void curve_shader_setup(KernelGlobals *kg,
|
||||
sd->dPdu = dPdu;
|
||||
sd->dPdv = cross(dPdu, sd->Ng);
|
||||
# endif
|
||||
# ifdef __DNDU__
|
||||
sd->dNdx = make_float3(0.0f, 0.0f, 0.0f);
|
||||
sd->dNdy = make_float3(0.0f, 0.0f, 0.0f);
|
||||
# endif
|
||||
|
||||
if (isect->object != OBJECT_NONE) {
|
||||
# ifdef __OBJECT_MOTION__
|
||||
|
@@ -85,7 +85,6 @@ ccl_device_noinline void motion_triangle_shader_setup(
|
||||
sd->dPdu = (verts[0] - verts[2]);
|
||||
sd->dPdv = (verts[1] - verts[2]);
|
||||
#endif
|
||||
|
||||
/* Compute smooth normal. */
|
||||
if (sd->shader & SHADER_SMOOTH_NORMAL) {
|
||||
/* Find attribute. */
|
||||
|
@@ -139,22 +139,6 @@ ccl_device_inline void triangle_dPdudv(KernelGlobals *kg,
|
||||
*dPdv = (p1 - p2);
|
||||
}
|
||||
|
||||
ccl_device_inline void triangle_dNdudv(KernelGlobals *kg,
|
||||
int prim,
|
||||
ccl_addr_space float3 *dNdu,
|
||||
ccl_addr_space float3 *dNdv)
|
||||
{
|
||||
/* load triangle vertices */
|
||||
const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim);
|
||||
float3 n0 = float4_to_float3(kernel_tex_fetch(__tri_vnormal, tri_vindex.x));
|
||||
float3 n1 = float4_to_float3(kernel_tex_fetch(__tri_vnormal, tri_vindex.y));
|
||||
float3 n2 = float4_to_float3(kernel_tex_fetch(__tri_vnormal, tri_vindex.z));
|
||||
|
||||
/* compute derivatives of N w.r.t. uv */
|
||||
*dNdu = (n0 - n2);
|
||||
*dNdv = (n1 - n2);
|
||||
}
|
||||
|
||||
/* Reading attributes on various triangle elements */
|
||||
|
||||
ccl_device float triangle_attribute_float(
|
||||
|
@@ -283,7 +283,6 @@ ccl_device void kernel_bake_evaluate(
|
||||
P,
|
||||
Ng,
|
||||
Ng,
|
||||
NULL,
|
||||
shader,
|
||||
object,
|
||||
prim,
|
||||
|
@@ -36,23 +36,6 @@ ccl_device void differential_transfer(ccl_addr_space differential3 *dP_,
|
||||
dP_->dy = tmpy - dot(tmpy, Ng) * tmp;
|
||||
}
|
||||
|
||||
ccl_device void differential_reflect(differential3 *dD_,
|
||||
float3 D,
|
||||
const ccl_addr_space differential3 *dD,
|
||||
float3 N,
|
||||
const differential3 *dN)
|
||||
{
|
||||
/* ray differential transfer through homogeneous medium, to
|
||||
* compute dPdx/dy at a shading point from the incoming ray */
|
||||
|
||||
const float dotDN = dot(D, N);
|
||||
const float3 tmpx = N * (dot(dD->dx, N) + dot(D, dN->dx));
|
||||
const float3 tmpy = N * (dot(dD->dy, N) + dot(D, dN->dy));
|
||||
|
||||
dD_->dx = dD->dx - 2.0f * (dotDN * dN->dx + tmpx);
|
||||
dD_->dy = dD->dy - 2.0f * (dotDN * dN->dy + tmpy);
|
||||
}
|
||||
|
||||
ccl_device void differential_incoming(ccl_addr_space differential3 *dI, const differential3 dD)
|
||||
{
|
||||
/* compute dIdx/dy at a shading point, we just need to negate the
|
||||
|
@@ -57,7 +57,6 @@ ccl_device_noinline_cpu float3 direct_emissive_eval(KernelGlobals *kg,
|
||||
ls->P,
|
||||
ls->Ng,
|
||||
I,
|
||||
&dI,
|
||||
ls->shader,
|
||||
ls->object,
|
||||
ls->prim,
|
||||
@@ -112,21 +111,10 @@ ccl_device_noinline_cpu bool direct_emission(KernelGlobals *kg,
|
||||
if (ls->pdf == 0.0f)
|
||||
return false;
|
||||
|
||||
differential3 dD;
|
||||
differential3 dN;
|
||||
#ifdef __DNDU__
|
||||
dN.dx = sd->dNdx;
|
||||
dN.dy = sd->dNdy;
|
||||
#else
|
||||
dN = differential3_zero();
|
||||
#endif
|
||||
/* This is how differentials are calculated for a perfect specular reflection.
|
||||
* This is not the exact value that we should be getting here,
|
||||
* but it's still better than using zero differentials. */
|
||||
differential_reflect(&dD, sd->I, &sd->dI, sd->N, &dN);
|
||||
/* todo: implement */
|
||||
differential3 dD = differential3_zero();
|
||||
|
||||
/* evaluate closure */
|
||||
emission_sd->dP = sd->dP;
|
||||
|
||||
float3 light_eval = direct_emissive_eval(
|
||||
kg, emission_sd, ls, state, -ls->D, dD, ls->t, sd->time);
|
||||
|
@@ -44,9 +44,6 @@ struct OSLGlobals;
|
||||
struct OSLThreadData;
|
||||
struct OSLShadingSystem;
|
||||
# endif
|
||||
# ifdef __OIIO__
|
||||
struct OIIOGlobals;
|
||||
# endif
|
||||
|
||||
typedef unordered_map<float, float> CoverageMap;
|
||||
|
||||
@@ -67,11 +64,6 @@ typedef struct KernelGlobals {
|
||||
OSLThreadData *osl_tdata;
|
||||
# endif
|
||||
|
||||
# ifdef __OIIO__
|
||||
OIIOGlobals *oiio;
|
||||
void *oiio_tdata;
|
||||
# endif
|
||||
|
||||
/* **** Run-time data **** */
|
||||
|
||||
/* Heap-allocated storage for transparent shadows intersections. */
|
||||
|
@@ -333,33 +333,6 @@ ccl_device bool lamp_light_eval(
|
||||
return true;
|
||||
}
|
||||
|
||||
ccl_device void lamp_light_dPdudv(KernelGlobals *kg,
|
||||
int lamp,
|
||||
float u,
|
||||
float v,
|
||||
ccl_addr_space float3 *dPdu,
|
||||
ccl_addr_space float3 *dPdv)
|
||||
{
|
||||
const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp);
|
||||
LightType type = (LightType)klight->type;
|
||||
|
||||
switch (type) {
|
||||
case LIGHT_AREA: {
|
||||
*dPdu = make_float3(klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]);
|
||||
*dPdv = make_float3(klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]);
|
||||
break;
|
||||
}
|
||||
case LIGHT_POINT:
|
||||
case LIGHT_DISTANT:
|
||||
case LIGHT_SPOT:
|
||||
default:
|
||||
// TODO (Stefan)
|
||||
*dPdu = make_float3(0.0f, 0.0f, 0.0f);
|
||||
*dPdv = make_float3(0.0f, 0.0f, 0.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Triangle Light */
|
||||
|
||||
/* returns true if the triangle is has motion blur or an instancing transform applied */
|
||||
|
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2017 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.
|
||||
*/
|
||||
|
||||
#ifndef __KERNEL_OIIO_GLOBALS_H__
|
||||
#define __KERNEL_OIIO_GLOBALS_H__
|
||||
|
||||
#include "util/util_thread.h"
|
||||
#include "util/util_vector.h"
|
||||
#include <OpenImageIO/texture.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct OIIOTexture {
|
||||
OIIO::TextureSystem::TextureHandle *handle;
|
||||
OIIO::TextureOpt::InterpMode interpolation;
|
||||
OIIO::TextureOpt::Wrap extension;
|
||||
bool is_linear;
|
||||
};
|
||||
|
||||
struct OIIOGlobals {
|
||||
OIIO::TextureSystem *tex_sys;
|
||||
float diffuse_blur;
|
||||
float glossy_blur;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif
|
@@ -30,8 +30,8 @@
|
||||
|
||||
#include "kernel/kernel_write_passes.h"
|
||||
#include "kernel/kernel_accumulate.h"
|
||||
#include "kernel/kernel_light.h"
|
||||
#include "kernel/kernel_shader.h"
|
||||
#include "kernel/kernel_light.h"
|
||||
#include "kernel/kernel_adaptive_sampling.h"
|
||||
#include "kernel/kernel_passes.h"
|
||||
|
||||
@@ -354,14 +354,8 @@ ccl_device_noinline
|
||||
light_ray.D = ao_D;
|
||||
light_ray.t = kernel_data.background.ao_distance;
|
||||
light_ray.time = sd->time;
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
light_ray.dP = sd->dP;
|
||||
/* This is how pbrt v3 implements differentials for diffuse bounces */
|
||||
float3 a, b;
|
||||
make_orthonormals(ao_D, &a, &b);
|
||||
light_ray.dD.dx = normalize(ao_D + 0.1f * a);
|
||||
light_ray.dD.dy = normalize(ao_D + 0.1f * b);
|
||||
#endif /* __RAY_DIFFERENTIALS__ */
|
||||
light_ray.dD = differential3_zero();
|
||||
|
||||
if (!shadow_blocked(kg, sd, emission_sd, state, &light_ray, &ao_shadow)) {
|
||||
path_radiance_accum_ao(kg, L, state, throughput, ao_alpha, ao_bsdf, ao_shadow);
|
||||
|
@@ -50,14 +50,8 @@ ccl_device_inline void kernel_branched_path_ao(KernelGlobals *kg,
|
||||
light_ray.D = ao_D;
|
||||
light_ray.t = kernel_data.background.ao_distance;
|
||||
light_ray.time = sd->time;
|
||||
# ifdef __RAY_DIFFERENTIALS__
|
||||
light_ray.dP = sd->dP;
|
||||
/* This is how pbrt v3 implements differentials for diffuse bounces */
|
||||
float3 a, b;
|
||||
make_orthonormals(ao_D, &a, &b);
|
||||
light_ray.dD.dx = normalize(ao_D + 0.1f * a);
|
||||
light_ray.dD.dy = normalize(ao_D + 0.1f * b);
|
||||
# endif /* __RAY_DIFFERENTIALS__ */
|
||||
light_ray.dD = differential3_zero();
|
||||
|
||||
if (!shadow_blocked(kg, sd, emission_sd, state, &light_ray, &ao_shadow)) {
|
||||
path_radiance_accum_ao(
|
||||
|
@@ -146,34 +146,6 @@ ccl_device_noinline
|
||||
differential_transfer(&sd->dP, ray->dP, ray->D, ray->dD, sd->Ng, isect->t);
|
||||
differential_incoming(&sd->dI, ray->dD);
|
||||
differential_dudv(&sd->du, &sd->dv, sd->dPdu, sd->dPdv, sd->dP, sd->Ng);
|
||||
# ifdef __DNDU__
|
||||
if (sd->shader & SHADER_SMOOTH_NORMAL && sd->type & PRIMITIVE_TRIANGLE) {
|
||||
// TODO stefan curves
|
||||
/* dNdu/dNdv */
|
||||
float3 dNdu, dNdv;
|
||||
triangle_dNdudv(kg, sd->prim, &dNdu, &dNdv);
|
||||
sd->dNdx = dNdu * sd->du.dx + dNdv * sd->dv.dx;
|
||||
sd->dNdy = dNdu * sd->du.dy + dNdv * sd->dv.dy;
|
||||
|
||||
/* backfacing test */
|
||||
bool backfacing = (dot(sd->Ng, sd->I) < 0.0f);
|
||||
if (backfacing) {
|
||||
sd->dNdx = -sd->dNdx;
|
||||
sd->dNdy = -sd->dNdy;
|
||||
}
|
||||
# ifdef __INSTANCING__
|
||||
if (isect->object != OBJECT_NONE) {
|
||||
/* instance transform */
|
||||
object_dir_transform_auto(kg, sd, &sd->dNdx);
|
||||
object_dir_transform_auto(kg, sd, &sd->dNdy);
|
||||
}
|
||||
# endif /* __INSTANCING__ */
|
||||
}
|
||||
else {
|
||||
sd->dNdx = make_float3(0.0f, 0.0f, 0.0f);
|
||||
sd->dNdy = make_float3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
# endif /* __DNDU__ */
|
||||
#endif
|
||||
|
||||
PROFILING_SHADER(sd->shader);
|
||||
@@ -207,11 +179,6 @@ ccl_device_inline
|
||||
sd->u = isect->u;
|
||||
sd->v = isect->v;
|
||||
|
||||
# ifdef __DNDU__
|
||||
sd->dNdx = make_float3(0.0f, 0.0f, 0.0f);
|
||||
sd->dNdy = make_float3(0.0f, 0.0f, 0.0f);
|
||||
# endif
|
||||
|
||||
/* fetch triangle data */
|
||||
if (sd->type == PRIMITIVE_TRIANGLE) {
|
||||
float3 Ng = triangle_normal(kg, sd);
|
||||
@@ -228,15 +195,6 @@ ccl_device_inline
|
||||
# ifdef __DPDU__
|
||||
/* dPdu/dPdv */
|
||||
triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv);
|
||||
# endif
|
||||
# ifdef __DNDU__
|
||||
/* dNdu/dNdv */
|
||||
if (sd->shader & SHADER_SMOOTH_NORMAL && sd->type & PRIMITIVE_TRIANGLE) {
|
||||
float3 dNdu, dNdv;
|
||||
triangle_dNdudv(kg, sd->prim, &dNdu, &dNdv);
|
||||
sd->dNdx = dNdu * sd->du.dx + dNdv * sd->dv.dx;
|
||||
sd->dNdy = dNdu * sd->du.dy + dNdv * sd->dv.dy;
|
||||
}
|
||||
# endif
|
||||
}
|
||||
else {
|
||||
@@ -253,10 +211,6 @@ ccl_device_inline
|
||||
# ifdef __DPDU__
|
||||
object_dir_transform_auto(kg, sd, &sd->dPdu);
|
||||
object_dir_transform_auto(kg, sd, &sd->dPdv);
|
||||
# endif
|
||||
# ifdef __DNDU__
|
||||
object_dir_transform(kg, sd, &sd->dNdx);
|
||||
object_dir_transform(kg, sd, &sd->dNdy);
|
||||
# endif
|
||||
}
|
||||
|
||||
@@ -268,10 +222,6 @@ ccl_device_inline
|
||||
# ifdef __DPDU__
|
||||
sd->dPdu = -sd->dPdu;
|
||||
sd->dPdv = -sd->dPdv;
|
||||
# endif
|
||||
# ifdef __DNDU__
|
||||
sd->dNdx = -sd->dNdx;
|
||||
sd->dNdy = -sd->dNdy;
|
||||
# endif
|
||||
}
|
||||
|
||||
@@ -296,7 +246,6 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg,
|
||||
const float3 P,
|
||||
const float3 Ng,
|
||||
const float3 I,
|
||||
const differential3 *dI,
|
||||
int shader,
|
||||
int object,
|
||||
int prim,
|
||||
@@ -377,39 +326,12 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg,
|
||||
object_dir_transform_auto(kg, sd, &sd->dPdu);
|
||||
object_dir_transform_auto(kg, sd, &sd->dPdv);
|
||||
}
|
||||
#endif
|
||||
#ifdef __DNDU__
|
||||
|
||||
float3 dNdu, dNdv;
|
||||
triangle_dNdudv(kg, sd->prim, &dNdu, &dNdv);
|
||||
sd->dNdx = dNdu * sd->du.dx + dNdv * sd->dv.dx;
|
||||
sd->dNdy = dNdu * sd->du.dy + dNdv * sd->dv.dy;
|
||||
|
||||
# ifdef __INSTANCING__
|
||||
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
object_normal_transform_auto(kg, sd, &sd->dNdx);
|
||||
object_normal_transform_auto(kg, sd, &sd->dNdy);
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
else if (sd->type & PRIMITIVE_LAMP) {
|
||||
#ifdef __DPDU__
|
||||
lamp_light_dPdudv(kg, lamp, sd->u, sd->v, &sd->dPdu, &sd->dPdv);
|
||||
#endif
|
||||
#ifdef __DNDU__
|
||||
sd->dNdx = make_float3(0.0f, 0.0f, 0.0f);
|
||||
sd->dNdy = make_float3(0.0f, 0.0f, 0.0f);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#ifdef __DPDU__
|
||||
sd->dPdu = zero_float3();
|
||||
sd->dPdv = zero_float3();
|
||||
#endif
|
||||
#ifdef __DNDU__
|
||||
sd->dNdx = make_float3(0.0f, 0.0f, 0.0f);
|
||||
sd->dNdy = make_float3(0.0f, 0.0f, 0.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -424,27 +346,16 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg,
|
||||
#ifdef __DPDU__
|
||||
sd->dPdu = -sd->dPdu;
|
||||
sd->dPdv = -sd->dPdv;
|
||||
#endif
|
||||
#ifdef __DNDU__
|
||||
sd->dNdx = -sd->dNdx;
|
||||
sd->dNdx = -sd->dNdx;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
if (dI) {
|
||||
sd->dI = *dI;
|
||||
differential_transfer(&sd->dP, sd->dP, I, *dI, Ng, t);
|
||||
differential_dudv(&sd->du, &sd->dv, sd->dPdu, sd->dPdv, sd->dP, sd->Ng);
|
||||
}
|
||||
else {
|
||||
sd->dP = differential3_zero();
|
||||
sd->dI = differential3_zero();
|
||||
sd->du = differential_zero();
|
||||
sd->dv = differential_zero();
|
||||
}
|
||||
|
||||
/* no ray differentials here yet */
|
||||
sd->dP = differential3_zero();
|
||||
sd->dI = differential3_zero();
|
||||
sd->du = differential_zero();
|
||||
sd->dv = differential_zero();
|
||||
#endif
|
||||
|
||||
PROFILING_SHADER(sd->shader);
|
||||
@@ -460,32 +371,25 @@ ccl_device void shader_setup_from_displace(
|
||||
int shader;
|
||||
|
||||
triangle_point_normal(kg, object, prim, u, v, &P, &Ng, &shader);
|
||||
triangle_dPdudv(kg, prim, &sd->dP.dx, &sd->dP.dy);
|
||||
|
||||
/* force smooth shading for displacement */
|
||||
shader |= SHADER_SMOOTH_NORMAL;
|
||||
|
||||
#if 0
|
||||
/* TODO Stefan - need differentials here that don't break the unfiltered case */
|
||||
I = -Ng;
|
||||
differential3 dI = differential3_zero();
|
||||
|
||||
shader_setup_from_sample(kg, sd,
|
||||
P, Ng, I, &dI,
|
||||
#else
|
||||
|
||||
shader_setup_from_sample(
|
||||
kg,
|
||||
sd,
|
||||
P,
|
||||
Ng,
|
||||
I,
|
||||
NULL,
|
||||
#endif
|
||||
shader, object, prim,
|
||||
u, v, 0.0f, 0.5f,
|
||||
!(kernel_tex_fetch(__object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED),
|
||||
LAMP_NONE);
|
||||
shader,
|
||||
object,
|
||||
prim,
|
||||
u,
|
||||
v,
|
||||
0.0f,
|
||||
0.5f,
|
||||
!(kernel_tex_fetch(__object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED),
|
||||
LAMP_NONE);
|
||||
}
|
||||
|
||||
/* ShaderData setup from ray into background */
|
||||
@@ -518,10 +422,6 @@ ccl_device_inline void shader_setup_from_background(KernelGlobals *kg,
|
||||
sd->dPdu = zero_float3();
|
||||
sd->dPdv = zero_float3();
|
||||
#endif
|
||||
#ifdef __DNDU__
|
||||
sd->dNdx = make_float3(0.0f, 0.0f, 0.0f);
|
||||
sd->dNdy = make_float3(0.0f, 0.0f, 0.0f);
|
||||
#endif
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
/* differentials */
|
||||
@@ -569,17 +469,11 @@ ccl_device_inline void shader_setup_from_volume(KernelGlobals *kg, ShaderData *s
|
||||
sd->dPdu = zero_float3();
|
||||
sd->dPdv = zero_float3();
|
||||
# endif
|
||||
# ifdef __DNDU__
|
||||
/* dNdu/dNdv */
|
||||
sd->dNdx = make_float3(0.0f, 0.0f, 0.0f);
|
||||
sd->dNdy = make_float3(0.0f, 0.0f, 0.0f);
|
||||
# endif
|
||||
|
||||
# ifdef __RAY_DIFFERENTIALS__
|
||||
/* differentials */
|
||||
sd->dP.dx = ray->dP.dx + ray->t * ray->dD.dx;
|
||||
sd->dP.dy = ray->dP.dy + ray->t * ray->dD.dy;
|
||||
differential_incoming(&sd->dI, ray->dD);
|
||||
sd->dP = ray->dD;
|
||||
differential_incoming(&sd->dI, sd->dP);
|
||||
sd->du = differential_zero();
|
||||
sd->dv = differential_zero();
|
||||
# endif
|
||||
|
@@ -558,7 +558,6 @@ ccl_device_noinline
|
||||
/* Sample scattering direction. */
|
||||
float scatter_u, scatter_v;
|
||||
path_state_rng_2D(kg, state, PRNG_BSDF_U, &scatter_u, &scatter_v);
|
||||
|
||||
float cos_theta;
|
||||
if (guided) {
|
||||
cos_theta = sample_phase_dwivedi(diffusion_length, phase_log, scatter_u);
|
||||
|
@@ -124,8 +124,6 @@ CCL_NAMESPACE_BEGIN
|
||||
# endif
|
||||
# define __VOLUME_DECOUPLED__
|
||||
# define __VOLUME_RECORD_ALL__
|
||||
# define __DNDU__
|
||||
# define __OIIO__
|
||||
#endif /* __KERNEL_CPU__ */
|
||||
|
||||
#ifdef __KERNEL_CUDA__
|
||||
@@ -955,11 +953,6 @@ typedef ccl_addr_space struct ccl_align(16) ShaderData
|
||||
float3 dPdu;
|
||||
float3 dPdv;
|
||||
#endif
|
||||
#ifdef __DNDU__
|
||||
/* differential of N w.r.t. x and y. */
|
||||
float3 dNdx;
|
||||
float3 dNdy;
|
||||
#endif
|
||||
|
||||
#ifdef __OBJECT_MOTION__
|
||||
/* object <-> world space transformations, cached to avoid
|
||||
|
@@ -34,8 +34,6 @@
|
||||
# endif
|
||||
#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX */
|
||||
|
||||
#define OIIO_NO_AVX 1
|
||||
|
||||
#include "kernel/filter/filter.h"
|
||||
#define KERNEL_ARCH cpu_avx
|
||||
#include "kernel/kernels/cpu/filter_cpu_impl.h"
|
||||
|
@@ -35,8 +35,6 @@
|
||||
# endif
|
||||
#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 */
|
||||
|
||||
#define OIIO_NO_AVX 1
|
||||
|
||||
#include "kernel/filter/filter.h"
|
||||
#define KERNEL_ARCH cpu_avx2
|
||||
#include "kernel/kernels/cpu/filter_cpu_impl.h"
|
||||
|
@@ -34,8 +34,6 @@
|
||||
# endif
|
||||
#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX */
|
||||
|
||||
#define OIIO_NO_AVX 1
|
||||
|
||||
#include "kernel/kernel.h"
|
||||
#define KERNEL_ARCH cpu_avx
|
||||
#include "kernel/kernels/cpu/kernel_cpu_impl.h"
|
||||
|
@@ -35,8 +35,6 @@
|
||||
# endif
|
||||
#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 */
|
||||
|
||||
#define OIIO_NO_AVX 1
|
||||
|
||||
#include "kernel/kernel.h"
|
||||
#define KERNEL_ARCH cpu_avx2
|
||||
#include "kernel/kernels/cpu/kernel_cpu_impl.h"
|
||||
|
@@ -23,14 +23,6 @@
|
||||
# include <nanovdb/util/SampleFromVoxels.h>
|
||||
#endif
|
||||
|
||||
#ifdef __OIIO__
|
||||
# include "kernel/kernel_oiio_globals.h"
|
||||
# define NEAREST_LOOKUP_PATHS \
|
||||
(PATH_RAY_DIFFUSE | PATH_RAY_SHADOW | PATH_RAY_DIFFUSE_ANCESTOR | PATH_RAY_VOLUME_SCATTER | \
|
||||
PATH_RAY_GLOSSY | PATH_RAY_EMISSION)
|
||||
# define DIFFUSE_BLUR_PATHS (PATH_RAY_DIFFUSE | PATH_RAY_DIFFUSE_ANCESTOR)
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Make template functions private so symbols don't conflict between kernels with different
|
||||
@@ -592,126 +584,32 @@ template<typename T> struct NanoVDBInterpolator {
|
||||
|
||||
#undef SET_CUBIC_SPLINE_WEIGHTS
|
||||
|
||||
ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, float y, differential ds, differential dt, uint path_flag)
|
||||
ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, float y)
|
||||
{
|
||||
const TextureInfo &info = kernel_tex_fetch(__texture_info, id);
|
||||
float4 r = make_float4(TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A);
|
||||
|
||||
switch (info.data_type) {
|
||||
case IMAGE_DATA_TYPE_HALF:
|
||||
r = TextureInterpolator<half>::interp(info, x, y);
|
||||
break;
|
||||
return TextureInterpolator<half>::interp(info, x, y);
|
||||
case IMAGE_DATA_TYPE_BYTE:
|
||||
r = TextureInterpolator<uchar>::interp(info, x, y);
|
||||
break;
|
||||
return TextureInterpolator<uchar>::interp(info, x, y);
|
||||
case IMAGE_DATA_TYPE_USHORT:
|
||||
r = TextureInterpolator<uint16_t>::interp(info, x, y);
|
||||
break;
|
||||
return TextureInterpolator<uint16_t>::interp(info, x, y);
|
||||
case IMAGE_DATA_TYPE_FLOAT:
|
||||
r = TextureInterpolator<float>::interp(info, x, y);
|
||||
break;
|
||||
return TextureInterpolator<float>::interp(info, x, y);
|
||||
case IMAGE_DATA_TYPE_HALF4:
|
||||
r = TextureInterpolator<half4>::interp(info, x, y);
|
||||
break;
|
||||
return TextureInterpolator<half4>::interp(info, x, y);
|
||||
case IMAGE_DATA_TYPE_BYTE4:
|
||||
r = TextureInterpolator<uchar4>::interp(info, x, y);
|
||||
break;
|
||||
return TextureInterpolator<uchar4>::interp(info, x, y);
|
||||
case IMAGE_DATA_TYPE_USHORT4:
|
||||
r = TextureInterpolator<ushort4>::interp(info, x, y);
|
||||
break;
|
||||
return TextureInterpolator<ushort4>::interp(info, x, y);
|
||||
case IMAGE_DATA_TYPE_FLOAT4:
|
||||
r = TextureInterpolator<float4>::interp(info, x, y);
|
||||
break;
|
||||
case IMAGE_DATA_TYPE_OIIO:
|
||||
{
|
||||
#ifdef __OIIO__
|
||||
/* Make sure we have all necessary data in place, if not, bail. */
|
||||
kernel_assert(kg->oiio);
|
||||
kernel_assert(kg->oiio->tex_sys);
|
||||
kernel_assert(info.data);
|
||||
if (!kg->oiio || !kg->oiio->tex_sys || !info.data) {
|
||||
return r;
|
||||
}
|
||||
/* Options: Anisotropic is a quality/speed tradeoff.
|
||||
* Interpolation and extensions are supported in OIIO under different constants.
|
||||
* */
|
||||
OIIO::TextureOpt options;
|
||||
options.anisotropic = 8;
|
||||
float missingcolor[4] = {
|
||||
TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A};
|
||||
options.missingcolor = missingcolor;
|
||||
options.mipmode = OIIO::TextureOpt::MipModeAniso;
|
||||
options.sblur = options.tblur = 0.0f;
|
||||
switch (info.interpolation) {
|
||||
case INTERPOLATION_SMART:
|
||||
options.interpmode = OIIO::TextureOpt::InterpSmartBicubic;
|
||||
break;
|
||||
case INTERPOLATION_CUBIC:
|
||||
options.interpmode = OIIO::TextureOpt::InterpBicubic;
|
||||
break;
|
||||
case INTERPOLATION_LINEAR:
|
||||
options.interpmode = OIIO::TextureOpt::InterpBilinear;
|
||||
break;
|
||||
//case INTERPOLATION_NONE:
|
||||
case INTERPOLATION_CLOSEST:
|
||||
default:
|
||||
options.interpmode = OIIO::TextureOpt::InterpClosest;
|
||||
break;
|
||||
}
|
||||
switch (info.extension) {
|
||||
case EXTENSION_CLIP:
|
||||
options.swrap = options.twrap = OIIO::TextureOpt::WrapBlack;
|
||||
break;
|
||||
case EXTENSION_EXTEND:
|
||||
options.swrap = options.twrap = OIIO::TextureOpt::WrapClamp;
|
||||
break;
|
||||
case EXTENSION_REPEAT:
|
||||
default:
|
||||
options.swrap = options.twrap = OIIO::TextureOpt::WrapPeriodic;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Texture lookup simplifications on less important paths. */
|
||||
if (path_flag & NEAREST_LOOKUP_PATHS && !(path_flag & PATH_RAY_SINGULAR)) {
|
||||
options.interpmode = OIIO::TextureOpt::InterpClosest;
|
||||
options.mipmode = OIIO::TextureOpt::MipModeOneLevel;
|
||||
}
|
||||
else {
|
||||
options.mipmode = OIIO::TextureOpt::MipModeAniso;
|
||||
}
|
||||
if (path_flag & DIFFUSE_BLUR_PATHS) {
|
||||
options.sblur = options.tblur = kg->oiio->diffuse_blur;
|
||||
}
|
||||
else if (path_flag & PATH_RAY_GLOSSY) {
|
||||
options.sblur = options.tblur = kg->oiio->glossy_blur;
|
||||
}
|
||||
else {
|
||||
options.sblur = options.tblur = 0.0f;
|
||||
}
|
||||
|
||||
OIIO::TextureSystem::TextureHandle *handle = *((OIIO::TextureSystem::TextureHandle**)info.data);
|
||||
kernel_assert(handle && kg->oiio->tex_sys->good(handle));
|
||||
if(handle && !kg->oiio->tex_sys->good(handle)) {
|
||||
return r;
|
||||
}
|
||||
kg->oiio->tex_sys->texture(handle,
|
||||
(OIIO::TextureSystem::Perthread *)kg->oiio_tdata,
|
||||
options,
|
||||
x,
|
||||
y,
|
||||
ds.dx,
|
||||
ds.dy,
|
||||
dt.dx,
|
||||
dt.dy,
|
||||
4,
|
||||
(float *)&r);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return TextureInterpolator<float4>::interp(info, x, y);
|
||||
default:
|
||||
assert(0);
|
||||
return make_float4(
|
||||
TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A);
|
||||
}
|
||||
return info.compress_as_srgb ? color_srgb_to_linear_v4(r) : r;
|
||||
}
|
||||
|
||||
ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg,
|
||||
@@ -748,8 +646,6 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg,
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FLOAT3:
|
||||
return NanoVDBInterpolator<nanovdb::Vec3f>::interp_3d(info, P.x, P.y, P.z, interp);
|
||||
#endif
|
||||
case IMAGE_DATA_TYPE_OIIO:
|
||||
return make_float4(TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A);
|
||||
default:
|
||||
assert(0);
|
||||
return make_float4(
|
||||
|
@@ -36,8 +36,6 @@
|
||||
# endif
|
||||
#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX */
|
||||
|
||||
#define OIIO_NO_AVX 1
|
||||
|
||||
#include "kernel/kernel.h"
|
||||
#define KERNEL_ARCH cpu_avx
|
||||
#include "kernel/kernels/cpu/kernel_cpu_impl.h"
|
||||
|
@@ -37,8 +37,6 @@
|
||||
# endif
|
||||
#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 */
|
||||
|
||||
#define OIIO_NO_AVX 1
|
||||
|
||||
#include "kernel/kernel.h"
|
||||
#define KERNEL_ARCH cpu_avx2
|
||||
#include "kernel/kernels/cpu/kernel_cpu_impl.h"
|
||||
|
@@ -178,7 +178,7 @@ ccl_device_inline T kernel_tex_image_interp_nanovdb(
|
||||
}
|
||||
#endif
|
||||
|
||||
ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, float y, differential, differential, uint)
|
||||
ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, float y)
|
||||
{
|
||||
const TextureInfo &info = kernel_tex_fetch(__texture_info, id);
|
||||
|
||||
|
@@ -123,10 +123,6 @@ ccl_device_inline float4 svm_image_texture_read(
|
||||
return make_float4(r.x, r.y, r.z, r.w);
|
||||
}
|
||||
#endif
|
||||
/* Unsupported. */
|
||||
else if (texture_type == IMAGE_DATA_TYPE_OIIO) {
|
||||
return make_float4(TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A);
|
||||
}
|
||||
/* Byte */
|
||||
else {
|
||||
uchar r = tex_fetch(uchar, info, data_offset);
|
||||
@@ -203,7 +199,7 @@ ccl_device_inline float svm_image_texture_frac(float x, int *ix)
|
||||
} \
|
||||
(void)0
|
||||
|
||||
ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, float y, differential, differential, uint)
|
||||
ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, float y)
|
||||
{
|
||||
const ccl_global TextureInfo *info = kernel_tex_info(kg, id);
|
||||
|
||||
|
@@ -56,7 +56,6 @@
|
||||
|
||||
#include "kernel/kernel_projection.h"
|
||||
#include "kernel/kernel_accumulate.h"
|
||||
#include "kernel/kernel_light.h"
|
||||
#include "kernel/kernel_shader.h"
|
||||
// clang-format on
|
||||
|
||||
@@ -1249,12 +1248,7 @@ bool OSLRenderServices::texture(ustring filename,
|
||||
}
|
||||
case OSLTextureHandle::SVM: {
|
||||
/* Packed texture. */
|
||||
differential ds, dt;
|
||||
ds.dx = dsdx;
|
||||
ds.dy = dsdy;
|
||||
dt.dx = dtdx;
|
||||
dt.dy = dtdy;
|
||||
float4 rgba = kernel_tex_image_interp(kernel_globals, handle->svm_slot, s, 1.0f - t, ds, dt, sg->raytype);
|
||||
float4 rgba = kernel_tex_image_interp(kernel_globals, handle->svm_slot, s, 1.0f - t);
|
||||
|
||||
result[0] = rgba[0];
|
||||
if (nchannels > 1)
|
||||
@@ -1286,7 +1280,7 @@ bool OSLRenderServices::texture(ustring filename,
|
||||
texture_thread_info,
|
||||
options,
|
||||
s,
|
||||
1.0f - t,
|
||||
t,
|
||||
dsdx,
|
||||
dtdx,
|
||||
dsdy,
|
||||
@@ -1300,7 +1294,7 @@ bool OSLRenderServices::texture(ustring filename,
|
||||
status = ts->texture(filename,
|
||||
options,
|
||||
s,
|
||||
1.0f - t,
|
||||
t,
|
||||
dsdx,
|
||||
dtdx,
|
||||
dsdy,
|
||||
|
@@ -47,6 +47,7 @@ shader node_environment_texture(
|
||||
string filename = "",
|
||||
string projection = "equirectangular",
|
||||
string interpolation = "linear",
|
||||
int compress_as_srgb = 0,
|
||||
int ignore_alpha = 0,
|
||||
int unassociate_alpha = 0,
|
||||
int is_float = 1,
|
||||
@@ -78,4 +79,7 @@ shader node_environment_texture(
|
||||
if (!is_float)
|
||||
Color = min(Color, 1.0);
|
||||
}
|
||||
|
||||
if (compress_as_srgb)
|
||||
Color = color_srgb_to_scene_linear(Color);
|
||||
}
|
||||
|
@@ -59,6 +59,7 @@ color image_texture_lookup(string filename,
|
||||
float u,
|
||||
float v,
|
||||
output float Alpha,
|
||||
int compress_as_srgb,
|
||||
int ignore_alpha,
|
||||
int unassociate_alpha,
|
||||
int is_float,
|
||||
@@ -88,6 +89,10 @@ color image_texture_lookup(string filename,
|
||||
rgb = min(rgb, 1.0);
|
||||
}
|
||||
|
||||
if (compress_as_srgb) {
|
||||
rgb = color_srgb_to_scene_linear(rgb);
|
||||
}
|
||||
|
||||
return rgb;
|
||||
}
|
||||
|
||||
@@ -99,6 +104,7 @@ shader node_image_texture(int use_mapping = 0,
|
||||
string interpolation = "smartcubic",
|
||||
string extension = "periodic",
|
||||
float projection_blend = 0.0,
|
||||
int compress_as_srgb = 0,
|
||||
int ignore_alpha = 0,
|
||||
int unassociate_alpha = 0,
|
||||
int is_tiled = 0,
|
||||
@@ -116,6 +122,7 @@ shader node_image_texture(int use_mapping = 0,
|
||||
p[0],
|
||||
p[1],
|
||||
Alpha,
|
||||
compress_as_srgb,
|
||||
ignore_alpha,
|
||||
unassociate_alpha,
|
||||
is_float,
|
||||
@@ -194,6 +201,7 @@ shader node_image_texture(int use_mapping = 0,
|
||||
p[1],
|
||||
p[2],
|
||||
tmp_alpha,
|
||||
compress_as_srgb,
|
||||
ignore_alpha,
|
||||
unassociate_alpha,
|
||||
is_float,
|
||||
@@ -207,6 +215,7 @@ shader node_image_texture(int use_mapping = 0,
|
||||
p[0],
|
||||
p[2],
|
||||
tmp_alpha,
|
||||
compress_as_srgb,
|
||||
ignore_alpha,
|
||||
unassociate_alpha,
|
||||
is_float,
|
||||
@@ -220,6 +229,7 @@ shader node_image_texture(int use_mapping = 0,
|
||||
p[1],
|
||||
p[0],
|
||||
tmp_alpha,
|
||||
compress_as_srgb,
|
||||
ignore_alpha,
|
||||
unassociate_alpha,
|
||||
is_float,
|
||||
@@ -235,6 +245,7 @@ shader node_image_texture(int use_mapping = 0,
|
||||
projected[0],
|
||||
projected[1],
|
||||
Alpha,
|
||||
compress_as_srgb,
|
||||
ignore_alpha,
|
||||
unassociate_alpha,
|
||||
is_float,
|
||||
@@ -248,6 +259,7 @@ shader node_image_texture(int use_mapping = 0,
|
||||
projected[0],
|
||||
projected[1],
|
||||
Alpha,
|
||||
compress_as_srgb,
|
||||
ignore_alpha,
|
||||
unassociate_alpha,
|
||||
is_float,
|
||||
|
@@ -327,10 +327,10 @@ ccl_device_noinline void svm_eval_nodes(
|
||||
break;
|
||||
# endif /* NODES_FEATURE(NODE_FEATURE_BUMP) */
|
||||
case NODE_TEX_IMAGE:
|
||||
svm_node_tex_image(kg, sd, path_flag, stack, node, &offset);
|
||||
svm_node_tex_image(kg, sd, stack, node, &offset);
|
||||
break;
|
||||
case NODE_TEX_IMAGE_BOX:
|
||||
svm_node_tex_image_box(kg, sd, path_flag, stack, node);
|
||||
svm_node_tex_image_box(kg, sd, stack, node);
|
||||
break;
|
||||
case NODE_TEX_NOISE:
|
||||
svm_node_tex_noise(kg, sd, stack, node.y, node.z, node.w, &offset);
|
||||
@@ -437,7 +437,7 @@ ccl_device_noinline void svm_eval_nodes(
|
||||
svm_node_camera(kg, sd, stack, node.y, node.z, node.w);
|
||||
break;
|
||||
case NODE_TEX_ENVIRONMENT:
|
||||
svm_node_tex_environment(kg, sd, path_flag, stack, node);
|
||||
svm_node_tex_environment(kg, sd, stack, node);
|
||||
break;
|
||||
case NODE_TEX_SKY:
|
||||
svm_node_tex_sky(kg, sd, stack, node, &offset);
|
||||
|
@@ -16,21 +16,14 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
ccl_device float4 svm_image_texture(KernelGlobals *kg,
|
||||
int id,
|
||||
float x,
|
||||
float y,
|
||||
differential ds,
|
||||
differential dt,
|
||||
uint flags,
|
||||
int path_flag)
|
||||
ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y, uint flags)
|
||||
{
|
||||
if (id == -1) {
|
||||
return make_float4(
|
||||
TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A);
|
||||
}
|
||||
|
||||
float4 r = kernel_tex_image_interp(kg, id, x, y, ds, dt, path_flag);
|
||||
float4 r = kernel_tex_image_interp(kg, id, x, y);
|
||||
const float alpha = r.w;
|
||||
|
||||
if ((flags & NODE_IMAGE_ALPHA_UNASSOCIATE) && alpha != 1.0f && alpha != 0.0f) {
|
||||
@@ -38,6 +31,10 @@ ccl_device float4 svm_image_texture(KernelGlobals *kg,
|
||||
r.w = alpha;
|
||||
}
|
||||
|
||||
if (flags & NODE_IMAGE_COMPRESS_AS_SRGB) {
|
||||
r = color_srgb_to_linear_v4(r);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -48,21 +45,19 @@ ccl_device_inline float3 texco_remap_square(float3 co)
|
||||
}
|
||||
|
||||
ccl_device void svm_node_tex_image(
|
||||
KernelGlobals *kg, ShaderData *sd, int path_flag, float *stack, uint4 node, int *offset)
|
||||
KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset)
|
||||
{
|
||||
uint co_offset, out_offset, alpha_offset, flags;
|
||||
uint projection, dx_offset, dy_offset, unused;
|
||||
|
||||
svm_unpack_node_uchar4(node.z, &co_offset, &out_offset, &alpha_offset, &flags);
|
||||
svm_unpack_node_uchar4(node.w, &projection, &dx_offset, &dy_offset, &unused);
|
||||
|
||||
float3 co = stack_load_float3(stack, co_offset);
|
||||
float2 tex_co;
|
||||
if (projection == NODE_IMAGE_PROJ_SPHERE) {
|
||||
if (node.w == NODE_IMAGE_PROJ_SPHERE) {
|
||||
co = texco_remap_square(co);
|
||||
tex_co = map_to_sphere(co);
|
||||
}
|
||||
else if (projection == NODE_IMAGE_PROJ_TUBE) {
|
||||
else if (node.w == NODE_IMAGE_PROJ_TUBE) {
|
||||
co = texco_remap_square(co);
|
||||
tex_co = map_to_tube(co);
|
||||
}
|
||||
@@ -70,40 +65,6 @@ ccl_device void svm_node_tex_image(
|
||||
tex_co = make_float2(co.x, co.y);
|
||||
}
|
||||
|
||||
differential ds, dt;
|
||||
#ifdef __KERNEL_CPU__
|
||||
if (stack_valid(dx_offset) && stack_valid(dy_offset)) {
|
||||
float3 dx = stack_load_float3(stack, dx_offset);
|
||||
float3 dy = stack_load_float3(stack, dy_offset);
|
||||
float2 tex_co_dx, tex_co_dy;
|
||||
if (projection == NODE_IMAGE_PROJ_SPHERE) {
|
||||
dx = texco_remap_square(dx);
|
||||
tex_co_dx = map_to_sphere(dx);
|
||||
dy = texco_remap_square(dy);
|
||||
tex_co_dy = map_to_sphere(dy);
|
||||
}
|
||||
else if (projection == NODE_IMAGE_PROJ_TUBE) {
|
||||
dx = texco_remap_square(dx);
|
||||
tex_co_dx = map_to_tube(dx);
|
||||
dy = texco_remap_square(dy);
|
||||
tex_co_dy = map_to_tube(dy);
|
||||
}
|
||||
else {
|
||||
tex_co_dx = make_float2(dx.x, dx.y);
|
||||
tex_co_dy = make_float2(dy.x, dy.y);
|
||||
}
|
||||
ds.dx = tex_co_dx.x - tex_co.x;
|
||||
ds.dy = tex_co_dy.x - tex_co.x;
|
||||
dt.dx = tex_co_dx.y - tex_co.y;
|
||||
dt.dy = tex_co_dy.y - tex_co.y;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ds = differential_zero();
|
||||
dt = differential_zero();
|
||||
}
|
||||
|
||||
/* TODO(lukas): Consider moving tile information out of the SVM node.
|
||||
* TextureInfo seems a reasonable candidate. */
|
||||
int id = -1;
|
||||
@@ -147,7 +108,7 @@ ccl_device void svm_node_tex_image(
|
||||
id = -num_nodes;
|
||||
}
|
||||
|
||||
float4 f = svm_image_texture(kg, id, tex_co.x, tex_co.y, ds, dt, flags, path_flag);
|
||||
float4 f = svm_image_texture(kg, id, tex_co.x, tex_co.y, flags);
|
||||
|
||||
if (stack_valid(out_offset))
|
||||
stack_store_float3(stack, out_offset, make_float3(f.x, f.y, f.z));
|
||||
@@ -155,8 +116,7 @@ ccl_device void svm_node_tex_image(
|
||||
stack_store_float(stack, alpha_offset, f.w);
|
||||
}
|
||||
|
||||
ccl_device void svm_node_tex_image_box(
|
||||
KernelGlobals *kg, ShaderData *sd, int path_flag, float *stack, uint4 node)
|
||||
ccl_device void svm_node_tex_image_box(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node)
|
||||
{
|
||||
/* get object space normal */
|
||||
float3 N = sd->N;
|
||||
@@ -184,9 +144,7 @@ ccl_device void svm_node_tex_image_box(
|
||||
* 7 zones, with an if() test for each zone */
|
||||
|
||||
float3 weight = make_float3(0.0f, 0.0f, 0.0f);
|
||||
uint blend_hi, blend_lo, dx_offset, dy_offset;
|
||||
svm_unpack_node_uchar4(node.w, &blend_hi, &blend_lo, &dx_offset, &dy_offset);
|
||||
float blend = ((blend_hi << 8) + blend_lo) / 65536.0f;
|
||||
float blend = __int_as_float(node.w);
|
||||
float limit = 0.5f * (1.0f + blend);
|
||||
|
||||
/* first test for corners with single texture */
|
||||
@@ -237,49 +195,30 @@ ccl_device void svm_node_tex_image_box(
|
||||
|
||||
float4 f = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
float3 co_dx = make_float3(0.0f, 0.0f, 0.0f);
|
||||
float3 co_dy = make_float3(0.0f, 0.0f, 0.0f);
|
||||
differential ds, dt;
|
||||
#ifdef __KERNEL_CPU__
|
||||
if (stack_valid(dx_offset) && stack_valid(dy_offset)) {
|
||||
co_dx = co - stack_load_float3(stack, dx_offset);
|
||||
co_dy = co - stack_load_float3(stack, dy_offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Map so that no textures are flipped, rotation is somewhat arbitrary. */
|
||||
if (weight.x > 0.0f) {
|
||||
float2 uv = make_float2((signed_N.x < 0.0f) ? 1.0f - co.y : co.y, co.z);
|
||||
ds.dx = co_dx.y;
|
||||
ds.dy = co_dy.y;
|
||||
dt.dx = co_dx.z;
|
||||
dt.dy = co_dy.z;
|
||||
f += weight.x * svm_image_texture(kg, id, uv.x, uv.y, ds, dt, flags, path_flag);
|
||||
f += weight.x * svm_image_texture(kg, id, uv.x, uv.y, flags);
|
||||
}
|
||||
if (weight.y > 0.0f) {
|
||||
float2 uv = make_float2((signed_N.y < 0.0f) ? 1.0f - co.x : co.x, co.z);
|
||||
ds.dx = co_dx.x;
|
||||
ds.dy = co_dy.x;
|
||||
dt.dx = co_dx.z;
|
||||
dt.dy = co_dy.z;
|
||||
f += weight.y * svm_image_texture(kg, id, uv.x, uv.y, ds, dt, flags, path_flag);
|
||||
float2 uv = make_float2((signed_N.y > 0.0f) ? 1.0f - co.x : co.x, co.z);
|
||||
f += weight.y * svm_image_texture(kg, id, uv.x, uv.y, flags);
|
||||
}
|
||||
if (weight.z > 0.0f) {
|
||||
float2 uv = make_float2((signed_N.z < 0.0f) ? 1.0f - co.y : co.y, co.x);
|
||||
ds.dx = co_dx.y;
|
||||
ds.dy = co_dy.y;
|
||||
dt.dx = co_dx.x;
|
||||
dt.dy = co_dy.x;
|
||||
f += weight.z * svm_image_texture(kg, id, uv.x, uv.y, ds, dt, flags, path_flag);
|
||||
float2 uv = make_float2((signed_N.z > 0.0f) ? 1.0f - co.y : co.y, co.x);
|
||||
f += weight.z * svm_image_texture(kg, id, uv.x, uv.y, flags);
|
||||
}
|
||||
|
||||
if (stack_valid(out_offset))
|
||||
stack_store_float3(stack, out_offset, make_float3(f.x, f.y, f.z));
|
||||
if (stack_valid(alpha_offset))
|
||||
stack_store_float(stack, alpha_offset, f.w);
|
||||
}
|
||||
|
||||
ccl_device void svm_node_tex_environment(
|
||||
KernelGlobals *kg, ShaderData *sd, int path_flag, float *stack, uint4 node)
|
||||
ccl_device void svm_node_tex_environment(KernelGlobals *kg,
|
||||
ShaderData *sd,
|
||||
float *stack,
|
||||
uint4 node)
|
||||
{
|
||||
uint id = node.y;
|
||||
uint co_offset, out_offset, alpha_offset, flags;
|
||||
@@ -297,8 +236,7 @@ ccl_device void svm_node_tex_environment(
|
||||
else
|
||||
uv = direction_to_mirrorball(co);
|
||||
|
||||
float4 f = svm_image_texture(
|
||||
kg, id, uv.x, uv.y, differential_zero(), differential_zero(), flags, path_flag);
|
||||
float4 f = svm_image_texture(kg, id, uv.x, uv.y, flags);
|
||||
|
||||
if (stack_valid(out_offset))
|
||||
stack_store_float3(stack, out_offset, make_float3(f.x, f.y, f.z));
|
||||
|
@@ -141,8 +141,6 @@ ccl_device float3 sky_radiance_nishita(KernelGlobals *kg,
|
||||
float3 xyz;
|
||||
/* convert dir to spherical coordinates */
|
||||
float2 direction = direction_to_spherical(dir);
|
||||
differential ds, dt;
|
||||
ds.dx = ds.dy = dt.dx = dt.dy = 0.0f;
|
||||
|
||||
/* render above the horizon */
|
||||
if (dir.z >= 0.0f) {
|
||||
@@ -186,7 +184,7 @@ ccl_device float3 sky_radiance_nishita(KernelGlobals *kg,
|
||||
if (x > 1.0f) {
|
||||
x -= 1.0f;
|
||||
}
|
||||
xyz = float4_to_float3(kernel_tex_image_interp(kg, texture_id, x, y, ds, dt, 0));
|
||||
xyz = float4_to_float3(kernel_tex_image_interp(kg, texture_id, x, y));
|
||||
}
|
||||
}
|
||||
/* ground */
|
||||
@@ -203,7 +201,7 @@ ccl_device float3 sky_radiance_nishita(KernelGlobals *kg,
|
||||
if (x > 1.0f) {
|
||||
x -= 1.0f;
|
||||
}
|
||||
xyz = float4_to_float3(kernel_tex_image_interp(kg, texture_id, x, -0.5f, ds, dt, 0)) * fade;
|
||||
xyz = float4_to_float3(kernel_tex_image_interp(kg, texture_id, x, -0.5)) * fade;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -121,9 +121,6 @@ ccl_device void svm_node_tex_coord_bump_dx(
|
||||
}
|
||||
case NODE_TEXCO_NORMAL: {
|
||||
data = sd->N;
|
||||
# ifdef __DNDU__
|
||||
data = sd->N + sd->dNdx;
|
||||
# endif
|
||||
object_inverse_normal_transform(kg, sd, &data);
|
||||
break;
|
||||
}
|
||||
@@ -204,9 +201,6 @@ ccl_device void svm_node_tex_coord_bump_dy(
|
||||
}
|
||||
case NODE_TEXCO_NORMAL: {
|
||||
data = sd->N;
|
||||
# ifdef __DNDU__
|
||||
data = sd->N + sd->dNdy;
|
||||
# endif
|
||||
object_inverse_normal_transform(kg, sd, &data);
|
||||
break;
|
||||
}
|
||||
|
@@ -1739,7 +1739,7 @@ void GeometryManager::device_update_displacement_images(Device *device,
|
||||
|
||||
ImageSlotTextureNode *image_node = static_cast<ImageSlotTextureNode *>(node);
|
||||
for (int i = 0; i < image_node->handle.num_tiles(); i++) {
|
||||
const int slot = image_node->handle.svm_slot(scene->shader_manager->use_osl(), i);
|
||||
const int slot = image_node->handle.svm_slot(i);
|
||||
if (slot != -1) {
|
||||
bump_images.insert(slot);
|
||||
}
|
||||
@@ -1748,12 +1748,10 @@ void GeometryManager::device_update_displacement_images(Device *device,
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
foreach (int slot, bump_images) {
|
||||
pool.push(function_bind(
|
||||
&ImageManager::device_update_slot, image_manager, device, scene, slot, &progress));
|
||||
}*/
|
||||
image_manager->device_update(device, scene, progress);
|
||||
}
|
||||
pool.wait_work();
|
||||
}
|
||||
|
||||
|
@@ -383,10 +383,6 @@ void ShaderGraph::finalize(Scene *scene, bool do_bump, bool do_simplify, bool bu
|
||||
if (do_bump)
|
||||
bump_from_displacement(bump_in_object_space);
|
||||
|
||||
/* This must be after all bump nodes are created,
|
||||
* so that bump map lookups can be mip mapped too. */
|
||||
add_differentials();
|
||||
|
||||
ShaderInput *surface_in = output()->input("Surface");
|
||||
ShaderInput *volume_in = output()->input("Volume");
|
||||
|
||||
@@ -958,91 +954,6 @@ void ShaderGraph::refine_bump_nodes()
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderGraph::add_differentials()
|
||||
{
|
||||
/* we transverse the node graph looking for texture nodes, when we find them,
|
||||
* we copy the sub-graph defined from "Vector"
|
||||
* input to the inputs "Vector_dx" and "Vector_dy" */
|
||||
|
||||
foreach (ShaderNode *node, nodes) {
|
||||
if (node->special_type == SHADER_SPECIAL_TYPE_IMAGE_SLOT && node->input("Vector")->link &&
|
||||
node->input("Vector_dx") && node->input("Vector_dy")) {
|
||||
ShaderInput *vector_input = node->input("Vector");
|
||||
ShaderNodeSet nodes_vector;
|
||||
|
||||
/* make 2 extra copies of the subgraph defined in Vector input */
|
||||
ShaderNodeMap nodes_dx;
|
||||
ShaderNodeMap nodes_dy;
|
||||
|
||||
/* find dependencies for the given input */
|
||||
find_dependencies(nodes_vector, vector_input);
|
||||
|
||||
copy_nodes(nodes_vector, nodes_dx);
|
||||
copy_nodes(nodes_vector, nodes_dy);
|
||||
|
||||
/* First: Nodes that have no bump are set to center, others are left untouched. */
|
||||
foreach (ShaderNode *node, nodes_vector)
|
||||
node->bump = node->bump == SHADER_BUMP_NONE ? SHADER_BUMP_CENTER : node->bump;
|
||||
|
||||
/* Second: Nodes that have no bump are set DX, others are shifted by one. */
|
||||
foreach (NodePair &pair, nodes_dx) {
|
||||
switch (pair.second->bump) {
|
||||
case SHADER_BUMP_DX:
|
||||
pair.second->bump = SHADER_BUMP_DY;
|
||||
break;
|
||||
case SHADER_BUMP_DY:
|
||||
pair.second->bump = SHADER_BUMP_CENTER;
|
||||
break;
|
||||
default:
|
||||
pair.second->bump = SHADER_BUMP_DX;
|
||||
}
|
||||
}
|
||||
|
||||
/* Second: Nodes that have no bump are set DY, others are shifted by two. */
|
||||
foreach (NodePair &pair, nodes_dy) {
|
||||
switch (pair.second->bump) {
|
||||
case SHADER_BUMP_DX:
|
||||
pair.second->bump = SHADER_BUMP_CENTER;
|
||||
break;
|
||||
case SHADER_BUMP_DY:
|
||||
pair.second->bump = SHADER_BUMP_DX;
|
||||
break;
|
||||
default:
|
||||
pair.second->bump = SHADER_BUMP_DY;
|
||||
}
|
||||
}
|
||||
|
||||
ShaderOutput *out = vector_input->link;
|
||||
ShaderOutput *out_dx = nodes_dx[out->parent]->output(out->name());
|
||||
ShaderOutput *out_dy = nodes_dy[out->parent]->output(out->name());
|
||||
|
||||
/* Insert mapping nodes that are duplicates of what's inside the image node.
|
||||
* This is somewhat wasteful, it would be better to have a MappingNode
|
||||
* that does three transforms at a time. */
|
||||
MappingNode *mapping1 = create_node<MappingNode>();
|
||||
MappingNode *mapping2 = create_node<MappingNode>();
|
||||
mapping1->set_location(((ImageTextureNode *)node)->tex_mapping.translation);
|
||||
mapping1->set_rotation(((ImageTextureNode *)node)->tex_mapping.rotation);
|
||||
mapping1->set_scale(((ImageTextureNode *)node)->tex_mapping.scale);
|
||||
mapping2->set_location(((ImageTextureNode *)node)->tex_mapping.translation);
|
||||
mapping2->set_rotation(((ImageTextureNode *)node)->tex_mapping.rotation);
|
||||
mapping2->set_scale(((ImageTextureNode *)node)->tex_mapping.scale);
|
||||
add(mapping1);
|
||||
add(mapping2);
|
||||
connect(out_dx, mapping1->input("Vector"));
|
||||
connect(out_dy, mapping2->input("Vector"));
|
||||
connect(mapping1->output("Vector"), node->input("Vector_dx"));
|
||||
connect(mapping2->output("Vector"), node->input("Vector_dy"));
|
||||
|
||||
/* add generated nodes */
|
||||
foreach (NodePair &pair, nodes_dx)
|
||||
add(pair.second);
|
||||
foreach (NodePair &pair, nodes_dy)
|
||||
add(pair.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderGraph::bump_from_displacement(bool use_object_space)
|
||||
{
|
||||
/* generate bump mapping automatically from displacement. bump mapping is
|
||||
|
@@ -381,7 +381,6 @@ class ShaderGraph : public NodeOwner {
|
||||
void break_cycles(ShaderNode *node, vector<bool> &visited, vector<bool> &on_stack);
|
||||
void bump_from_displacement(bool use_object_space);
|
||||
void refine_bump_nodes();
|
||||
void add_differentials();
|
||||
void expand();
|
||||
void default_inputs(bool do_osl);
|
||||
void transform_multi_closure(ShaderNode *node, ShaderOutput *weight_out, bool volume);
|
||||
|
@@ -36,8 +36,6 @@
|
||||
# include <OSL/oslexec.h>
|
||||
#endif
|
||||
|
||||
#include "kernel/kernel_oiio_globals.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
namespace {
|
||||
@@ -79,8 +77,6 @@ const char *name_from_type(ImageDataType type)
|
||||
return "nanovdb_float";
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FLOAT3:
|
||||
return "nanovdb_float3";
|
||||
case IMAGE_DATA_TYPE_OIIO:
|
||||
return "openimageio";
|
||||
case IMAGE_DATA_NUM_TYPES:
|
||||
assert(!"System enumerator type, should never be used");
|
||||
return "";
|
||||
@@ -155,13 +151,13 @@ ImageMetaData ImageHandle::metadata()
|
||||
return img->metadata;
|
||||
}
|
||||
|
||||
int ImageHandle::svm_slot(bool osl, const int tile_index) const
|
||||
int ImageHandle::svm_slot(const int tile_index) const
|
||||
{
|
||||
if (tile_index >= tile_slots.size()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (osl) {
|
||||
if (manager->osl_texture_system) {
|
||||
ImageManager::Image *img = manager->images[tile_slots[tile_index]];
|
||||
if (!img->loader->osl_filepath().empty()) {
|
||||
return -1;
|
||||
@@ -243,11 +239,6 @@ bool ImageMetaData::is_float() const
|
||||
|
||||
void ImageMetaData::detect_colorspace()
|
||||
{
|
||||
if (type == IMAGE_DATA_TYPE_OIIO) {
|
||||
compress_as_srgb = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Convert used specified color spaces to one we know how to handle. */
|
||||
colorspace = ColorSpaceManager::detect_known_colorspace(
|
||||
colorspace, colorspace_file_format, is_float());
|
||||
@@ -308,13 +299,12 @@ bool ImageLoader::is_vdb_loader() const
|
||||
ImageManager::ImageManager(const DeviceInfo &info)
|
||||
{
|
||||
need_update_ = true;
|
||||
oiio_texture_system = NULL;
|
||||
osl_texture_system = NULL;
|
||||
animation_frame = 0;
|
||||
|
||||
/* Set image limits */
|
||||
features.has_half_float = info.has_half_images;
|
||||
features.has_nanovdb = info.has_nanovdb;
|
||||
features.has_texture_cache = false;
|
||||
}
|
||||
|
||||
ImageManager::~ImageManager()
|
||||
@@ -323,10 +313,9 @@ ImageManager::~ImageManager()
|
||||
assert(!images[slot]);
|
||||
}
|
||||
|
||||
void ImageManager::set_oiio_texture_system(void *texture_system)
|
||||
void ImageManager::set_osl_texture_system(void *texture_system)
|
||||
{
|
||||
oiio_texture_system = texture_system;
|
||||
features.has_texture_cache = texture_system != NULL;
|
||||
osl_texture_system = texture_system;
|
||||
}
|
||||
|
||||
bool ImageManager::set_animation_frame_update(int frame)
|
||||
@@ -405,28 +394,6 @@ ImageHandle ImageManager::add_image(const string &filename,
|
||||
return handle;
|
||||
}
|
||||
|
||||
const string ImageManager::get_mip_map_path(const string &filename)
|
||||
{
|
||||
if (!path_exists(filename)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
string::size_type idx = filename.rfind('.');
|
||||
if (idx != string::npos) {
|
||||
std::string extension = filename.substr(idx + 1);
|
||||
if (extension == "tx") {
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
|
||||
string tx_name = filename.substr(0, idx) + ".tx";
|
||||
if (path_exists(tx_name)) {
|
||||
return tx_name;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
ImageHandle ImageManager::add_image(ImageLoader *loader,
|
||||
const ImageParams ¶ms,
|
||||
const bool builtin)
|
||||
@@ -473,7 +440,7 @@ int ImageManager::add_image_slot(ImageLoader *loader,
|
||||
img->params = params;
|
||||
img->loader = loader;
|
||||
img->need_metadata = true;
|
||||
img->need_load = !(oiio_texture_system && !img->loader->osl_filepath().empty());
|
||||
img->need_load = !(osl_texture_system && !img->loader->osl_filepath().empty());
|
||||
img->builtin = builtin;
|
||||
img->users = 1;
|
||||
img->mem = NULL;
|
||||
@@ -683,23 +650,6 @@ void ImageManager::device_load_image(Device *device, Scene *scene, int slot, Pro
|
||||
|
||||
Image *img = images[slot];
|
||||
|
||||
if (features.has_texture_cache && !img->builtin) {
|
||||
/* Get or generate a mip mapped tile image file.
|
||||
* If we have a mip map, assume it's linear, not sRGB. */
|
||||
const char *cache_path = scene->params.texture.use_custom_cache_path ?
|
||||
scene->params.texture.custom_cache_path.c_str() :
|
||||
NULL;
|
||||
bool have_mip = ((OIIOImageLoader *)img->loader)
|
||||
->get_tx(img->metadata.colorspace,
|
||||
img->params.extension,
|
||||
progress,
|
||||
scene->params.texture.auto_convert,
|
||||
cache_path);
|
||||
if (have_mip) {
|
||||
img->need_metadata = true;
|
||||
}
|
||||
}
|
||||
|
||||
progress->set_status("Updating Images", "Loading " + img->loader->name());
|
||||
|
||||
const int texture_limit = scene->params.texture_limit;
|
||||
@@ -721,7 +671,6 @@ void ImageManager::device_load_image(Device *device, Scene *scene, int slot, Pro
|
||||
device, img->mem_name.c_str(), slot, type, img->params.interpolation, img->params.extension);
|
||||
img->mem->info.use_transform_3d = img->metadata.use_transform_3d;
|
||||
img->mem->info.transform_3d = img->metadata.transform_3d;
|
||||
img->mem->info.compress_as_srgb = img->metadata.compress_as_srgb;
|
||||
|
||||
/* Create new texture. */
|
||||
if (type == IMAGE_DATA_TYPE_FLOAT4) {
|
||||
@@ -818,17 +767,7 @@ void ImageManager::device_load_image(Device *device, Scene *scene, int slot, Pro
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (type == IMAGE_DATA_TYPE_OIIO) {
|
||||
thread_scoped_lock device_lock(device_mutex);
|
||||
void *pixels = img->mem->alloc(1, 1);
|
||||
|
||||
if (pixels != NULL) {
|
||||
OIIO::TextureSystem *tex_sys = (OIIO::TextureSystem *)oiio_texture_system;
|
||||
OIIO::TextureSystem::TextureHandle *handle = tex_sys->get_texture_handle(
|
||||
OIIO::ustring(img->loader->osl_filepath()));
|
||||
*((OIIO::TextureSystem::TextureHandle **)pixels) = tex_sys->good(handle) ? handle : NULL;
|
||||
}
|
||||
}
|
||||
{
|
||||
thread_scoped_lock device_lock(device_mutex);
|
||||
img->mem->copy_to_device();
|
||||
@@ -846,11 +785,11 @@ void ImageManager::device_free_image(Device *, int slot)
|
||||
return;
|
||||
}
|
||||
|
||||
if (oiio_texture_system) {
|
||||
if (osl_texture_system) {
|
||||
#ifdef WITH_OSL
|
||||
ustring filepath = img->loader->osl_filepath();
|
||||
if (!filepath.empty()) {
|
||||
((OIIO::TextureSystem *)oiio_texture_system)->invalidate(filepath);
|
||||
((OSL::TextureSystem *)osl_texture_system)->invalidate(filepath);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@@ -102,7 +102,6 @@ class ImageDeviceFeatures {
|
||||
public:
|
||||
bool has_half_float;
|
||||
bool has_nanovdb;
|
||||
bool has_texture_cache;
|
||||
};
|
||||
|
||||
/* Image loader base class, that can be subclassed to load image data
|
||||
@@ -158,7 +157,7 @@ class ImageHandle {
|
||||
int num_tiles();
|
||||
|
||||
ImageMetaData metadata();
|
||||
int svm_slot(bool osl = false, const int tile_index = 0) const;
|
||||
int svm_slot(const int tile_index = 0) const;
|
||||
device_texture *image_memory(const int tile_index = 0) const;
|
||||
|
||||
VDBImageLoader *vdb_loader(const int tile_index = 0) const;
|
||||
@@ -192,9 +191,7 @@ class ImageManager {
|
||||
void device_load_builtin(Device *device, Scene *scene, Progress &progress);
|
||||
void device_free_builtin(Device *device);
|
||||
|
||||
void set_oiio_texture_system(void *texture_system);
|
||||
const string get_mip_map_path(const string &filename);
|
||||
void set_pack_images(bool pack_images_);
|
||||
void set_osl_texture_system(void *texture_system);
|
||||
bool set_animation_frame_update(int frame);
|
||||
|
||||
void collect_statistics(RenderStats *stats);
|
||||
@@ -230,7 +227,7 @@ class ImageManager {
|
||||
int animation_frame;
|
||||
|
||||
vector<Image *> images;
|
||||
void *oiio_texture_system;
|
||||
void *osl_texture_system;
|
||||
|
||||
int add_image_slot(ImageLoader *loader, const ImageParams ¶ms, const bool builtin);
|
||||
void add_image_user(int slot);
|
||||
|
@@ -19,9 +19,6 @@
|
||||
#include "util/util_image.h"
|
||||
#include "util/util_logging.h"
|
||||
#include "util/util_path.h"
|
||||
#include "util/util_progress.h"
|
||||
|
||||
#include <OpenImageIO/imagebufalgo.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
@@ -65,8 +62,7 @@ bool OIIOImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMe
|
||||
size_t channel_size = spec.format.basesize();
|
||||
|
||||
bool is_float = false;
|
||||
bool is_half = spec.format == TypeDesc::HALF && features.has_half_float;
|
||||
bool is_tiled = spec.tile_pixels() != 0;
|
||||
bool is_half = false;
|
||||
|
||||
if (spec.format.is_floating_point()) {
|
||||
is_float = true;
|
||||
@@ -79,13 +75,15 @@ bool OIIOImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMe
|
||||
}
|
||||
}
|
||||
|
||||
/* check if it's half float */
|
||||
if (spec.format == TypeDesc::HALF && features.has_half_float) {
|
||||
is_half = true;
|
||||
}
|
||||
|
||||
/* set type and channels */
|
||||
metadata.channels = spec.nchannels;
|
||||
|
||||
if (is_tiled && features.has_texture_cache) {
|
||||
metadata.type = IMAGE_DATA_TYPE_OIIO;
|
||||
}
|
||||
else if (is_half) {
|
||||
if (is_half) {
|
||||
metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_HALF4 : IMAGE_DATA_TYPE_HALF;
|
||||
}
|
||||
else if (is_float) {
|
||||
@@ -213,7 +211,6 @@ bool OIIOImageLoader::load_pixels(const ImageMetaData &metadata,
|
||||
break;
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FLOAT:
|
||||
case IMAGE_DATA_TYPE_NANOVDB_FLOAT3:
|
||||
case IMAGE_DATA_TYPE_OIIO:
|
||||
case IMAGE_DATA_NUM_TYPES:
|
||||
break;
|
||||
}
|
||||
@@ -238,85 +235,4 @@ bool OIIOImageLoader::equals(const ImageLoader &other) const
|
||||
return filepath == other_loader.filepath;
|
||||
}
|
||||
|
||||
bool OIIOImageLoader::make_tx(const string &filename,
|
||||
const string &outputfilename,
|
||||
const ustring &colorspace,
|
||||
ExtensionType extension)
|
||||
{
|
||||
ImageSpec config;
|
||||
config.attribute("maketx:filtername", "lanczos3");
|
||||
config.attribute("maketx:opaque_detect", 1);
|
||||
config.attribute("maketx:highlightcomp", 1);
|
||||
config.attribute("maketx:oiio_options", 1);
|
||||
config.attribute("maketx:updatemode", 1);
|
||||
|
||||
switch (extension) {
|
||||
case EXTENSION_CLIP:
|
||||
config.attribute("maketx:wrap", "black");
|
||||
break;
|
||||
case EXTENSION_REPEAT:
|
||||
config.attribute("maketx:wrap", "periodic");
|
||||
break;
|
||||
case EXTENSION_EXTEND:
|
||||
config.attribute("maketx:wrap", "clamp");
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Convert textures to linear color space before mip mapping. */
|
||||
if (colorspace != u_colorspace_raw) {
|
||||
if (colorspace == u_colorspace_srgb || colorspace.empty()) {
|
||||
config.attribute("maketx:incolorspace", "sRGB");
|
||||
}
|
||||
else {
|
||||
config.attribute("maketx:incolorspace", colorspace.c_str());
|
||||
}
|
||||
config.attribute("maketx:outcolorspace", "linear");
|
||||
}
|
||||
|
||||
return ImageBufAlgo::make_texture(ImageBufAlgo::MakeTxTexture, filename, outputfilename, config);
|
||||
}
|
||||
|
||||
bool OIIOImageLoader::get_tx(const ustring &colorspace,
|
||||
const ExtensionType &extension,
|
||||
Progress *progress,
|
||||
bool auto_convert,
|
||||
const char *cache_path)
|
||||
{
|
||||
if (!path_exists(osl_filepath().c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string::size_type idx = osl_filepath().rfind('.');
|
||||
if (idx != string::npos) {
|
||||
string extension = osl_filepath().substr(idx + 1).c_str();
|
||||
if (extension == "tx") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
string tx_name = string(osl_filepath().substr(0, idx).c_str()) + ".tx";
|
||||
if (cache_path) {
|
||||
string filename = path_filename(tx_name);
|
||||
tx_name = path_join(string(cache_path), filename);
|
||||
}
|
||||
if (path_exists(tx_name)) {
|
||||
filepath = tx_name;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto_convert && progress) {
|
||||
progress->set_status("Updating Images", string("Converting ") + osl_filepath().c_str());
|
||||
|
||||
bool ok = make_tx(osl_filepath().c_str(), tx_name, colorspace, extension);
|
||||
if (ok) {
|
||||
filepath = tx_name;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -39,17 +39,6 @@ class OIIOImageLoader : public ImageLoader {
|
||||
|
||||
bool equals(const ImageLoader &other) const override;
|
||||
|
||||
bool get_tx(const ustring &colorspace,
|
||||
const ExtensionType &extension,
|
||||
Progress *progress,
|
||||
bool auto_convert,
|
||||
const char *cache_path);
|
||||
|
||||
static bool make_tx(const string &filename,
|
||||
const string &outputfilename,
|
||||
const ustring &colorspace,
|
||||
ExtensionType extension);
|
||||
|
||||
protected:
|
||||
ustring filepath;
|
||||
};
|
||||
|
@@ -250,10 +250,7 @@ NODE_DEFINE(ImageTextureNode)
|
||||
SOCKET_INT_ARRAY(tiles, "Tiles", array<int>());
|
||||
SOCKET_BOOLEAN(animated, "Animated", false);
|
||||
|
||||
|
||||
SOCKET_IN_POINT(vector, "Vector", zero_float3(), SocketType::LINK_TEXTURE_UV);
|
||||
SOCKET_IN_POINT(vector_dx, "Vector_dx", zero_float3());
|
||||
SOCKET_IN_POINT(vector_dy, "Vector_dy", zero_float3());
|
||||
|
||||
SOCKET_OUT_COLOR(color, "Color");
|
||||
SOCKET_OUT_FLOAT(alpha, "Alpha");
|
||||
@@ -368,8 +365,6 @@ void ImageTextureNode::compile(SVMCompiler &compiler)
|
||||
ShaderInput *vector_in = input("Vector");
|
||||
ShaderOutput *color_out = output("Color");
|
||||
ShaderOutput *alpha_out = output("Alpha");
|
||||
ShaderInput *vector_dx = input("Vector_dx");
|
||||
ShaderInput *vector_dy = input("Vector_dy");
|
||||
|
||||
if (handle.empty()) {
|
||||
cull_tiles(compiler.scene, compiler.current_graph);
|
||||
@@ -408,24 +403,22 @@ void ImageTextureNode::compile(SVMCompiler &compiler)
|
||||
num_nodes = divide_up(handle.num_tiles(), 2);
|
||||
}
|
||||
|
||||
compiler.add_node(
|
||||
NODE_TEX_IMAGE,
|
||||
num_nodes,
|
||||
compiler.encode_uchar4(vector_offset,
|
||||
compiler.stack_assign_if_linked(color_out),
|
||||
compiler.stack_assign_if_linked(alpha_out),
|
||||
flags),
|
||||
compiler.encode_uchar4(
|
||||
projection, compiler.stack_assign(vector_dx), compiler.stack_assign(vector_dy), 0));
|
||||
compiler.add_node(NODE_TEX_IMAGE,
|
||||
num_nodes,
|
||||
compiler.encode_uchar4(vector_offset,
|
||||
compiler.stack_assign_if_linked(color_out),
|
||||
compiler.stack_assign_if_linked(alpha_out),
|
||||
flags),
|
||||
projection);
|
||||
|
||||
if (num_nodes > 0) {
|
||||
for (int i = 0; i < num_nodes; i++) {
|
||||
int4 node;
|
||||
node.x = tiles[2 * i];
|
||||
node.y = handle.svm_slot(false, 2 * i);
|
||||
node.y = handle.svm_slot(2 * i);
|
||||
if (2 * i + 1 < tiles.size()) {
|
||||
node.z = tiles[2 * i + 1];
|
||||
node.w = handle.svm_slot(false, 2 * i + 1);
|
||||
node.w = handle.svm_slot(2 * i + 1);
|
||||
}
|
||||
else {
|
||||
node.z = -1;
|
||||
@@ -437,19 +430,13 @@ void ImageTextureNode::compile(SVMCompiler &compiler)
|
||||
}
|
||||
else {
|
||||
assert(handle.num_tiles() == 1);
|
||||
/* Blend is a float between 0 and 1. Convert to 16 bit unsigned int to make room for vector_dx
|
||||
* and vector_dy. */
|
||||
uint blend = clamp((uint)(projection_blend * 65535.0f), 0, 0xffff);
|
||||
compiler.add_node(NODE_TEX_IMAGE_BOX,
|
||||
handle.svm_slot(),
|
||||
compiler.encode_uchar4(vector_offset,
|
||||
compiler.stack_assign_if_linked(color_out),
|
||||
compiler.stack_assign_if_linked(alpha_out),
|
||||
flags),
|
||||
compiler.encode_uchar4(blend >> 8,
|
||||
blend & 0xff,
|
||||
compiler.stack_assign(vector_dx),
|
||||
compiler.stack_assign(vector_dy)));
|
||||
__float_as_int(projection_blend));
|
||||
}
|
||||
|
||||
tex_mapping.compile_end(compiler, vector_in, vector_offset);
|
||||
@@ -471,13 +458,12 @@ void ImageTextureNode::compile(OSLCompiler &compiler)
|
||||
const bool compress_as_srgb = metadata.compress_as_srgb;
|
||||
const ustring known_colorspace = metadata.colorspace;
|
||||
|
||||
if (handle.svm_slot(true) == -1) {
|
||||
filename = compiler.scene->image_manager->get_mip_map_path(filename.string());
|
||||
if (handle.svm_slot() == -1) {
|
||||
compiler.parameter_texture(
|
||||
"filename", filename, compress_as_srgb ? u_colorspace_raw : known_colorspace);
|
||||
}
|
||||
else {
|
||||
compiler.parameter_texture("filename", handle.svm_slot(true));
|
||||
compiler.parameter_texture("filename", handle.svm_slot());
|
||||
}
|
||||
|
||||
const bool unassociate_alpha = !(ColorSpaceManager::colorspace_is_data(colorspace) ||
|
||||
@@ -487,6 +473,7 @@ void ImageTextureNode::compile(OSLCompiler &compiler)
|
||||
|
||||
compiler.parameter(this, "projection");
|
||||
compiler.parameter(this, "projection_blend");
|
||||
compiler.parameter("compress_as_srgb", compress_as_srgb);
|
||||
compiler.parameter("ignore_alpha", alpha_type == IMAGE_ALPHA_IGNORE);
|
||||
compiler.parameter("unassociate_alpha", !alpha_out->links.empty() && unassociate_alpha);
|
||||
compiler.parameter("is_float", is_float);
|
||||
@@ -531,8 +518,6 @@ NODE_DEFINE(EnvironmentTextureNode)
|
||||
SOCKET_BOOLEAN(animated, "Animated", false);
|
||||
|
||||
SOCKET_IN_POINT(vector, "Vector", zero_float3(), SocketType::LINK_POSITION);
|
||||
SOCKET_IN_POINT(vector_dx, "Vector_dx", zero_float3());
|
||||
SOCKET_IN_POINT(vector_dy, "Vector_dy", zero_float3());
|
||||
|
||||
SOCKET_OUT_COLOR(color, "Color");
|
||||
SOCKET_OUT_FLOAT(alpha, "Alpha");
|
||||
@@ -582,8 +567,6 @@ void EnvironmentTextureNode::compile(SVMCompiler &compiler)
|
||||
ShaderInput *vector_in = input("Vector");
|
||||
ShaderOutput *color_out = output("Color");
|
||||
ShaderOutput *alpha_out = output("Alpha");
|
||||
ShaderInput *vector_dx = input("Vector_dx");
|
||||
ShaderInput *vector_dy = input("Vector_dy");
|
||||
|
||||
if (handle.empty()) {
|
||||
ImageManager *image_manager = compiler.scene->image_manager;
|
||||
@@ -626,16 +609,17 @@ void EnvironmentTextureNode::compile(OSLCompiler &compiler)
|
||||
const bool compress_as_srgb = metadata.compress_as_srgb;
|
||||
const ustring known_colorspace = metadata.colorspace;
|
||||
|
||||
if (handle.svm_slot(true) == -1) {
|
||||
if (handle.svm_slot() == -1) {
|
||||
compiler.parameter_texture(
|
||||
"filename", filename, compress_as_srgb ? u_colorspace_raw : known_colorspace);
|
||||
}
|
||||
else {
|
||||
compiler.parameter_texture("filename", handle.svm_slot(true));
|
||||
compiler.parameter_texture("filename", handle.svm_slot());
|
||||
}
|
||||
|
||||
compiler.parameter(this, "projection");
|
||||
compiler.parameter(this, "interpolation");
|
||||
compiler.parameter("compress_as_srgb", compress_as_srgb);
|
||||
compiler.parameter("ignore_alpha", alpha_type == IMAGE_ALPHA_IGNORE);
|
||||
compiler.parameter("is_float", is_float);
|
||||
compiler.add(this, "node_environment_texture");
|
||||
@@ -985,7 +969,7 @@ void SkyTextureNode::compile(OSLCompiler &compiler)
|
||||
compiler.parameter_array("nishita_data", sunsky.nishita_data, 10);
|
||||
/* nishita texture */
|
||||
if (sky_type == NODE_SKY_NISHITA) {
|
||||
compiler.parameter_texture("filename", handle.svm_slot(true));
|
||||
compiler.parameter_texture("filename", handle.svm_slot());
|
||||
}
|
||||
compiler.add(this, "node_sky_texture");
|
||||
}
|
||||
@@ -1877,7 +1861,7 @@ void PointDensityTextureNode::compile(OSLCompiler &compiler)
|
||||
handle = image_manager->add_image(filename.string(), image_params());
|
||||
}
|
||||
|
||||
compiler.parameter_texture("filename", handle.svm_slot(true));
|
||||
compiler.parameter_texture("filename", handle.svm_slot());
|
||||
if (space == NODE_TEX_VOXEL_SPACE_WORLD) {
|
||||
compiler.parameter("mapping", tfm);
|
||||
compiler.parameter("use_mapping", 1);
|
||||
|
@@ -128,8 +128,6 @@ class ImageTextureNode : public ImageSlotTextureNode {
|
||||
NODE_SOCKET_API(float, projection_blend)
|
||||
NODE_SOCKET_API(bool, animated)
|
||||
NODE_SOCKET_API(float3, vector)
|
||||
NODE_SOCKET_API(float3, vector_dx)
|
||||
NODE_SOCKET_API(float3, vector_dy)
|
||||
NODE_SOCKET_API(array<int>, tiles)
|
||||
|
||||
protected:
|
||||
@@ -166,8 +164,6 @@ class EnvironmentTextureNode : public ImageSlotTextureNode {
|
||||
NODE_SOCKET_API(InterpolationType, interpolation)
|
||||
NODE_SOCKET_API(bool, animated)
|
||||
NODE_SOCKET_API(float3, vector)
|
||||
NODE_SOCKET_API(float3, vector_dx)
|
||||
NODE_SOCKET_API(float3, vector_dy)
|
||||
};
|
||||
|
||||
class SkyTextureNode : public TextureNode {
|
||||
|
@@ -46,7 +46,11 @@ CCL_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef WITH_OSL
|
||||
|
||||
/* Shared Shading System */
|
||||
/* Shared Texture and Shading System */
|
||||
|
||||
OSL::TextureSystem *OSLShaderManager::ts_shared = NULL;
|
||||
int OSLShaderManager::ts_shared_users = 0;
|
||||
thread_mutex OSLShaderManager::ts_shared_mutex;
|
||||
|
||||
OSL::ShadingSystem *OSLShaderManager::ss_shared = NULL;
|
||||
OSLRenderServices *OSLShaderManager::services_shared = NULL;
|
||||
@@ -106,7 +110,7 @@ void OSLShaderManager::device_update_specific(Device *device,
|
||||
device_free(device, dscene, scene);
|
||||
|
||||
/* set texture system */
|
||||
scene->image_manager->set_oiio_texture_system((void *)ts);
|
||||
scene->image_manager->set_osl_texture_system((void *)ts);
|
||||
|
||||
/* create shaders */
|
||||
OSLGlobals *og = (OSLGlobals *)device->osl_memory();
|
||||
@@ -186,6 +190,41 @@ void OSLShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *s
|
||||
og->background_state.reset();
|
||||
}
|
||||
|
||||
void OSLShaderManager::texture_system_init()
|
||||
{
|
||||
/* create texture system, shared between different renders to reduce memory usage */
|
||||
thread_scoped_lock lock(ts_shared_mutex);
|
||||
|
||||
if (ts_shared_users == 0) {
|
||||
ts_shared = TextureSystem::create(true);
|
||||
|
||||
ts_shared->attribute("automip", 1);
|
||||
ts_shared->attribute("autotile", 64);
|
||||
ts_shared->attribute("gray_to_rgb", 1);
|
||||
|
||||
/* effectively unlimited for now, until we support proper mipmap lookups */
|
||||
ts_shared->attribute("max_memory_MB", 16384);
|
||||
}
|
||||
|
||||
ts = ts_shared;
|
||||
ts_shared_users++;
|
||||
}
|
||||
|
||||
void OSLShaderManager::texture_system_free()
|
||||
{
|
||||
/* shared texture system decrease users and destroy if no longer used */
|
||||
thread_scoped_lock lock(ts_shared_mutex);
|
||||
ts_shared_users--;
|
||||
|
||||
if (ts_shared_users == 0) {
|
||||
ts_shared->invalidate_all(true);
|
||||
OSL::TextureSystem::destroy(ts_shared);
|
||||
ts_shared = NULL;
|
||||
}
|
||||
|
||||
ts = NULL;
|
||||
}
|
||||
|
||||
void OSLShaderManager::shading_system_init()
|
||||
{
|
||||
/* create shading system, shared between different renders to reduce memory usage */
|
||||
@@ -193,7 +232,7 @@ void OSLShaderManager::shading_system_init()
|
||||
|
||||
if (ss_shared_users == 0) {
|
||||
/* Must use aligned new due to concurrent hash map. */
|
||||
services_shared = util_aligned_new<OSLRenderServices>(ts);
|
||||
services_shared = util_aligned_new<OSLRenderServices>(ts_shared);
|
||||
|
||||
string shader_path = path_get("shader");
|
||||
# ifdef _WIN32
|
||||
@@ -208,7 +247,7 @@ void OSLShaderManager::shading_system_init()
|
||||
shader_path = string_to_ansi(shader_path);
|
||||
# endif
|
||||
|
||||
ss_shared = new OSL::ShadingSystem(services_shared, ts, &errhandler);
|
||||
ss_shared = new OSL::ShadingSystem(services_shared, ts_shared, &errhandler);
|
||||
ss_shared->attribute("lockgeom", 1);
|
||||
ss_shared->attribute("commonspace", "world");
|
||||
ss_shared->attribute("searchpath:shader", shader_path);
|
||||
|
@@ -103,14 +103,22 @@ class OSLShaderManager : public ShaderManager {
|
||||
const std::string &bytecode = "");
|
||||
|
||||
protected:
|
||||
void texture_system_init();
|
||||
void texture_system_free();
|
||||
|
||||
void shading_system_init();
|
||||
void shading_system_free();
|
||||
|
||||
OSL::ShadingSystem *ss;
|
||||
OSL::TextureSystem *ts;
|
||||
OSLRenderServices *services;
|
||||
OSL::ErrorHandler errhandler;
|
||||
map<string, OSLShaderInfo> loaded_shaders;
|
||||
|
||||
static OSL::TextureSystem *ts_shared;
|
||||
static thread_mutex ts_shared_mutex;
|
||||
static int ts_shared_users;
|
||||
|
||||
static OSL::ShadingSystem *ss_shared;
|
||||
static OSLRenderServices *services_shared;
|
||||
static thread_mutex ss_shared_mutex;
|
||||
|
@@ -138,50 +138,6 @@ class DeviceScene {
|
||||
DeviceScene(Device *device);
|
||||
};
|
||||
|
||||
/* Texture Cache Params */
|
||||
class TextureCacheParams {
|
||||
public:
|
||||
TextureCacheParams()
|
||||
: use_cache(false),
|
||||
cache_size(1024),
|
||||
tile_size(64),
|
||||
diffuse_blur(1.0f / 64.f),
|
||||
glossy_blur(0.0f),
|
||||
auto_convert(true),
|
||||
accept_unmipped(true),
|
||||
accept_untiled(true),
|
||||
auto_tile(true),
|
||||
auto_mip(true),
|
||||
use_custom_cache_path(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool modified(const TextureCacheParams ¶ms)
|
||||
{
|
||||
return !(use_cache == params.use_cache && cache_size == params.cache_size &&
|
||||
tile_size == params.tile_size && diffuse_blur == params.diffuse_blur &&
|
||||
glossy_blur == params.glossy_blur && auto_convert == params.auto_convert &&
|
||||
accept_unmipped == params.accept_unmipped &&
|
||||
accept_untiled == params.accept_untiled && auto_tile == params.auto_tile &&
|
||||
auto_mip == params.auto_mip &&
|
||||
use_custom_cache_path == params.use_custom_cache_path &&
|
||||
custom_cache_path == params.custom_cache_path);
|
||||
}
|
||||
|
||||
bool use_cache;
|
||||
int cache_size;
|
||||
int tile_size;
|
||||
float diffuse_blur;
|
||||
float glossy_blur;
|
||||
bool auto_convert;
|
||||
bool accept_unmipped;
|
||||
bool accept_untiled;
|
||||
bool auto_tile;
|
||||
bool auto_mip;
|
||||
bool use_custom_cache_path;
|
||||
string custom_cache_path;
|
||||
};
|
||||
|
||||
/* Scene Parameters */
|
||||
|
||||
class SceneParams {
|
||||
@@ -223,7 +179,6 @@ class SceneParams {
|
||||
int hair_subdivisions;
|
||||
CurveShapeType hair_shape;
|
||||
int texture_limit;
|
||||
TextureCacheParams texture;
|
||||
|
||||
bool background;
|
||||
|
||||
@@ -249,7 +204,7 @@ class SceneParams {
|
||||
use_bvh_unaligned_nodes == params.use_bvh_unaligned_nodes &&
|
||||
num_bvh_time_steps == params.num_bvh_time_steps &&
|
||||
hair_subdivisions == params.hair_subdivisions && hair_shape == params.hair_shape &&
|
||||
texture_limit == params.texture_limit) || texture.modified(params.texture);
|
||||
texture_limit == params.texture_limit);
|
||||
}
|
||||
|
||||
int curve_subdivisions()
|
||||
|
@@ -33,13 +33,10 @@
|
||||
#include "render/tables.h"
|
||||
|
||||
#include "util/util_foreach.h"
|
||||
#include "util/util_logging.h"
|
||||
#include "util/util_murmurhash.h"
|
||||
#include "util/util_task.h"
|
||||
#include "util/util_transform.h"
|
||||
|
||||
#include "kernel/kernel_oiio_globals.h"
|
||||
#include <OpenImageIO/texture.h>
|
||||
#ifdef WITH_OCIO
|
||||
# include <OpenColorIO/OpenColorIO.h>
|
||||
namespace OCIO = OCIO_NAMESPACE;
|
||||
@@ -48,7 +45,6 @@ namespace OCIO = OCIO_NAMESPACE;
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
thread_mutex ShaderManager::lookup_table_mutex;
|
||||
|
||||
vector<float> ShaderManager::beckmann_table;
|
||||
bool ShaderManager::beckmann_table_ready = false;
|
||||
|
||||
@@ -495,37 +491,10 @@ void ShaderManager::device_update_common(Device *device,
|
||||
Progress & /*progress*/)
|
||||
{
|
||||
dscene->shaders.free();
|
||||
|
||||
if (scene->shaders.size() == 0)
|
||||
return;
|
||||
|
||||
if (device->info.type == DEVICE_CPU &&
|
||||
(scene->params.shadingsystem == SHADINGSYSTEM_OSL || scene->params.texture.use_cache)) {
|
||||
/* set texture system */
|
||||
scene->image_manager->set_oiio_texture_system((void *)ts);
|
||||
OIIOGlobals *oiio_globals = (OIIOGlobals *)device->oiio_memory();
|
||||
if (oiio_globals) {
|
||||
/* update attributes from scene parms */
|
||||
ts->attribute("autotile",
|
||||
scene->params.texture.auto_tile ? scene->params.texture.tile_size : 0);
|
||||
ts->attribute("automip", scene->params.texture.auto_mip ? 1 : 0);
|
||||
ts->attribute("accept_unmipped", scene->params.texture.accept_unmipped ? 1 : 0);
|
||||
ts->attribute("accept_untiled", scene->params.texture.accept_untiled ? 1 : 0);
|
||||
ts->attribute("max_memory_MB",
|
||||
scene->params.texture.cache_size > 0 ?
|
||||
(float)scene->params.texture.cache_size :
|
||||
16384.0f);
|
||||
ts->attribute("latlong_up", "z");
|
||||
ts->attribute("flip_t", 1);
|
||||
ts->attribute("max_tile_channels", 1);
|
||||
if (scene->params.texture_limit > 0) {
|
||||
ts->attribute("max_mip_res", scene->params.texture_limit);
|
||||
}
|
||||
oiio_globals->tex_sys = ts;
|
||||
oiio_globals->diffuse_blur = scene->params.texture.diffuse_blur;
|
||||
oiio_globals->glossy_blur = scene->params.texture.glossy_blur;
|
||||
}
|
||||
}
|
||||
|
||||
KernelShader *kshader = dscene->shaders.alloc(scene->shaders.size());
|
||||
bool has_volumes = false;
|
||||
bool has_transparent_shadow = false;
|
||||
@@ -781,22 +750,6 @@ void ShaderManager::free_memory()
|
||||
ColorSpaceManager::free_memory();
|
||||
}
|
||||
|
||||
void ShaderManager::texture_system_init()
|
||||
{
|
||||
ts = TextureSystem::create(true);
|
||||
ts->attribute("gray_to_rgb", 1);
|
||||
ts->attribute("forcefloat", 1);
|
||||
}
|
||||
|
||||
void ShaderManager::texture_system_free()
|
||||
{
|
||||
VLOG(1) << ts->getstats(2);
|
||||
ts->reset_stats();
|
||||
ts->invalidate_all(true);
|
||||
TextureSystem::destroy(ts);
|
||||
ts = NULL;
|
||||
}
|
||||
|
||||
float ShaderManager::linear_rgb_to_gray(float3 c)
|
||||
{
|
||||
return dot(c, rgb_to_y);
|
||||
|
@@ -23,14 +23,11 @@
|
||||
# include <OSL/oslexec.h>
|
||||
#endif
|
||||
|
||||
#include <OpenImageIO/texture.h>
|
||||
|
||||
#include "kernel/kernel_types.h"
|
||||
#include "render/attribute.h"
|
||||
|
||||
#include "graph/node.h"
|
||||
|
||||
#include "kernel/kernel_oiio_globals.h"
|
||||
#include "util/util_map.h"
|
||||
#include "util/util_param.h"
|
||||
#include "util/util_string.h"
|
||||
@@ -252,11 +249,6 @@ class ShaderManager {
|
||||
|
||||
thread_spin_lock attribute_lock_;
|
||||
|
||||
void texture_system_init();
|
||||
void texture_system_free();
|
||||
|
||||
OIIO::TextureSystem *ts;
|
||||
|
||||
float3 xyz_to_r;
|
||||
float3 xyz_to_g;
|
||||
float3 xyz_to_b;
|
||||
|
@@ -37,12 +37,10 @@ CCL_NAMESPACE_BEGIN
|
||||
|
||||
SVMShaderManager::SVMShaderManager()
|
||||
{
|
||||
texture_system_init();
|
||||
}
|
||||
|
||||
SVMShaderManager::~SVMShaderManager()
|
||||
{
|
||||
texture_system_free();
|
||||
}
|
||||
|
||||
void SVMShaderManager::reset(Scene * /*scene*/)
|
||||
|
@@ -50,7 +50,6 @@ typedef enum ImageDataType {
|
||||
IMAGE_DATA_TYPE_USHORT = 7,
|
||||
IMAGE_DATA_TYPE_NANOVDB_FLOAT = 8,
|
||||
IMAGE_DATA_TYPE_NANOVDB_FLOAT3 = 9,
|
||||
IMAGE_DATA_TYPE_OIIO = 10,
|
||||
|
||||
IMAGE_DATA_NUM_TYPES
|
||||
} ImageDataType;
|
||||
@@ -95,7 +94,6 @@ typedef struct TextureInfo {
|
||||
/* Transform for 3D textures. */
|
||||
uint use_transform_3d;
|
||||
Transform transform_3d;
|
||||
uint compress_as_srgb;
|
||||
} TextureInfo;
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -78,7 +78,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#ifdef WIN32
|
||||
# include "BLI_winstuff.h"
|
||||
|
@@ -33,6 +33,9 @@ set(SRC
|
||||
intern/generic_virtual_vector_array.cc
|
||||
intern/multi_function.cc
|
||||
intern/multi_function_builder.cc
|
||||
intern/multi_function_procedure.cc
|
||||
intern/multi_function_procedure_builder.cc
|
||||
intern/multi_function_procedure_executor.cc
|
||||
|
||||
FN_cpp_type.hh
|
||||
FN_cpp_type_make.hh
|
||||
@@ -48,6 +51,9 @@ set(SRC
|
||||
FN_multi_function_data_type.hh
|
||||
FN_multi_function_param_type.hh
|
||||
FN_multi_function_params.hh
|
||||
FN_multi_function_procedure.hh
|
||||
FN_multi_function_procedure_builder.hh
|
||||
FN_multi_function_procedure_executor.hh
|
||||
FN_multi_function_signature.hh
|
||||
)
|
||||
|
||||
@@ -62,6 +68,7 @@ if(WITH_GTESTS)
|
||||
tests/FN_cpp_type_test.cc
|
||||
tests/FN_generic_span_test.cc
|
||||
tests/FN_generic_vector_array_test.cc
|
||||
tests/FN_multi_function_procedure_test.cc
|
||||
tests/FN_multi_function_test.cc
|
||||
)
|
||||
set(TEST_LIB
|
||||
|
408
source/blender/functions/FN_multi_function_procedure.hh
Normal file
408
source/blender/functions/FN_multi_function_procedure.hh
Normal file
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup fn
|
||||
*/
|
||||
|
||||
#include "FN_multi_function.hh"
|
||||
|
||||
namespace blender::fn {
|
||||
|
||||
class MFVariable;
|
||||
class MFInstruction;
|
||||
class MFCallInstruction;
|
||||
class MFBranchInstruction;
|
||||
class MFDestructInstruction;
|
||||
class MFDummyInstruction;
|
||||
class MFReturnInstruction;
|
||||
class MFProcedure;
|
||||
|
||||
enum class MFInstructionType {
|
||||
Call,
|
||||
Branch,
|
||||
Destruct,
|
||||
Dummy,
|
||||
Return,
|
||||
};
|
||||
|
||||
class MFVariable : NonCopyable, NonMovable {
|
||||
private:
|
||||
MFDataType data_type_;
|
||||
Vector<MFInstruction *> users_;
|
||||
std::string name_;
|
||||
int id_;
|
||||
|
||||
friend MFProcedure;
|
||||
friend MFCallInstruction;
|
||||
friend MFBranchInstruction;
|
||||
friend MFDestructInstruction;
|
||||
|
||||
public:
|
||||
MFDataType data_type() const;
|
||||
Span<MFInstruction *> users();
|
||||
|
||||
StringRefNull name() const;
|
||||
void set_name(std::string name);
|
||||
|
||||
int id() const;
|
||||
};
|
||||
|
||||
class MFInstruction : NonCopyable, NonMovable {
|
||||
protected:
|
||||
MFInstructionType type_;
|
||||
Vector<MFInstruction *> prev_;
|
||||
|
||||
friend MFProcedure;
|
||||
friend MFCallInstruction;
|
||||
friend MFBranchInstruction;
|
||||
friend MFDestructInstruction;
|
||||
friend MFDummyInstruction;
|
||||
friend MFReturnInstruction;
|
||||
|
||||
public:
|
||||
MFInstructionType type() const;
|
||||
Span<MFInstruction *> prev();
|
||||
Span<const MFInstruction *> prev() const;
|
||||
};
|
||||
|
||||
class MFCallInstruction : public MFInstruction {
|
||||
private:
|
||||
const MultiFunction *fn_ = nullptr;
|
||||
MFInstruction *next_ = nullptr;
|
||||
MutableSpan<MFVariable *> params_;
|
||||
|
||||
friend MFProcedure;
|
||||
|
||||
public:
|
||||
const MultiFunction &fn() const;
|
||||
|
||||
MFInstruction *next();
|
||||
const MFInstruction *next() const;
|
||||
void set_next(MFInstruction *instruction);
|
||||
|
||||
void set_param_variable(int param_index, MFVariable *variable);
|
||||
void set_params(Span<MFVariable *> variables);
|
||||
Span<MFVariable *> params();
|
||||
Span<const MFVariable *> params() const;
|
||||
};
|
||||
|
||||
class MFBranchInstruction : public MFInstruction {
|
||||
private:
|
||||
MFVariable *condition_ = nullptr;
|
||||
MFInstruction *branch_true_ = nullptr;
|
||||
MFInstruction *branch_false_ = nullptr;
|
||||
|
||||
friend MFProcedure;
|
||||
|
||||
public:
|
||||
MFVariable *condition();
|
||||
const MFVariable *condition() const;
|
||||
void set_condition(MFVariable *variable);
|
||||
|
||||
MFInstruction *branch_true();
|
||||
const MFInstruction *branch_true() const;
|
||||
void set_branch_true(MFInstruction *instruction);
|
||||
|
||||
MFInstruction *branch_false();
|
||||
const MFInstruction *branch_false() const;
|
||||
void set_branch_false(MFInstruction *instruction);
|
||||
};
|
||||
|
||||
class MFDestructInstruction : public MFInstruction {
|
||||
private:
|
||||
MFVariable *variable_ = nullptr;
|
||||
MFInstruction *next_ = nullptr;
|
||||
|
||||
friend MFProcedure;
|
||||
|
||||
public:
|
||||
MFVariable *variable();
|
||||
const MFVariable *variable() const;
|
||||
void set_variable(MFVariable *variable);
|
||||
|
||||
MFInstruction *next();
|
||||
const MFInstruction *next() const;
|
||||
void set_next(MFInstruction *instruction);
|
||||
};
|
||||
|
||||
class MFDummyInstruction : public MFInstruction {
|
||||
private:
|
||||
MFInstruction *next_ = nullptr;
|
||||
|
||||
friend MFProcedure;
|
||||
|
||||
public:
|
||||
MFInstruction *next();
|
||||
const MFInstruction *next() const;
|
||||
void set_next(MFInstruction *instruction);
|
||||
};
|
||||
|
||||
class MFReturnInstruction : public MFInstruction {
|
||||
};
|
||||
|
||||
struct MFParameter {
|
||||
MFParamType::InterfaceType type;
|
||||
MFVariable *variable;
|
||||
};
|
||||
|
||||
struct ConstMFParameter {
|
||||
MFParamType::InterfaceType type;
|
||||
const MFVariable *variable;
|
||||
};
|
||||
|
||||
class MFProcedure : NonCopyable, NonMovable {
|
||||
private:
|
||||
LinearAllocator<> allocator_;
|
||||
Vector<MFCallInstruction *> call_instructions_;
|
||||
Vector<MFBranchInstruction *> branch_instructions_;
|
||||
Vector<MFDestructInstruction *> destruct_instructions_;
|
||||
Vector<MFDummyInstruction *> dummy_instructions_;
|
||||
Vector<MFReturnInstruction *> return_instructions_;
|
||||
Vector<MFVariable *> variables_;
|
||||
Vector<MFParameter> params_;
|
||||
MFInstruction *entry_ = nullptr;
|
||||
|
||||
public:
|
||||
MFProcedure() = default;
|
||||
~MFProcedure();
|
||||
|
||||
MFVariable &new_variable(MFDataType data_type, std::string name = "");
|
||||
MFCallInstruction &new_call_instruction(const MultiFunction &fn);
|
||||
MFBranchInstruction &new_branch_instruction();
|
||||
MFDestructInstruction &new_destruct_instruction();
|
||||
MFDummyInstruction &new_dummy_instruction();
|
||||
MFReturnInstruction &new_return_instruction();
|
||||
|
||||
void add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable);
|
||||
|
||||
Span<ConstMFParameter> params() const;
|
||||
|
||||
MFInstruction *entry();
|
||||
const MFInstruction *entry() const;
|
||||
void set_entry(MFInstruction &entry);
|
||||
|
||||
Span<MFVariable *> variables();
|
||||
Span<const MFVariable *> variables() const;
|
||||
|
||||
void assert_valid() const;
|
||||
|
||||
std::string to_dot() const;
|
||||
|
||||
bool validate() const;
|
||||
|
||||
private:
|
||||
bool validate_all_instruction_pointers_set() const;
|
||||
bool validate_all_params_provided() const;
|
||||
bool validate_same_variables_in_one_call() const;
|
||||
bool validate_parameters() const;
|
||||
bool validate_initialization() const;
|
||||
|
||||
struct InitState {
|
||||
bool can_be_initialized = false;
|
||||
bool can_be_uninitialized = false;
|
||||
};
|
||||
|
||||
InitState find_initialization_state_before_instruction(const MFInstruction &target_instruction,
|
||||
const MFVariable &variable) const;
|
||||
};
|
||||
|
||||
namespace multi_function_procedure_types {
|
||||
using MFVariable = fn::MFVariable;
|
||||
using MFInstruction = fn::MFInstruction;
|
||||
using MFCallInstruction = fn::MFCallInstruction;
|
||||
using MFBranchInstruction = fn::MFBranchInstruction;
|
||||
using MFDestructInstruction = fn::MFDestructInstruction;
|
||||
using MFProcedure = fn::MFProcedure;
|
||||
} // namespace multi_function_procedure_types
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* MFVariable inline methods.
|
||||
*/
|
||||
|
||||
inline MFDataType MFVariable::data_type() const
|
||||
{
|
||||
return data_type_;
|
||||
}
|
||||
|
||||
inline Span<MFInstruction *> MFVariable::users()
|
||||
{
|
||||
return users_;
|
||||
}
|
||||
|
||||
inline StringRefNull MFVariable::name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
inline int MFVariable::id() const
|
||||
{
|
||||
return id_;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* MFInstruction inline methods.
|
||||
*/
|
||||
|
||||
inline MFInstructionType MFInstruction::type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
inline Span<MFInstruction *> MFInstruction::prev()
|
||||
{
|
||||
return prev_;
|
||||
}
|
||||
|
||||
inline Span<const MFInstruction *> MFInstruction::prev() const
|
||||
{
|
||||
return prev_;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* MFCallInstruction inline methods.
|
||||
*/
|
||||
|
||||
inline const MultiFunction &MFCallInstruction::fn() const
|
||||
{
|
||||
return *fn_;
|
||||
}
|
||||
|
||||
inline MFInstruction *MFCallInstruction::next()
|
||||
{
|
||||
return next_;
|
||||
}
|
||||
|
||||
inline const MFInstruction *MFCallInstruction::next() const
|
||||
{
|
||||
return next_;
|
||||
}
|
||||
|
||||
inline Span<MFVariable *> MFCallInstruction::params()
|
||||
{
|
||||
return params_;
|
||||
}
|
||||
|
||||
inline Span<const MFVariable *> MFCallInstruction::params() const
|
||||
{
|
||||
return params_;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* MFBranchInstruction inline methods.
|
||||
*/
|
||||
|
||||
inline MFVariable *MFBranchInstruction::condition()
|
||||
{
|
||||
return condition_;
|
||||
}
|
||||
|
||||
inline const MFVariable *MFBranchInstruction::condition() const
|
||||
{
|
||||
return condition_;
|
||||
}
|
||||
|
||||
inline MFInstruction *MFBranchInstruction::branch_true()
|
||||
{
|
||||
return branch_true_;
|
||||
}
|
||||
|
||||
inline const MFInstruction *MFBranchInstruction::branch_true() const
|
||||
{
|
||||
return branch_true_;
|
||||
}
|
||||
|
||||
inline MFInstruction *MFBranchInstruction::branch_false()
|
||||
{
|
||||
return branch_false_;
|
||||
}
|
||||
|
||||
inline const MFInstruction *MFBranchInstruction::branch_false() const
|
||||
{
|
||||
return branch_false_;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* MFDestructInstruction inline methods.
|
||||
*/
|
||||
|
||||
inline MFVariable *MFDestructInstruction::variable()
|
||||
{
|
||||
return variable_;
|
||||
}
|
||||
|
||||
inline const MFVariable *MFDestructInstruction::variable() const
|
||||
{
|
||||
return variable_;
|
||||
}
|
||||
|
||||
inline MFInstruction *MFDestructInstruction::next()
|
||||
{
|
||||
return next_;
|
||||
}
|
||||
|
||||
inline const MFInstruction *MFDestructInstruction::next() const
|
||||
{
|
||||
return next_;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* MFDummyInstruction inline methods.
|
||||
*/
|
||||
|
||||
inline MFInstruction *MFDummyInstruction::next()
|
||||
{
|
||||
return next_;
|
||||
}
|
||||
|
||||
inline const MFInstruction *MFDummyInstruction::next() const
|
||||
{
|
||||
return next_;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* MFProcedure inline methods.
|
||||
*/
|
||||
|
||||
inline Span<ConstMFParameter> MFProcedure::params() const
|
||||
{
|
||||
static_assert(sizeof(MFParameter) == sizeof(ConstMFParameter));
|
||||
return params_.as_span().cast<ConstMFParameter>();
|
||||
}
|
||||
|
||||
inline MFInstruction *MFProcedure::entry()
|
||||
{
|
||||
return entry_;
|
||||
}
|
||||
|
||||
inline const MFInstruction *MFProcedure::entry() const
|
||||
{
|
||||
return entry_;
|
||||
}
|
||||
|
||||
inline Span<MFVariable *> MFProcedure::variables()
|
||||
{
|
||||
return variables_;
|
||||
}
|
||||
|
||||
inline Span<const MFVariable *> MFProcedure::variables() const
|
||||
{
|
||||
return variables_;
|
||||
}
|
||||
|
||||
} // namespace blender::fn
|
251
source/blender/functions/FN_multi_function_procedure_builder.hh
Normal file
251
source/blender/functions/FN_multi_function_procedure_builder.hh
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup fn
|
||||
*/
|
||||
|
||||
#include "FN_multi_function_procedure.hh"
|
||||
|
||||
namespace blender::fn {
|
||||
|
||||
class MFInstructionCursor {
|
||||
private:
|
||||
MFInstruction *instruction_ = nullptr;
|
||||
/* Only used when it is a branch instruction. */
|
||||
bool branch_output_ = false;
|
||||
/* Only used when instruction is null. */
|
||||
bool is_entry_ = false;
|
||||
|
||||
public:
|
||||
MFInstructionCursor() = default;
|
||||
|
||||
MFInstructionCursor(MFCallInstruction &instruction);
|
||||
MFInstructionCursor(MFDestructInstruction &instruction);
|
||||
MFInstructionCursor(MFBranchInstruction &instruction, bool branch_output);
|
||||
MFInstructionCursor(MFDummyInstruction &instruction);
|
||||
|
||||
static MFInstructionCursor Entry();
|
||||
|
||||
void insert(MFProcedure &procedure, MFInstruction *new_instruction);
|
||||
};
|
||||
|
||||
class MFProcedureBuilder {
|
||||
private:
|
||||
MFProcedure *procedure_ = nullptr;
|
||||
Vector<MFInstructionCursor> cursors_;
|
||||
|
||||
public:
|
||||
struct Branch;
|
||||
struct Loop;
|
||||
|
||||
MFProcedureBuilder(MFProcedure &procedure,
|
||||
MFInstructionCursor initial_cursor = MFInstructionCursor::Entry());
|
||||
|
||||
MFProcedureBuilder(Span<MFProcedureBuilder *> builders);
|
||||
|
||||
MFProcedureBuilder(Branch &branch);
|
||||
|
||||
void set_cursor(const MFInstructionCursor &cursor);
|
||||
void set_cursor(Span<MFInstructionCursor> cursors);
|
||||
void set_cursor(Span<MFProcedureBuilder *> builders);
|
||||
void set_cursor_after_branch(Branch &branch);
|
||||
void set_cursor_after_loop(Loop &loop);
|
||||
|
||||
void add_destruct(MFVariable &variable);
|
||||
void add_destruct(Span<MFVariable *> variables);
|
||||
|
||||
MFReturnInstruction &add_return();
|
||||
|
||||
Branch add_branch(MFVariable &condition);
|
||||
|
||||
Loop add_loop();
|
||||
void add_loop_continue(Loop &loop);
|
||||
void add_loop_break(Loop &loop);
|
||||
|
||||
MFCallInstruction &add_call_with_no_variables(const MultiFunction &fn);
|
||||
MFCallInstruction &add_call_with_all_variables(const MultiFunction &fn,
|
||||
Span<MFVariable *> param_variables);
|
||||
|
||||
Vector<MFVariable *> add_call(const MultiFunction &fn,
|
||||
Span<MFVariable *> input_and_mutable_variables = {});
|
||||
|
||||
template<int OutputN>
|
||||
std::array<MFVariable *, OutputN> add_call(const MultiFunction &fn,
|
||||
Span<MFVariable *> input_and_mutable_variables = {});
|
||||
|
||||
void add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable);
|
||||
MFVariable &add_parameter(MFParamType param_type, std::string name = "");
|
||||
|
||||
MFVariable &add_input_parameter(MFDataType data_type, std::string name = "");
|
||||
template<typename T> MFVariable &add_single_input_parameter(std::string name = "");
|
||||
template<typename T> MFVariable &add_single_mutable_parameter(std::string name = "");
|
||||
|
||||
void add_output_parameter(MFVariable &variable);
|
||||
|
||||
private:
|
||||
void link_to_cursors(MFInstruction *instruction);
|
||||
};
|
||||
|
||||
struct MFProcedureBuilder::Branch {
|
||||
MFProcedureBuilder branch_true;
|
||||
MFProcedureBuilder branch_false;
|
||||
};
|
||||
|
||||
struct MFProcedureBuilder::Loop {
|
||||
MFInstruction *begin = nullptr;
|
||||
MFDummyInstruction *end = nullptr;
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* MFInstructionCursor inline methods.
|
||||
*/
|
||||
|
||||
inline MFInstructionCursor::MFInstructionCursor(MFCallInstruction &instruction)
|
||||
: instruction_(&instruction)
|
||||
{
|
||||
}
|
||||
|
||||
inline MFInstructionCursor::MFInstructionCursor(MFDestructInstruction &instruction)
|
||||
: instruction_(&instruction)
|
||||
{
|
||||
}
|
||||
|
||||
inline MFInstructionCursor::MFInstructionCursor(MFBranchInstruction &instruction,
|
||||
bool branch_output)
|
||||
: instruction_(&instruction), branch_output_(branch_output)
|
||||
{
|
||||
}
|
||||
|
||||
inline MFInstructionCursor::MFInstructionCursor(MFDummyInstruction &instruction)
|
||||
: instruction_(&instruction)
|
||||
{
|
||||
}
|
||||
|
||||
inline MFInstructionCursor MFInstructionCursor::Entry()
|
||||
{
|
||||
MFInstructionCursor cursor;
|
||||
cursor.is_entry_ = true;
|
||||
return cursor;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* MFProcedureBuilder inline methods.
|
||||
*/
|
||||
|
||||
inline MFProcedureBuilder::MFProcedureBuilder(Branch &branch)
|
||||
: MFProcedureBuilder(*branch.branch_true.procedure_)
|
||||
{
|
||||
this->set_cursor_after_branch(branch);
|
||||
}
|
||||
|
||||
inline MFProcedureBuilder::MFProcedureBuilder(MFProcedure &procedure,
|
||||
MFInstructionCursor initial_cursor)
|
||||
: procedure_(&procedure), cursors_({initial_cursor})
|
||||
{
|
||||
}
|
||||
|
||||
inline MFProcedureBuilder::MFProcedureBuilder(Span<MFProcedureBuilder *> builders)
|
||||
: MFProcedureBuilder(*builders[0]->procedure_)
|
||||
{
|
||||
this->set_cursor(builders);
|
||||
}
|
||||
|
||||
inline void MFProcedureBuilder::set_cursor(const MFInstructionCursor &cursor)
|
||||
{
|
||||
cursors_ = {cursor};
|
||||
}
|
||||
|
||||
inline void MFProcedureBuilder::set_cursor(Span<MFInstructionCursor> cursors)
|
||||
{
|
||||
cursors_ = cursors;
|
||||
}
|
||||
|
||||
inline void MFProcedureBuilder::set_cursor_after_branch(Branch &branch)
|
||||
{
|
||||
this->set_cursor({&branch.branch_false, &branch.branch_true});
|
||||
}
|
||||
|
||||
inline void MFProcedureBuilder::set_cursor_after_loop(Loop &loop)
|
||||
{
|
||||
this->set_cursor(MFInstructionCursor{*loop.end});
|
||||
}
|
||||
|
||||
inline void MFProcedureBuilder::set_cursor(Span<MFProcedureBuilder *> builders)
|
||||
{
|
||||
cursors_.clear();
|
||||
for (MFProcedureBuilder *builder : builders) {
|
||||
cursors_.extend(builder->cursors_);
|
||||
}
|
||||
}
|
||||
|
||||
template<int OutputN>
|
||||
inline std::array<MFVariable *, OutputN> MFProcedureBuilder::add_call(
|
||||
const MultiFunction &fn, Span<MFVariable *> input_and_mutable_variables)
|
||||
{
|
||||
Vector<MFVariable *> output_variables = this->add_call(fn, input_and_mutable_variables);
|
||||
BLI_assert(output_variables.size() == OutputN);
|
||||
|
||||
std::array<MFVariable *, OutputN> output_array;
|
||||
initialized_copy_n(output_variables.data(), OutputN, output_array.data());
|
||||
return output_array;
|
||||
}
|
||||
|
||||
inline void MFProcedureBuilder::add_parameter(MFParamType::InterfaceType interface_type,
|
||||
MFVariable &variable)
|
||||
{
|
||||
procedure_->add_parameter(interface_type, variable);
|
||||
}
|
||||
|
||||
inline MFVariable &MFProcedureBuilder::add_parameter(MFParamType param_type, std::string name)
|
||||
{
|
||||
MFVariable &variable = procedure_->new_variable(param_type.data_type(), std::move(name));
|
||||
this->add_parameter(param_type.interface_type(), variable);
|
||||
return variable;
|
||||
}
|
||||
|
||||
inline MFVariable &MFProcedureBuilder::add_input_parameter(MFDataType data_type, std::string name)
|
||||
{
|
||||
return this->add_parameter(MFParamType(MFParamType::Input, data_type), std::move(name));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline MFVariable &MFProcedureBuilder::add_single_input_parameter(std::string name)
|
||||
{
|
||||
return this->add_parameter(MFParamType::ForSingleInput(CPPType::get<T>()), std::move(name));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline MFVariable &MFProcedureBuilder::add_single_mutable_parameter(std::string name)
|
||||
{
|
||||
return this->add_parameter(MFParamType::ForMutableSingle(CPPType::get<T>()), std::move(name));
|
||||
}
|
||||
|
||||
inline void MFProcedureBuilder::add_output_parameter(MFVariable &variable)
|
||||
{
|
||||
this->add_parameter(MFParamType::Output, variable);
|
||||
}
|
||||
|
||||
inline void MFProcedureBuilder::link_to_cursors(MFInstruction *instruction)
|
||||
{
|
||||
for (MFInstructionCursor &cursor : cursors_) {
|
||||
cursor.insert(*procedure_, instruction);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::fn
|
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup fn
|
||||
*/
|
||||
|
||||
#include "FN_multi_function_procedure.hh"
|
||||
|
||||
namespace blender::fn {
|
||||
|
||||
class MFProcedureExecutor : public MultiFunction {
|
||||
private:
|
||||
MFSignature signature_;
|
||||
const MFProcedure &procedure_;
|
||||
|
||||
public:
|
||||
MFProcedureExecutor(std::string name, const MFProcedure &procedure);
|
||||
|
||||
void call(IndexMask mask, MFParams params, MFContext context) const override;
|
||||
};
|
||||
|
||||
} // namespace blender::fn
|
743
source/blender/functions/intern/multi_function_procedure.cc
Normal file
743
source/blender/functions/intern/multi_function_procedure.cc
Normal file
@@ -0,0 +1,743 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "FN_multi_function_procedure.hh"
|
||||
|
||||
#include "BLI_dot_export.hh"
|
||||
#include "BLI_stack.hh"
|
||||
|
||||
namespace blender::fn {
|
||||
|
||||
void MFVariable::set_name(std::string name)
|
||||
{
|
||||
name_ = std::move(name);
|
||||
}
|
||||
|
||||
void MFCallInstruction::set_next(MFInstruction *instruction)
|
||||
{
|
||||
if (next_ != nullptr) {
|
||||
next_->prev_.remove_first_occurrence_and_reorder(this);
|
||||
}
|
||||
if (instruction != nullptr) {
|
||||
instruction->prev_.append(this);
|
||||
}
|
||||
next_ = instruction;
|
||||
}
|
||||
|
||||
void MFCallInstruction::set_param_variable(int param_index, MFVariable *variable)
|
||||
{
|
||||
if (params_[param_index] != nullptr) {
|
||||
params_[param_index]->users_.remove_first_occurrence_and_reorder(this);
|
||||
}
|
||||
if (variable != nullptr) {
|
||||
BLI_assert(fn_->param_type(param_index).data_type() == variable->data_type());
|
||||
variable->users_.append(this);
|
||||
}
|
||||
params_[param_index] = variable;
|
||||
}
|
||||
|
||||
void MFCallInstruction::set_params(Span<MFVariable *> variables)
|
||||
{
|
||||
BLI_assert(variables.size() == params_.size());
|
||||
for (const int i : variables.index_range()) {
|
||||
this->set_param_variable(i, variables[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void MFBranchInstruction::set_condition(MFVariable *variable)
|
||||
{
|
||||
if (condition_ != nullptr) {
|
||||
condition_->users_.remove_first_occurrence_and_reorder(this);
|
||||
}
|
||||
if (variable != nullptr) {
|
||||
variable->users_.append(this);
|
||||
}
|
||||
condition_ = variable;
|
||||
}
|
||||
|
||||
void MFBranchInstruction::set_branch_true(MFInstruction *instruction)
|
||||
{
|
||||
if (branch_true_ != nullptr) {
|
||||
branch_true_->prev_.remove_first_occurrence_and_reorder(this);
|
||||
}
|
||||
if (instruction != nullptr) {
|
||||
instruction->prev_.append(this);
|
||||
}
|
||||
branch_true_ = instruction;
|
||||
}
|
||||
|
||||
void MFBranchInstruction::set_branch_false(MFInstruction *instruction)
|
||||
{
|
||||
if (branch_false_ != nullptr) {
|
||||
branch_false_->prev_.remove_first_occurrence_and_reorder(this);
|
||||
}
|
||||
if (instruction != nullptr) {
|
||||
instruction->prev_.append(this);
|
||||
}
|
||||
branch_false_ = instruction;
|
||||
}
|
||||
|
||||
void MFDestructInstruction::set_variable(MFVariable *variable)
|
||||
{
|
||||
if (variable_ != nullptr) {
|
||||
variable_->users_.remove_first_occurrence_and_reorder(this);
|
||||
}
|
||||
if (variable != nullptr) {
|
||||
variable->users_.append(this);
|
||||
}
|
||||
variable_ = variable;
|
||||
}
|
||||
|
||||
void MFDestructInstruction::set_next(MFInstruction *instruction)
|
||||
{
|
||||
if (next_ != nullptr) {
|
||||
next_->prev_.remove_first_occurrence_and_reorder(this);
|
||||
}
|
||||
if (instruction != nullptr) {
|
||||
instruction->prev_.append(this);
|
||||
}
|
||||
next_ = instruction;
|
||||
}
|
||||
|
||||
void MFDummyInstruction::set_next(MFInstruction *instruction)
|
||||
{
|
||||
if (next_ != nullptr) {
|
||||
next_->prev_.remove_first_occurrence_and_reorder(this);
|
||||
}
|
||||
if (instruction != nullptr) {
|
||||
instruction->prev_.append(this);
|
||||
}
|
||||
next_ = instruction;
|
||||
}
|
||||
|
||||
MFVariable &MFProcedure::new_variable(MFDataType data_type, std::string name)
|
||||
{
|
||||
MFVariable &variable = *allocator_.construct<MFVariable>().release();
|
||||
variable.name_ = std::move(name);
|
||||
variable.data_type_ = data_type;
|
||||
variable.id_ = variables_.size();
|
||||
variables_.append(&variable);
|
||||
return variable;
|
||||
}
|
||||
|
||||
MFCallInstruction &MFProcedure::new_call_instruction(const MultiFunction &fn)
|
||||
{
|
||||
MFCallInstruction &instruction = *allocator_.construct<MFCallInstruction>().release();
|
||||
instruction.type_ = MFInstructionType::Call;
|
||||
instruction.fn_ = &fn;
|
||||
instruction.params_ = allocator_.allocate_array<MFVariable *>(fn.param_amount());
|
||||
instruction.params_.fill(nullptr);
|
||||
call_instructions_.append(&instruction);
|
||||
return instruction;
|
||||
}
|
||||
|
||||
MFBranchInstruction &MFProcedure::new_branch_instruction()
|
||||
{
|
||||
MFBranchInstruction &instruction = *allocator_.construct<MFBranchInstruction>().release();
|
||||
instruction.type_ = MFInstructionType::Branch;
|
||||
branch_instructions_.append(&instruction);
|
||||
return instruction;
|
||||
}
|
||||
|
||||
MFDestructInstruction &MFProcedure::new_destruct_instruction()
|
||||
{
|
||||
MFDestructInstruction &instruction = *allocator_.construct<MFDestructInstruction>().release();
|
||||
instruction.type_ = MFInstructionType::Destruct;
|
||||
destruct_instructions_.append(&instruction);
|
||||
return instruction;
|
||||
}
|
||||
|
||||
MFDummyInstruction &MFProcedure::new_dummy_instruction()
|
||||
{
|
||||
MFDummyInstruction &instruction = *allocator_.construct<MFDummyInstruction>().release();
|
||||
instruction.type_ = MFInstructionType::Dummy;
|
||||
dummy_instructions_.append(&instruction);
|
||||
return instruction;
|
||||
}
|
||||
|
||||
MFReturnInstruction &MFProcedure::new_return_instruction()
|
||||
{
|
||||
MFReturnInstruction &instruction = *allocator_.construct<MFReturnInstruction>().release();
|
||||
instruction.type_ = MFInstructionType::Return;
|
||||
return_instructions_.append(&instruction);
|
||||
return instruction;
|
||||
}
|
||||
|
||||
void MFProcedure::add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable)
|
||||
{
|
||||
params_.append({interface_type, &variable});
|
||||
}
|
||||
|
||||
void MFProcedure::set_entry(MFInstruction &entry)
|
||||
{
|
||||
entry_ = &entry;
|
||||
}
|
||||
|
||||
void MFProcedure::assert_valid() const
|
||||
{
|
||||
/**
|
||||
* - Non parameter variables are destructed.
|
||||
* - At every instruction, every variable is either initialized or uninitialized.
|
||||
* - Input and mutable parameters of call instructions are initialized.
|
||||
* - Condition of branch instruction is initialized.
|
||||
* - Output parameters of call instructions are not initialized.
|
||||
* - Input parameters are never destructed.
|
||||
* - Mutable and output parameteres are initialized on every exit.
|
||||
* - No aliasing issues in call instructions (can happen when variable is used more than once).
|
||||
*/
|
||||
}
|
||||
|
||||
MFProcedure::~MFProcedure()
|
||||
{
|
||||
for (MFCallInstruction *instruction : call_instructions_) {
|
||||
instruction->~MFCallInstruction();
|
||||
}
|
||||
for (MFBranchInstruction *instruction : branch_instructions_) {
|
||||
instruction->~MFBranchInstruction();
|
||||
}
|
||||
for (MFDestructInstruction *instruction : destruct_instructions_) {
|
||||
instruction->~MFDestructInstruction();
|
||||
}
|
||||
for (MFDummyInstruction *instruction : dummy_instructions_) {
|
||||
instruction->~MFDummyInstruction();
|
||||
}
|
||||
for (MFReturnInstruction *instruction : return_instructions_) {
|
||||
instruction->~MFReturnInstruction();
|
||||
}
|
||||
for (MFVariable *variable : variables_) {
|
||||
variable->~MFVariable();
|
||||
}
|
||||
}
|
||||
|
||||
bool MFProcedure::validate() const
|
||||
{
|
||||
if (entry_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (!this->validate_all_instruction_pointers_set()) {
|
||||
return false;
|
||||
}
|
||||
if (!this->validate_all_params_provided()) {
|
||||
return false;
|
||||
}
|
||||
if (!this->validate_same_variables_in_one_call()) {
|
||||
return false;
|
||||
}
|
||||
if (!this->validate_parameters()) {
|
||||
return false;
|
||||
}
|
||||
if (!this->validate_initialization()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MFProcedure::validate_all_instruction_pointers_set() const
|
||||
{
|
||||
for (const MFCallInstruction *instruction : call_instructions_) {
|
||||
if (instruction->next_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const MFDestructInstruction *instruction : destruct_instructions_) {
|
||||
if (instruction->next_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const MFBranchInstruction *instruction : branch_instructions_) {
|
||||
if (instruction->branch_true_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (instruction->branch_false_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const MFDummyInstruction *instruction : dummy_instructions_) {
|
||||
if (instruction->next_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MFProcedure::validate_all_params_provided() const
|
||||
{
|
||||
for (const MFCallInstruction *instruction : call_instructions_) {
|
||||
for (const MFVariable *variable : instruction->params_) {
|
||||
if (variable == nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const MFBranchInstruction *instruction : branch_instructions_) {
|
||||
if (instruction->condition_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const MFDestructInstruction *instruction : destruct_instructions_) {
|
||||
if (instruction->variable_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MFProcedure::validate_same_variables_in_one_call() const
|
||||
{
|
||||
for (const MFCallInstruction *instruction : call_instructions_) {
|
||||
const MultiFunction &fn = *instruction->fn_;
|
||||
for (const int param_index : fn.param_indices()) {
|
||||
const MFParamType param_type = fn.param_type(param_index);
|
||||
const MFVariable *variable = instruction->params_[param_index];
|
||||
for (const int other_param_index : fn.param_indices()) {
|
||||
if (other_param_index == param_index) {
|
||||
continue;
|
||||
}
|
||||
const MFVariable *other_variable = instruction->params_[other_param_index];
|
||||
if (other_variable != variable) {
|
||||
continue;
|
||||
}
|
||||
if (ELEM(param_type.interface_type(), MFParamType::Mutable, MFParamType::Output)) {
|
||||
/* When a variable is used as mutable or output parameter, it can only be used once. */
|
||||
return false;
|
||||
}
|
||||
const MFParamType other_param_type = fn.param_type(other_param_index);
|
||||
/* A variable is allowed to be used as input more than once. */
|
||||
if (other_param_type.interface_type() != MFParamType::Input) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MFProcedure::validate_parameters() const
|
||||
{
|
||||
Set<const MFVariable *> variables;
|
||||
for (const MFParameter ¶m : params_) {
|
||||
/* One variable cannot be used as multiple parameters. */
|
||||
if (!variables.add(param.variable)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MFProcedure::validate_initialization() const
|
||||
{
|
||||
/* TODO: Issue warning when it maybe wrongly initialized. */
|
||||
for (const MFDestructInstruction *instruction : destruct_instructions_) {
|
||||
const MFVariable &variable = *instruction->variable_;
|
||||
const InitState state = this->find_initialization_state_before_instruction(*instruction,
|
||||
variable);
|
||||
if (!state.can_be_initialized) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const MFBranchInstruction *instruction : branch_instructions_) {
|
||||
const MFVariable &variable = *instruction->condition_;
|
||||
const InitState state = this->find_initialization_state_before_instruction(*instruction,
|
||||
variable);
|
||||
if (!state.can_be_initialized) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const MFCallInstruction *instruction : call_instructions_) {
|
||||
const MultiFunction &fn = *instruction->fn_;
|
||||
for (const int param_index : fn.param_indices()) {
|
||||
const MFParamType param_type = fn.param_type(param_index);
|
||||
const MFVariable &variable = *instruction->params_[param_index];
|
||||
const InitState state = this->find_initialization_state_before_instruction(*instruction,
|
||||
variable);
|
||||
switch (param_type.interface_type()) {
|
||||
case MFParamType::Input:
|
||||
case MFParamType::Mutable: {
|
||||
if (!state.can_be_initialized) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MFParamType::Output: {
|
||||
if (!state.can_be_uninitialized) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Set<const MFVariable *> variables_that_should_be_initialized_on_return;
|
||||
for (const MFParameter ¶m : params_) {
|
||||
if (ELEM(param.type, MFParamType::Mutable, MFParamType::Output)) {
|
||||
variables_that_should_be_initialized_on_return.add_new(param.variable);
|
||||
}
|
||||
}
|
||||
for (const MFReturnInstruction *instruction : return_instructions_) {
|
||||
for (const MFVariable *variable : variables_) {
|
||||
const InitState init_state = this->find_initialization_state_before_instruction(*instruction,
|
||||
*variable);
|
||||
if (variables_that_should_be_initialized_on_return.contains(variable)) {
|
||||
if (!init_state.can_be_initialized) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!init_state.can_be_uninitialized) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MFProcedure::InitState MFProcedure::find_initialization_state_before_instruction(
|
||||
const MFInstruction &target_instruction, const MFVariable &target_variable) const
|
||||
{
|
||||
InitState state;
|
||||
|
||||
auto check_entry_instruction = [&]() {
|
||||
bool caller_initialized_variable = false;
|
||||
for (const MFParameter ¶m : params_) {
|
||||
if (param.variable == &target_variable) {
|
||||
if (ELEM(param.type, MFParamType::Input, MFParamType::Mutable)) {
|
||||
caller_initialized_variable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (caller_initialized_variable) {
|
||||
state.can_be_initialized = true;
|
||||
}
|
||||
else {
|
||||
state.can_be_uninitialized = true;
|
||||
}
|
||||
};
|
||||
|
||||
if (&target_instruction == entry_) {
|
||||
check_entry_instruction();
|
||||
}
|
||||
|
||||
Set<const MFInstruction *> checked_instructions;
|
||||
Stack<const MFInstruction *> instructions_to_check;
|
||||
instructions_to_check.push_multiple(target_instruction.prev_);
|
||||
|
||||
while (!instructions_to_check.is_empty()) {
|
||||
const MFInstruction &instruction = *instructions_to_check.pop();
|
||||
if (!checked_instructions.add(&instruction)) {
|
||||
/* Skip if the instruction has been checked already. */
|
||||
continue;
|
||||
}
|
||||
bool state_modified = false;
|
||||
switch (instruction.type_) {
|
||||
case MFInstructionType::Call: {
|
||||
const MFCallInstruction &call_instruction = static_cast<const MFCallInstruction &>(
|
||||
instruction);
|
||||
const MultiFunction &fn = *call_instruction.fn_;
|
||||
for (const int param_index : fn.param_indices()) {
|
||||
if (call_instruction.params_[param_index] == &target_variable) {
|
||||
const MFParamType param_type = fn.param_type(param_index);
|
||||
if (param_type.interface_type() == MFParamType::Output) {
|
||||
state.can_be_initialized = true;
|
||||
state_modified = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Destruct: {
|
||||
const MFDestructInstruction &destruct_instruction =
|
||||
static_cast<const MFDestructInstruction &>(instruction);
|
||||
if (destruct_instruction.variable_ == &target_variable) {
|
||||
state.can_be_uninitialized = true;
|
||||
state_modified = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Branch:
|
||||
case MFInstructionType::Dummy:
|
||||
case MFInstructionType::Return: {
|
||||
/* These instruction types don't change the initialization state of variables. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!state_modified) {
|
||||
if (&instruction == entry_) {
|
||||
check_entry_instruction();
|
||||
}
|
||||
instructions_to_check.push_multiple(instruction.prev_);
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static bool has_to_be_block_begin(const MFProcedure &procedure, const MFInstruction &instruction)
|
||||
{
|
||||
if (procedure.entry() == &instruction) {
|
||||
return true;
|
||||
}
|
||||
if (instruction.prev().size() != 1) {
|
||||
return true;
|
||||
}
|
||||
if (instruction.prev()[0]->type() == MFInstructionType::Branch) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const MFInstruction &get_first_instruction_in_block(const MFProcedure &procedure,
|
||||
const MFInstruction &representative)
|
||||
{
|
||||
const MFInstruction *current = &representative;
|
||||
while (!has_to_be_block_begin(procedure, *current)) {
|
||||
current = current->prev()[0];
|
||||
if (current == &representative) {
|
||||
/* There is a loop without entry or exit, just break it up here. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *current;
|
||||
}
|
||||
|
||||
static const MFInstruction *get_next_instruction_in_block(const MFProcedure &procedure,
|
||||
const MFInstruction &instruction,
|
||||
const MFInstruction &block_begin)
|
||||
{
|
||||
const MFInstruction *next = nullptr;
|
||||
switch (instruction.type()) {
|
||||
case MFInstructionType::Call: {
|
||||
next = static_cast<const MFCallInstruction &>(instruction).next();
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Destruct: {
|
||||
next = static_cast<const MFDestructInstruction &>(instruction).next();
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Dummy: {
|
||||
next = static_cast<const MFDummyInstruction &>(instruction).next();
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Return:
|
||||
case MFInstructionType::Branch: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (next == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (next == &block_begin) {
|
||||
return nullptr;
|
||||
}
|
||||
if (has_to_be_block_begin(procedure, *next)) {
|
||||
return nullptr;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
static Vector<const MFInstruction *> get_instructions_in_block(const MFProcedure &procedure,
|
||||
const MFInstruction &representative)
|
||||
{
|
||||
Vector<const MFInstruction *> instructions;
|
||||
const MFInstruction &begin = get_first_instruction_in_block(procedure, representative);
|
||||
for (const MFInstruction *current = &begin; current != nullptr;
|
||||
current = get_next_instruction_in_block(procedure, *current, begin)) {
|
||||
instructions.append(current);
|
||||
}
|
||||
return instructions;
|
||||
}
|
||||
|
||||
static void variable_to_string(const MFVariable *variable, std::stringstream &ss)
|
||||
{
|
||||
if (variable == nullptr) {
|
||||
ss << "<none>";
|
||||
}
|
||||
else {
|
||||
ss << "$" << variable->id();
|
||||
if (!variable->name().is_empty()) {
|
||||
ss << "(" << variable->name() << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void instruction_to_string(const MFCallInstruction &instruction, std::stringstream &ss)
|
||||
{
|
||||
const MultiFunction &fn = instruction.fn();
|
||||
ss << fn.name() << " - ";
|
||||
for (const int param_index : fn.param_indices()) {
|
||||
const MFParamType param_type = fn.param_type(param_index);
|
||||
const MFVariable *variable = instruction.params()[param_index];
|
||||
switch (param_type.interface_type()) {
|
||||
case MFParamType::Input: {
|
||||
ss << "in";
|
||||
break;
|
||||
}
|
||||
case MFParamType::Mutable: {
|
||||
ss << "mut";
|
||||
break;
|
||||
}
|
||||
case MFParamType::Output: {
|
||||
ss << "out";
|
||||
break;
|
||||
}
|
||||
}
|
||||
ss << " ";
|
||||
variable_to_string(variable, ss);
|
||||
if (param_index < fn.param_amount() - 1) {
|
||||
ss << ", ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void instruction_to_string(const MFDestructInstruction &instruction, std::stringstream &ss)
|
||||
{
|
||||
ss << "Destruct ";
|
||||
variable_to_string(instruction.variable(), ss);
|
||||
}
|
||||
|
||||
static void instruction_to_string(const MFDummyInstruction &UNUSED(instruction),
|
||||
std::stringstream &ss)
|
||||
{
|
||||
ss << "Dummy";
|
||||
}
|
||||
|
||||
static void instruction_to_string(const MFReturnInstruction &UNUSED(instruction),
|
||||
std::stringstream &ss)
|
||||
{
|
||||
ss << "Return";
|
||||
}
|
||||
|
||||
static void instruction_to_string(const MFBranchInstruction &instruction, std::stringstream &ss)
|
||||
{
|
||||
ss << "Branch on ";
|
||||
variable_to_string(instruction.condition(), ss);
|
||||
}
|
||||
|
||||
std::string MFProcedure::to_dot() const
|
||||
{
|
||||
Vector<const MFInstruction *> all_instructions;
|
||||
all_instructions.extend(call_instructions_.begin(), call_instructions_.end());
|
||||
all_instructions.extend(branch_instructions_.begin(), branch_instructions_.end());
|
||||
all_instructions.extend(destruct_instructions_.begin(), destruct_instructions_.end());
|
||||
all_instructions.extend(dummy_instructions_.begin(), dummy_instructions_.end());
|
||||
all_instructions.extend(return_instructions_.begin(), return_instructions_.end());
|
||||
|
||||
Set<const MFInstruction *> handled_instructions;
|
||||
|
||||
dot::DirectedGraph digraph;
|
||||
Map<const MFInstruction *, dot::Node *> dot_nodes_by_begin;
|
||||
Map<const MFInstruction *, dot::Node *> dot_nodes_by_end;
|
||||
|
||||
for (const MFInstruction *representative : all_instructions) {
|
||||
if (handled_instructions.contains(representative)) {
|
||||
continue;
|
||||
}
|
||||
Vector<const MFInstruction *> block_instructions = get_instructions_in_block(*this,
|
||||
*representative);
|
||||
std::stringstream ss;
|
||||
|
||||
for (const MFInstruction *current : block_instructions) {
|
||||
handled_instructions.add_new(current);
|
||||
switch (current->type()) {
|
||||
case MFInstructionType::Call: {
|
||||
instruction_to_string(*static_cast<const MFCallInstruction *>(current), ss);
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Destruct: {
|
||||
instruction_to_string(*static_cast<const MFDestructInstruction *>(current), ss);
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Dummy: {
|
||||
instruction_to_string(*static_cast<const MFDummyInstruction *>(current), ss);
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Return: {
|
||||
instruction_to_string(*static_cast<const MFReturnInstruction *>(current), ss);
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Branch: {
|
||||
instruction_to_string(*static_cast<const MFBranchInstruction *>(current), ss);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ss << "\\l";
|
||||
}
|
||||
|
||||
dot::Node &dot_node = digraph.new_node(ss.str());
|
||||
dot_node.set_shape(dot::Attr_shape::Rectangle);
|
||||
dot_nodes_by_begin.add_new(block_instructions.first(), &dot_node);
|
||||
dot_nodes_by_end.add_new(block_instructions.last(), &dot_node);
|
||||
}
|
||||
|
||||
auto create_edge = [&](dot::Node &from_node,
|
||||
const MFInstruction *to_instruction) -> dot::DirectedEdge & {
|
||||
if (to_instruction == nullptr) {
|
||||
dot::Node &to_node = digraph.new_node("missing");
|
||||
to_node.set_shape(dot::Attr_shape::Diamond);
|
||||
return digraph.new_edge(from_node, to_node);
|
||||
}
|
||||
dot::Node &to_node = *dot_nodes_by_begin.lookup(to_instruction);
|
||||
return digraph.new_edge(from_node, to_node);
|
||||
};
|
||||
|
||||
for (auto item : dot_nodes_by_end.items()) {
|
||||
const MFInstruction &from_instruction = *item.key;
|
||||
dot::Node &from_node = *item.value;
|
||||
switch (from_instruction.type()) {
|
||||
case MFInstructionType::Call: {
|
||||
const MFInstruction *to_instruction =
|
||||
static_cast<const MFCallInstruction &>(from_instruction).next();
|
||||
create_edge(from_node, to_instruction);
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Destruct: {
|
||||
const MFInstruction *to_instruction =
|
||||
static_cast<const MFDestructInstruction &>(from_instruction).next();
|
||||
create_edge(from_node, to_instruction);
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Dummy: {
|
||||
const MFInstruction *to_instruction =
|
||||
static_cast<const MFDummyInstruction &>(from_instruction).next();
|
||||
create_edge(from_node, to_instruction);
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Return: {
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Branch: {
|
||||
const MFBranchInstruction &branch_instruction = static_cast<const MFBranchInstruction &>(
|
||||
from_instruction);
|
||||
const MFInstruction *to_true_instruction = branch_instruction.branch_true();
|
||||
const MFInstruction *to_false_instruction = branch_instruction.branch_false();
|
||||
create_edge(from_node, to_true_instruction).attributes.set("color", "#118811");
|
||||
create_edge(from_node, to_false_instruction).attributes.set("color", "#881111");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dot::Node &entry_node = digraph.new_node("Entry");
|
||||
entry_node.set_shape(dot::Attr_shape::Circle);
|
||||
create_edge(entry_node, entry_);
|
||||
|
||||
return digraph.to_dot_string();
|
||||
}
|
||||
|
||||
} // namespace blender::fn
|
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "FN_multi_function_procedure_builder.hh"
|
||||
|
||||
namespace blender::fn {
|
||||
|
||||
void MFInstructionCursor::insert(MFProcedure &procedure, MFInstruction *new_instruction)
|
||||
{
|
||||
if (instruction_ == nullptr) {
|
||||
if (is_entry_) {
|
||||
procedure.set_entry(*new_instruction);
|
||||
}
|
||||
else {
|
||||
/* The cursors points at nothing, nothing to do. */
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (instruction_->type()) {
|
||||
case MFInstructionType::Call: {
|
||||
static_cast<MFCallInstruction *>(instruction_)->set_next(new_instruction);
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Branch: {
|
||||
MFBranchInstruction &branch_instruction = *static_cast<MFBranchInstruction *>(
|
||||
instruction_);
|
||||
if (branch_output_) {
|
||||
branch_instruction.set_branch_true(new_instruction);
|
||||
}
|
||||
else {
|
||||
branch_instruction.set_branch_false(new_instruction);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Destruct: {
|
||||
static_cast<MFDestructInstruction *>(instruction_)->set_next(new_instruction);
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Dummy: {
|
||||
static_cast<MFDummyInstruction *>(instruction_)->set_next(new_instruction);
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Return: {
|
||||
/* It shouldn't be possible to build a cursor that points to a return instruction. */
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MFProcedureBuilder::add_destruct(MFVariable &variable)
|
||||
{
|
||||
MFDestructInstruction &instruction = procedure_->new_destruct_instruction();
|
||||
instruction.set_variable(&variable);
|
||||
this->link_to_cursors(&instruction);
|
||||
cursors_ = {MFInstructionCursor{instruction}};
|
||||
}
|
||||
|
||||
void MFProcedureBuilder::add_destruct(Span<MFVariable *> variables)
|
||||
{
|
||||
for (MFVariable *variable : variables) {
|
||||
this->add_destruct(*variable);
|
||||
}
|
||||
}
|
||||
|
||||
MFReturnInstruction &MFProcedureBuilder::add_return()
|
||||
{
|
||||
MFReturnInstruction &instruction = procedure_->new_return_instruction();
|
||||
this->link_to_cursors(&instruction);
|
||||
cursors_ = {};
|
||||
return instruction;
|
||||
}
|
||||
|
||||
MFCallInstruction &MFProcedureBuilder::add_call_with_no_variables(const MultiFunction &fn)
|
||||
{
|
||||
MFCallInstruction &instruction = procedure_->new_call_instruction(fn);
|
||||
this->link_to_cursors(&instruction);
|
||||
cursors_ = {MFInstructionCursor{instruction}};
|
||||
return instruction;
|
||||
}
|
||||
|
||||
MFCallInstruction &MFProcedureBuilder::add_call_with_all_variables(
|
||||
const MultiFunction &fn, Span<MFVariable *> param_variables)
|
||||
{
|
||||
MFCallInstruction &instruction = this->add_call_with_no_variables(fn);
|
||||
instruction.set_params(param_variables);
|
||||
return instruction;
|
||||
}
|
||||
|
||||
Vector<MFVariable *> MFProcedureBuilder::add_call(const MultiFunction &fn,
|
||||
Span<MFVariable *> input_and_mutable_variables)
|
||||
{
|
||||
Vector<MFVariable *> output_variables;
|
||||
MFCallInstruction &instruction = this->add_call_with_no_variables(fn);
|
||||
for (const int param_index : fn.param_indices()) {
|
||||
const MFParamType param_type = fn.param_type(param_index);
|
||||
switch (param_type.interface_type()) {
|
||||
case MFParamType::Input:
|
||||
case MFParamType::Mutable: {
|
||||
MFVariable *variable = input_and_mutable_variables.first();
|
||||
instruction.set_param_variable(param_index, variable);
|
||||
input_and_mutable_variables = input_and_mutable_variables.drop_front(1);
|
||||
break;
|
||||
}
|
||||
case MFParamType::Output: {
|
||||
MFVariable &variable = procedure_->new_variable(param_type.data_type());
|
||||
instruction.set_param_variable(param_index, &variable);
|
||||
output_variables.append(&variable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* All passed in variables should have been dropped in the loop above. */
|
||||
BLI_assert(input_and_mutable_variables.is_empty());
|
||||
return output_variables;
|
||||
}
|
||||
|
||||
MFProcedureBuilder::Branch MFProcedureBuilder::add_branch(MFVariable &condition)
|
||||
{
|
||||
MFBranchInstruction &instruction = procedure_->new_branch_instruction();
|
||||
instruction.set_condition(&condition);
|
||||
this->link_to_cursors(&instruction);
|
||||
/* Clear cursors because this builder ends here. */
|
||||
cursors_.clear();
|
||||
|
||||
Branch branch{*procedure_, *procedure_};
|
||||
branch.branch_true.set_cursor(MFInstructionCursor{instruction, true});
|
||||
branch.branch_false.set_cursor(MFInstructionCursor{instruction, false});
|
||||
return branch;
|
||||
}
|
||||
|
||||
MFProcedureBuilder::Loop MFProcedureBuilder::add_loop()
|
||||
{
|
||||
MFDummyInstruction &loop_begin = procedure_->new_dummy_instruction();
|
||||
MFDummyInstruction &loop_end = procedure_->new_dummy_instruction();
|
||||
this->link_to_cursors(&loop_begin);
|
||||
cursors_ = {MFInstructionCursor{loop_begin}};
|
||||
|
||||
Loop loop;
|
||||
loop.begin = &loop_begin;
|
||||
loop.end = &loop_end;
|
||||
|
||||
return loop;
|
||||
}
|
||||
|
||||
void MFProcedureBuilder::add_loop_continue(Loop &loop)
|
||||
{
|
||||
this->link_to_cursors(loop.begin);
|
||||
/* Clear cursors because this builder ends here. */
|
||||
cursors_.clear();
|
||||
}
|
||||
|
||||
void MFProcedureBuilder::add_loop_break(Loop &loop)
|
||||
{
|
||||
this->link_to_cursors(loop.end);
|
||||
/* Clear cursors because this builder ends here. */
|
||||
cursors_.clear();
|
||||
}
|
||||
|
||||
} // namespace blender::fn
|
1188
source/blender/functions/intern/multi_function_procedure_executor.cc
Normal file
1188
source/blender/functions/intern/multi_function_procedure_executor.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,344 @@
|
||||
/* Apache License, Version 2.0 */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "FN_multi_function_builder.hh"
|
||||
#include "FN_multi_function_procedure_builder.hh"
|
||||
#include "FN_multi_function_procedure_executor.hh"
|
||||
#include "FN_multi_function_test_common.hh"
|
||||
|
||||
namespace blender::fn::tests {
|
||||
|
||||
TEST(multi_function_procedure, SimpleTest)
|
||||
{
|
||||
/**
|
||||
* procedure(int var1, int var2, int *var4) {
|
||||
* int var3 = var1 + var2;
|
||||
* var4 = var2 + var3;
|
||||
* var4 += 10;
|
||||
* }
|
||||
*/
|
||||
|
||||
CustomMF_SI_SI_SO<int, int, int> add_fn{"add", [](int a, int b) { return a + b; }};
|
||||
CustomMF_SM<int> add_10_fn{"add_10", [](int &a) { a += 10; }};
|
||||
|
||||
MFProcedure procedure;
|
||||
MFProcedureBuilder builder{procedure};
|
||||
|
||||
MFVariable *var1 = &builder.add_single_input_parameter<int>();
|
||||
MFVariable *var2 = &builder.add_single_input_parameter<int>();
|
||||
auto [var3] = builder.add_call<1>(add_fn, {var1, var2});
|
||||
auto [var4] = builder.add_call<1>(add_fn, {var2, var3});
|
||||
builder.add_call(add_10_fn, {var4});
|
||||
builder.add_destruct({var1, var2, var3});
|
||||
builder.add_return();
|
||||
builder.add_output_parameter(*var4);
|
||||
|
||||
EXPECT_TRUE(procedure.validate());
|
||||
|
||||
MFProcedureExecutor executor{"My Procedure", procedure};
|
||||
|
||||
MFParamsBuilder params{executor, 3};
|
||||
MFContextBuilder context;
|
||||
|
||||
Array<int> input_array = {1, 2, 3};
|
||||
params.add_readonly_single_input(input_array.as_span());
|
||||
params.add_readonly_single_input_value(3);
|
||||
|
||||
Array<int> output_array(3);
|
||||
params.add_uninitialized_single_output(output_array.as_mutable_span());
|
||||
|
||||
executor.call(IndexRange(3), params, context);
|
||||
|
||||
EXPECT_EQ(output_array[0], 17);
|
||||
EXPECT_EQ(output_array[1], 18);
|
||||
EXPECT_EQ(output_array[2], 19);
|
||||
}
|
||||
|
||||
TEST(multi_function_procedure, BranchTest)
|
||||
{
|
||||
/**
|
||||
* procedure(int &var1, bool var2) {
|
||||
* if (var2) {
|
||||
* var1 += 100;
|
||||
* }
|
||||
* else {
|
||||
* var1 += 10;
|
||||
* }
|
||||
* var1 += 10;
|
||||
* }
|
||||
*/
|
||||
|
||||
CustomMF_SM<int> add_10_fn{"add_10", [](int &a) { a += 10; }};
|
||||
CustomMF_SM<int> add_100_fn{"add_100", [](int &a) { a += 100; }};
|
||||
|
||||
MFProcedure procedure;
|
||||
MFProcedureBuilder builder{procedure};
|
||||
|
||||
MFVariable *var1 = &builder.add_single_mutable_parameter<int>();
|
||||
MFVariable *var2 = &builder.add_single_input_parameter<bool>();
|
||||
|
||||
MFProcedureBuilder::Branch branch = builder.add_branch(*var2);
|
||||
branch.branch_false.add_call(add_10_fn, {var1});
|
||||
branch.branch_true.add_call(add_100_fn, {var1});
|
||||
builder.set_cursor_after_branch(branch);
|
||||
builder.add_call(add_10_fn, {var1});
|
||||
builder.add_destruct({var2});
|
||||
builder.add_return();
|
||||
|
||||
EXPECT_TRUE(procedure.validate());
|
||||
|
||||
MFProcedureExecutor procedure_fn{"Condition Test", procedure};
|
||||
MFParamsBuilder params(procedure_fn, 5);
|
||||
|
||||
Array<int> values_a = {1, 5, 3, 6, 2};
|
||||
Array<bool> values_cond = {true, false, true, true, false};
|
||||
|
||||
params.add_single_mutable(values_a.as_mutable_span());
|
||||
params.add_readonly_single_input(values_cond.as_span());
|
||||
|
||||
MFContextBuilder context;
|
||||
procedure_fn.call({1, 2, 3, 4}, params, context);
|
||||
|
||||
EXPECT_EQ(values_a[0], 1);
|
||||
EXPECT_EQ(values_a[1], 25);
|
||||
EXPECT_EQ(values_a[2], 113);
|
||||
EXPECT_EQ(values_a[3], 116);
|
||||
EXPECT_EQ(values_a[4], 22);
|
||||
}
|
||||
|
||||
TEST(multi_function_procedure, EvaluateOne)
|
||||
{
|
||||
/**
|
||||
* procedure(int var1, int var2) {
|
||||
* var2 = var1 + 10;
|
||||
* }
|
||||
*/
|
||||
|
||||
int tot_evaluations = 0;
|
||||
CustomMF_SI_SO<int, int> add_10_fn{"add_10", [&](int a) {
|
||||
tot_evaluations++;
|
||||
return a + 10;
|
||||
}};
|
||||
|
||||
MFProcedure procedure;
|
||||
MFProcedureBuilder builder{procedure};
|
||||
|
||||
MFVariable *var1 = &builder.add_single_input_parameter<int>();
|
||||
auto [var2] = builder.add_call<1>(add_10_fn, {var1});
|
||||
builder.add_destruct(*var1);
|
||||
builder.add_return();
|
||||
builder.add_output_parameter(*var2);
|
||||
|
||||
MFProcedureExecutor procedure_fn{"Evaluate One", procedure};
|
||||
MFParamsBuilder params{procedure_fn, 5};
|
||||
|
||||
Array<int> values_out = {1, 2, 3, 4, 5};
|
||||
params.add_readonly_single_input_value(1);
|
||||
params.add_uninitialized_single_output(values_out.as_mutable_span());
|
||||
|
||||
MFContextBuilder context;
|
||||
procedure_fn.call({0, 1, 3, 4}, params, context);
|
||||
|
||||
EXPECT_EQ(values_out[0], 11);
|
||||
EXPECT_EQ(values_out[1], 11);
|
||||
EXPECT_EQ(values_out[2], 3);
|
||||
EXPECT_EQ(values_out[3], 11);
|
||||
EXPECT_EQ(values_out[4], 11);
|
||||
/* We expect only one evaluation, because the input is constant. */
|
||||
EXPECT_EQ(tot_evaluations, 1);
|
||||
}
|
||||
|
||||
TEST(multi_function_procedure, SimpleLoop)
|
||||
{
|
||||
/**
|
||||
* procedure(int count, int *out) {
|
||||
* out = 1;
|
||||
* int index = 0'
|
||||
* loop {
|
||||
* if (index >= count) {
|
||||
* break;
|
||||
* }
|
||||
* out *= 2;
|
||||
* index += 1;
|
||||
* }
|
||||
* out += 1000;
|
||||
* }
|
||||
*/
|
||||
|
||||
CustomMF_Constant<int> const_1_fn{1};
|
||||
CustomMF_Constant<int> const_0_fn{0};
|
||||
CustomMF_SI_SI_SO<int, int, bool> greater_or_equal_fn{"greater or equal",
|
||||
[](int a, int b) { return a >= b; }};
|
||||
CustomMF_SM<int> double_fn{"double", [](int &a) { a *= 2; }};
|
||||
CustomMF_SM<int> add_1000_fn{"add 1000", [](int &a) { a += 1000; }};
|
||||
CustomMF_SM<int> add_1_fn{"add 1", [](int &a) { a += 1; }};
|
||||
|
||||
MFProcedure procedure;
|
||||
MFProcedureBuilder builder{procedure};
|
||||
|
||||
MFVariable *var_count = &builder.add_single_input_parameter<int>("count");
|
||||
auto [var_out] = builder.add_call<1>(const_1_fn);
|
||||
var_out->set_name("out");
|
||||
auto [var_index] = builder.add_call<1>(const_0_fn);
|
||||
var_index->set_name("index");
|
||||
|
||||
MFProcedureBuilder::Loop loop = builder.add_loop();
|
||||
auto [var_condition] = builder.add_call<1>(greater_or_equal_fn, {var_index, var_count});
|
||||
var_condition->set_name("condition");
|
||||
MFProcedureBuilder::Branch branch = builder.add_branch(*var_condition);
|
||||
branch.branch_true.add_destruct(*var_condition);
|
||||
branch.branch_true.add_loop_break(loop);
|
||||
branch.branch_false.add_destruct(*var_condition);
|
||||
builder.set_cursor_after_branch(branch);
|
||||
builder.add_call(double_fn, {var_out});
|
||||
builder.add_call(add_1_fn, {var_index});
|
||||
builder.add_loop_continue(loop);
|
||||
builder.set_cursor_after_loop(loop);
|
||||
builder.add_call(add_1000_fn, {var_out});
|
||||
builder.add_destruct({var_count, var_index});
|
||||
builder.add_return();
|
||||
builder.add_output_parameter(*var_out);
|
||||
|
||||
EXPECT_TRUE(procedure.validate());
|
||||
|
||||
MFProcedureExecutor procedure_fn{"Simple Loop", procedure};
|
||||
MFParamsBuilder params{procedure_fn, 5};
|
||||
|
||||
Array<int> counts = {4, 3, 7, 6, 4};
|
||||
Array<int> results(5, -1);
|
||||
|
||||
params.add_readonly_single_input(counts.as_span());
|
||||
params.add_uninitialized_single_output(results.as_mutable_span());
|
||||
|
||||
MFContextBuilder context;
|
||||
procedure_fn.call({0, 1, 3, 4}, params, context);
|
||||
|
||||
EXPECT_EQ(results[0], 1016);
|
||||
EXPECT_EQ(results[1], 1008);
|
||||
EXPECT_EQ(results[2], -1);
|
||||
EXPECT_EQ(results[3], 1064);
|
||||
EXPECT_EQ(results[4], 1016);
|
||||
}
|
||||
|
||||
TEST(multi_function_procedure, Vectors)
|
||||
{
|
||||
/**
|
||||
* procedure(vector<int> v1, vector<int> &v2, vector<int> *v3) {
|
||||
* v1.extend(v2);
|
||||
* int constant = 5;
|
||||
* v2.append(constant);
|
||||
* v2.extend(v1);
|
||||
* int len = sum(v2);
|
||||
* v3 = range(len);
|
||||
* }
|
||||
*/
|
||||
|
||||
CreateRangeFunction create_range_fn;
|
||||
ConcatVectorsFunction extend_fn;
|
||||
GenericAppendFunction append_fn{CPPType::get<int>()};
|
||||
SumVectorFunction sum_elements_fn;
|
||||
CustomMF_Constant<int> constant_5_fn{5};
|
||||
|
||||
MFProcedure procedure;
|
||||
MFProcedureBuilder builder{procedure};
|
||||
|
||||
MFVariable *var_v1 = &builder.add_input_parameter(MFDataType::ForVector<int>());
|
||||
MFVariable *var_v2 = &builder.add_parameter(MFParamType::ForMutableVector(CPPType::get<int>()));
|
||||
builder.add_call(extend_fn, {var_v1, var_v2});
|
||||
auto [var_constant] = builder.add_call<1>(constant_5_fn);
|
||||
builder.add_call(append_fn, {var_v2, var_constant});
|
||||
builder.add_destruct(*var_constant);
|
||||
builder.add_call(extend_fn, {var_v2, var_v1});
|
||||
auto [var_len] = builder.add_call<1>(sum_elements_fn, {var_v2});
|
||||
auto [var_v3] = builder.add_call<1>(create_range_fn, {var_len});
|
||||
builder.add_destruct({var_v1, var_len});
|
||||
builder.add_return();
|
||||
builder.add_output_parameter(*var_v3);
|
||||
|
||||
EXPECT_TRUE(procedure.validate());
|
||||
|
||||
MFProcedureExecutor procedure_fn{"Vectors", procedure};
|
||||
MFParamsBuilder params{procedure_fn, 5};
|
||||
|
||||
Array<int> v1 = {5, 2, 3};
|
||||
GVectorArray v2{CPPType::get<int>(), 5};
|
||||
GVectorArray v3{CPPType::get<int>(), 5};
|
||||
|
||||
int value_10 = 10;
|
||||
v2.append(0, &value_10);
|
||||
v2.append(4, &value_10);
|
||||
|
||||
params.add_readonly_vector_input(v1.as_span());
|
||||
params.add_vector_mutable(v2);
|
||||
params.add_vector_output(v3);
|
||||
|
||||
MFContextBuilder context;
|
||||
procedure_fn.call({0, 1, 3, 4}, params, context);
|
||||
|
||||
EXPECT_EQ(v2[0].size(), 6);
|
||||
EXPECT_EQ(v2[1].size(), 4);
|
||||
EXPECT_EQ(v2[2].size(), 0);
|
||||
EXPECT_EQ(v2[3].size(), 4);
|
||||
EXPECT_EQ(v2[4].size(), 6);
|
||||
|
||||
EXPECT_EQ(v3[0].size(), 35);
|
||||
EXPECT_EQ(v3[1].size(), 15);
|
||||
EXPECT_EQ(v3[2].size(), 0);
|
||||
EXPECT_EQ(v3[3].size(), 15);
|
||||
EXPECT_EQ(v3[4].size(), 35);
|
||||
}
|
||||
|
||||
TEST(multi_function_procedure, BufferReuse)
|
||||
{
|
||||
/**
|
||||
* procedure(int a, int *out) {
|
||||
* int b = a + 10;
|
||||
* int c = c + 10;
|
||||
* int d = d + 10;
|
||||
* int e = d + 10;
|
||||
* out = e + 10;
|
||||
* }
|
||||
*/
|
||||
|
||||
CustomMF_SI_SO<int, int> add_10_fn{"add 10", [](int a) { return a + 10; }};
|
||||
|
||||
MFProcedure procedure;
|
||||
MFProcedureBuilder builder{procedure};
|
||||
|
||||
MFVariable *var_a = &builder.add_single_input_parameter<int>();
|
||||
auto [var_b] = builder.add_call<1>(add_10_fn, {var_a});
|
||||
builder.add_destruct(*var_a);
|
||||
auto [var_c] = builder.add_call<1>(add_10_fn, {var_b});
|
||||
builder.add_destruct(*var_b);
|
||||
auto [var_d] = builder.add_call<1>(add_10_fn, {var_c});
|
||||
builder.add_destruct(*var_c);
|
||||
auto [var_e] = builder.add_call<1>(add_10_fn, {var_d});
|
||||
builder.add_destruct(*var_d);
|
||||
auto [var_out] = builder.add_call<1>(add_10_fn, {var_e});
|
||||
builder.add_destruct(*var_e);
|
||||
builder.add_return();
|
||||
builder.add_output_parameter(*var_out);
|
||||
|
||||
EXPECT_TRUE(procedure.validate());
|
||||
|
||||
MFProcedureExecutor procedure_fn{"Buffer Reuse", procedure};
|
||||
|
||||
Array<int> inputs = {4, 1, 6, 2, 3};
|
||||
Array<int> results(5, -1);
|
||||
|
||||
MFParamsBuilder params{procedure_fn, 5};
|
||||
params.add_readonly_single_input(inputs.as_span());
|
||||
params.add_uninitialized_single_output(results.as_mutable_span());
|
||||
|
||||
MFContextBuilder context;
|
||||
procedure_fn.call({0, 2, 3, 4}, params, context);
|
||||
|
||||
EXPECT_EQ(results[0], 54);
|
||||
EXPECT_EQ(results[1], -1);
|
||||
EXPECT_EQ(results[2], 56);
|
||||
EXPECT_EQ(results[3], 52);
|
||||
EXPECT_EQ(results[4], 53);
|
||||
}
|
||||
|
||||
} // namespace blender::fn::tests
|
Reference in New Issue
Block a user