Hi m1,
I didn’t check all versions, but you should be able to use MDL SDK 2019.1.1 or higher with OptiX 7.
With OptiX 7, we are not using callable programs for the texture runtime anymore, so you have to set the “tex_lookup_call_mode” backend option to “direct_call”. Thus “set_mdl_textures_ptx_string” is not used anymore.
As a starting point for the texture runtime, you can now use “texture_support_cuda.h” from MDL SDK examples/mdl_sdk/shared and include it in the CUDA file calling the functions generated by MDL SDK.
For loading the textures you can base on “Material_gpu_context::prepare_texture()” from “example_cuda_shared.h”.
This texture runtime uses the “res_data” parameter provided to the generated functions to access the textures. I put a pointer to the filled “Texture_handler” from “texture_support_cuda.h” in the hit group data.
mi::neuraylib::Resource_data res_data = {
NULL,
rt_data->texture_handler
};
So what about generating and calling the DF functions?
As you already expected, we are using direct callables for them:
We generate the target code and load the textures:
state.mdl_helper = new Mdl_helper(
state.context,
/*mdl_textures_ptx_path=*/ "",
module_path,
/*num_texture_spaces=*/ 1,
/*num_texture_results=*/ 16 );
mi::base::Handle<mi::neuraylib::ITransaction> transaction =
state.mdl_helper->create_transaction();
std::vector<mi::neuraylib::Target_function_description> descs;
descs.push_back(
mi::neuraylib::Target_function_description(
"surface.scattering",
"__direct_callable__mdlcode"));
Material_info *mat_info = nullptr;
mi::base::Handle<const mi::neuraylib::ITarget_code> target_code(
state.mdl_helper->compile_mdl_material(
transaction.get(),
material_name.c_str(),
descs,
/*class_compilation=*/ true,
&mat_info));
if (target_code->get_texture_count() > 1) {
for (mi::Size i = 1, n = target_code->get_texture_count(); i < n; ++i) {
state.mdl_helper->prepare_texture(
transaction.get(),
target_code.get(),
i,
state.mdl_textures);
}
}
Create an OptiX module from the resulting PTX code:
OptixModuleCompileOptions moduleCompileOptions = {};
moduleCompileOptions.maxRegisterCount = 0;
moduleCompileOptions.optLevel = OPTIX_COMPILE_OPTIMIZATION_DEFAULT;
moduleCompileOptions.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO;
OptixPipelineCompileOptions pipelineCompileOptions = {};
pipelineCompileOptions.usesMotionBlur = false;
pipelineCompileOptions.traversableGraphFlags = OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_SINGLE_LEVEL_INSTANCING;
pipelineCompileOptions.numPayloadValues = 2;
pipelineCompileOptions.numAttributeValues = 2;
pipelineCompileOptions.exceptionFlags = OPTIX_EXCEPTION_FLAG_NONE;
pipelineCompileOptions.pipelineLaunchParamsVariableName = "params";
OptixModule module;
OPTIX_CHECK( optixModuleCreateFromPTX(
state.context,
&moduleCompileOptions,
&pipelineCompileOptions,
target_code->get_code(),
target_code->get_code_size(),
0,
0,
&module ) );
Create a program group (per material) for the functions:
size_t callable_base_index = state.mdl_callables_groups.size();
state.mdl_callables_groups.resize(callable_base_index + 4);
OptixProgramGroupOptions callableProgramGroupOptions = {};
OptixProgramGroupDesc callableProgramGroupDesc[4] = {};
callableProgramGroupDesc[0].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES;
callableProgramGroupDesc[0].callables.moduleDC = module;
callableProgramGroupDesc[0].callables.entryFunctionNameDC = "__direct_callable__mdlcode_init";
callableProgramGroupDesc[1].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES;
callableProgramGroupDesc[1].callables.moduleDC = module;
callableProgramGroupDesc[1].callables.entryFunctionNameDC = "__direct_callable__mdlcode_sample";
callableProgramGroupDesc[2].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES;
callableProgramGroupDesc[2].callables.moduleDC = module;
callableProgramGroupDesc[2].callables.entryFunctionNameDC = "__direct_callable__mdlcode_evaluate";
callableProgramGroupDesc[3].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES;
callableProgramGroupDesc[3].callables.moduleDC = module;
callableProgramGroupDesc[3].callables.entryFunctionNameDC = "__direct_callable__mdlcode_pdf";
OPTIX_CHECK( optixProgramGroupCreate(
state.context,
callableProgramGroupDesc,
4,
&callableProgramGroupOptions,
0,
0,
&state.mdl_callables_groups[callable_base_index] ) );
state.mdl_materials.push_back(MDLMaterial(mat_info, unsigned(callable_base_index)));
transaction->commit();
We save the “callable_base_index” here and later write it to the hit group data for the material together with the texture handler structure and the argument block of the material.
Add the program groups to the pipeline (when calling optixPipelineCreate) and add CallableSbtRecord objects for the to the SBT.
std::vector<CallableSbtRecord> callable_records;
callable_records.reserve(state.mdl_callables_groups.size());
for (OptixProgramGroup &group : state.mdl_callables_groups) {
callable_records.push_back(CallableSbtRecord());
OPTIX_CHECK( optixSbtRecordPackHeader( group, &callable_records.back() ) );
}
CUdeviceptr d_callable_records = gpuMemDup(callable_records);
[...]
state.sbt.callablesRecordBase = d_callable_records;
state.sbt.callablesRecordStrideInBytes = sizeof(CallableSbtRecord);
state.sbt.callablesRecordCount = unsigned(callable_records.size());
In the renderer code, we then call the generated functions using “optixDirectCall” using IDs based on the callable_base_index stored in the hit groups:
const int callable_index_init = rt_data->mdl_callable_base_index;
const int callable_index_sample = rt_data->mdl_callable_base_index + 1;
const int callable_index_eval = rt_data->mdl_callable_base_index + 2;
optixDirectCall<void>(callable_index_init, &state, &res_data, /*exception_state=*/ nullptr, rt_data->arg_block);
[...]
optixDirectCall<void>(callable_index_eval, &eval_data, &state, &res_data, /*exception_state=*/ nullptr, rt_data->arg_block);
[...]
optixDirectCall<void>(callable_index_sample, &sample_data, &state, &res_data, /*exception_state=*/ nullptr, rt_data->arg_block);
I hope that helps as a starter and I hope I didn’t forget any relevant details.
If you need more information, just ask!
Best regards
Moritz