Skip to content

Commit 056ba18

Browse files
authored
Add lazytrace option to avoid combinatoric optixTrace inlining (#1815)
OptiX inlines all calls to optixTrace at module compilation time. Due to OSL's lazy layer evaluation, if layer A calls layer B M times, and layer B calls layer C N times, that can lead to M*N inlines of optixTrace if layer C contains a trace operation. In practice we've observed single trace ops being inlined hundreds of times, leading to minutes-long shader compilations. This patch adds a new option, lazytrace, to run layers with trace ops unconditionally at the start of shader evaluation. This costs some potential performance in cases where the trace layer would never be evaluated, but removes all the compilation penalties that the inlining was presenting. --------- Signed-off-by: Chris Hellmuth <chellmuth@gmail.com>
1 parent f6b88b9 commit 056ba18

10 files changed

Lines changed: 82 additions & 0 deletions

File tree

src/cmake/testing.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ macro (osl_add_all_tests)
299299
isconstant
300300
layers layers-Ciassign layers-entry layers-lazy layers-lazyerror
301301
layers-nonlazycopy layers-repeatedoutputs
302+
lazytrace
302303
length-reg linearstep
303304
logic loop luminance-reg
304305
matrix matrix-reg matrix-arithmetic-reg

src/include/OSL/oslexec.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,8 @@ class OSLEXECPUBLIC ShadingSystem {
228228
/// output connections (1). For debugging.
229229
/// int lazyerror Run layers lazily even if they have error
230230
/// ops after optimization (1).
231+
/// int lazytrace Run layers lazily even if they have trace
232+
/// operations (1). Recommend 0 for OptiX.
231233
/// int lazy_userdata Retrieve userdata lazily (0).
232234
/// int userdata_isconnected Should interpolated=1 params (that may
233235
/// receive userdata) return true from

src/liboslexec/instance.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ ShaderInstance::ShaderInstance(ShaderMaster::ref master, string_view layername)
3535
, m_outgoing_connections(false)
3636
, m_renderer_outputs(false)
3737
, m_has_error_op(false)
38+
, m_has_trace_op(false)
3839
, m_merged_unused(false)
3940
, m_last_layer(false)
4041
, m_entry_layer(false)

src/liboslexec/llvm_gen.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ BackendLLVM::llvm_run_connected_layers(Symbol& sym, int symindex, int opnum,
187187
const Connection& con(inst()->connection(c));
188188
// If the connection gives a value to this param
189189
if (con.dst.param == symindex) {
190+
// Non-lazy layers are run upfront directly via llvm_call_layer.
191+
// Eliding them here doesn't change the semantics of execution,
192+
// but it will prevent optixTrace calls from being repeatedly
193+
// inlined when lazytrace=0.
194+
if (!group()[con.srclayer]->run_lazily())
195+
continue;
196+
190197
// already_run is a set of layers run for this particular op.
191198
// Just so we don't stupidly do several consecutive checks on
192199
// whether we ran this same layer. It's JUST for this op.
@@ -3022,6 +3029,12 @@ LLVMGEN(llvm_gen_trace)
30223029
};
30233030
llvm::Value* r = rop.ll.call_function("osl_trace", args);
30243031
rop.llvm_store_value(r, Result);
3032+
3033+
// Mark the instance as containing a trace call.
3034+
// With lazytrace=0, we will want to flag the instance
3035+
// for eager execution.
3036+
rop.inst()->has_trace_op(true);
3037+
30253038
return true;
30263039
}
30273040

src/liboslexec/oslexec_pvt.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,7 @@ class ShadingSystemImpl {
663663
}
664664
bool countlayerexecs() const { return m_countlayerexecs; }
665665
bool lazy_userdata() const { return m_lazy_userdata; }
666+
bool lazy_trace() const { return m_lazy_trace; }
666667
bool userdata_isconnected() const { return m_userdata_isconnected; }
667668
int profile() const { return m_profile; }
668669
bool no_noise() const { return m_no_noise; }
@@ -871,6 +872,7 @@ class ShadingSystemImpl {
871872
bool m_lazyunconnected; ///< Run lazily even if not connected?
872873
bool m_lazyerror; ///< Run lazily even if it has error op
873874
bool m_lazy_userdata; ///< Retrieve userdata lazily?
875+
bool m_lazy_trace; ///< Run lazily even if it has trace call
874876
bool m_userdata_isconnected; ///< Userdata params isconnected()?
875877
bool m_clearmemory; ///< Zero mem before running shader?
876878
bool m_debugnan; ///< Root out NaN's?
@@ -1261,6 +1263,9 @@ class ShaderInstance {
12611263
bool has_error_op() const { return m_has_error_op; }
12621264
void has_error_op(bool val) { m_has_error_op = val; }
12631265

1266+
bool has_trace_op() const { return m_has_trace_op; }
1267+
void has_trace_op(bool val) { m_has_trace_op = val; }
1268+
12641269
/// Should this instance only be run lazily (i.e., not
12651270
/// unconditionally)?
12661271
bool run_lazily() const
@@ -1284,6 +1289,10 @@ class ShaderInstance {
12841289
// Shaders with error ops are not lazy unless lazyerror is on.
12851290
if (!shadingsys().m_lazyerror && has_error_op())
12861291
return false;
1292+
1293+
if (!shadingsys().lazy_trace() && has_trace_op())
1294+
return false;
1295+
12871296
return true;
12881297
}
12891298

@@ -1512,6 +1521,7 @@ class ShaderInstance {
15121521
bool m_outgoing_connections; ///< Any outgoing connections?
15131522
bool m_renderer_outputs; ///< Any outputs params render outputs?
15141523
bool m_has_error_op; ///< Any error ops in the code?
1524+
bool m_has_trace_op; ///< Any trace ops in the code?
15151525
bool m_merged_unused; ///< Unused because of a merge
15161526
bool m_last_layer; ///< Is it the group's last layer?
15171527
bool m_entry_layer; ///< Is it an entry layer?

src/liboslexec/shadingsys.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,7 @@ ShadingSystemImpl::ShadingSystemImpl(RendererServices* renderer,
10451045
, m_lazyunconnected(true)
10461046
, m_lazyerror(true)
10471047
, m_lazy_userdata(false)
1048+
, m_lazy_trace(true)
10481049
, m_userdata_isconnected(false)
10491050
, m_clearmemory(false)
10501051
, m_debugnan(false)
@@ -1580,6 +1581,7 @@ ShadingSystemImpl::attribute(string_view name, TypeDesc type, const void* val)
15801581
ATTR_SET("lazyglobals", int, m_lazyglobals);
15811582
ATTR_SET("lazyunconnected", int, m_lazyunconnected);
15821583
ATTR_SET("lazyerror", int, m_lazyerror);
1584+
ATTR_SET("lazytrace", int, m_lazy_trace);
15831585
ATTR_SET("lazy_userdata", int, m_lazy_userdata);
15841586
ATTR_SET("userdata_isconnected", int, m_userdata_isconnected);
15851587
ATTR_SET("clearmemory", int, m_clearmemory);
@@ -1769,6 +1771,7 @@ ShadingSystemImpl::getattribute(string_view name, TypeDesc type, void* val)
17691771
ATTR_DECODE("lazylayers", int, m_lazylayers);
17701772
ATTR_DECODE("lazyglobals", int, m_lazyglobals);
17711773
ATTR_DECODE("lazyunconnected", int, m_lazyunconnected);
1774+
ATTR_DECODE("lazytrace", int, m_lazy_trace);
17721775
ATTR_DECODE("lazy_userdata", int, m_lazy_userdata);
17731776
ATTR_DECODE("userdata_isconnected", int, m_userdata_isconnected);
17741777
ATTR_DECODE("clearmemory", int, m_clearmemory);
@@ -2404,6 +2407,7 @@ ShadingSystemImpl::getstats(int level) const
24042407
BOOLOPT(lazyunconnected);
24052408
BOOLOPT(lazyerror);
24062409
BOOLOPT(lazy_userdata);
2410+
BOOLOPT(lazy_trace);
24072411
BOOLOPT(userdata_isconnected);
24082412
BOOLOPT(clearmemory);
24092413
BOOLOPT(debugnan);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright Contributors to the Open Shading Language project.
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4+
5+
shader main_shader(int x = 0)
6+
{
7+
printf("executing main shader\n");
8+
if (u == 99.5230942) {
9+
printf(" x = %i\n", x);
10+
}
11+
}

testsuite/lazytrace/ref/out.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Compiled main_shader.osl -> main_shader.oso
2+
Compiled trace_shader.osl -> trace_shader.oso
3+
Connect trace_layer.x to main_layer.x
4+
executing main shader
5+
6+
Connect trace_layer.x to main_layer.x
7+
executing trace shader
8+
Hit!
9+
executing main shader
10+

testsuite/lazytrace/run.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright Contributors to the Open Shading Language project.
4+
# SPDX-License-Identifier: BSD-3-Clause
5+
# https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
6+
7+
shader_commands = " ".join([
8+
"-shader trace_shader trace_layer",
9+
"-shader main_shader main_layer",
10+
"-connect trace_layer x main_layer x",
11+
])
12+
13+
# Run once with default (lazytrace=1), and once explicitly disabled
14+
command += testshade(shader_commands)
15+
command += testshade("{} --options 'lazytrace=0'".format(shader_commands))
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright Contributors to the Open Shading Language project.
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4+
5+
shader trace_shader(output int x = 0)
6+
{
7+
printf("executing trace shader\n");
8+
vector dir = vector(1.0, 1.0, 1.0) - P;
9+
10+
int result = trace(P, dir);
11+
if (result) {
12+
printf(" Hit!\n");
13+
x = 1;
14+
}
15+
}

0 commit comments

Comments
 (0)