From 58bb38ea504ee33778f6b99fa487b9953c0ca976 Mon Sep 17 00:00:00 2001 From: Tuomas Tonteri Date: Mon, 17 Jun 2024 15:34:49 +0300 Subject: [PATCH] Add new testminimal and closure-string test. Signed-off-by: Tuomas Tonteri --- CMakeLists.txt | 9 +- src/cmake/testing.cmake | 2 +- src/include/OSL/llvm_util.h | 10 +- src/liboslexec/batched_backendllvm.cpp | 10 ++ src/liboslexec/batched_backendllvm.h | 3 + src/liboslexec/batched_llvm_gen.cpp | 2 +- src/liboslexec/llvm_util.cpp | 11 +- src/liboslexec/wide/wide_opmatrix.cpp | 107 ++++++----- src/testminimal/CMakeLists.txt | 30 ++++ src/testminimal/oslmaterial.cpp | 199 +++++++++++++++++++++ src/testminimal/oslmaterial.h | 235 +++++++++++++++++++++++++ src/testminimal/testminimal.cpp | 143 +++++++++++++++ testsuite/closure-string/BATCHED | 0 testsuite/closure-string/ref/out.txt | 3 + testsuite/closure-string/run.py | 7 + testsuite/closure-string/test.osl | 4 + testsuite/runtest.py | 8 + 17 files changed, 716 insertions(+), 67 deletions(-) create mode 100644 src/testminimal/CMakeLists.txt create mode 100644 src/testminimal/oslmaterial.cpp create mode 100644 src/testminimal/oslmaterial.h create mode 100644 src/testminimal/testminimal.cpp create mode 100644 testsuite/closure-string/BATCHED create mode 100644 testsuite/closure-string/ref/out.txt create mode 100755 testsuite/closure-string/run.py create mode 100644 testsuite/closure-string/test.osl diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c612a27e2..c3fe4b873b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,7 +95,7 @@ else () endif () set (OSL_LIBNAME_SUFFIX "" CACHE STRING "Optional name appended to ${PROJECT_NAME} libraries that are built") -option (OSL_BUILD_TESTS "Build the unit tests, testshade, testrender" ON) +option (OSL_BUILD_TESTS "Build the unit tests, testminimal, testshade, testrender" ON) if (WIN32) option (USE_LLVM_BITCODE "Generate embedded LLVM bitcode" OFF) else () @@ -113,10 +113,14 @@ set (OSL_SHADER_INSTALL_DIR "${CMAKE_INSTALL_FULL_DATADIR}/${PROJECT_NAME}/shade set (OSL_PTX_INSTALL_DIR "${CMAKE_INSTALL_FULL_DATADIR}/${PROJECT_NAME}/ptx" CACHE STRING "Directory where OptiX PTX files will be installed") set (CMAKE_DEBUG_POSTFIX "" CACHE STRING "Library naming postfix for Debug builds (e.g., '_debug')") -option (OSL_USTRINGREP_IS_HASH "Always use ustringhash for strings" OFF) +option (OSL_USTRINGREP_IS_HASH "Always use ustringhash for strings" ON) set (OSL_NO_DEFAULT_TEXTURESYSTEM OFF CACHE BOOL "Do not use create a raw OIIO::TextureSystem") + +if (OSL_USTRINGREP_IS_HASH) + add_definitions ("-DOSL_USTRINGREP_IS_HASH=1") +endif () if (OSL_NO_DEFAULT_TEXTURESYSTEM) add_definitions ("-DOSL_NO_DEFAULT_TEXTURESYSTEM=1") endif () @@ -220,6 +224,7 @@ add_subdirectory (src/oslc) add_subdirectory (src/oslinfo) if (OSL_BUILD_TESTS AND BUILD_TESTING) + add_subdirectory (src/testminimal) add_subdirectory (src/testshade) add_subdirectory (src/testrender) endif () diff --git a/src/cmake/testing.cmake b/src/cmake/testing.cmake index fc5e956b05..c3c0bee88e 100644 --- a/src/cmake/testing.cmake +++ b/src/cmake/testing.cmake @@ -270,7 +270,7 @@ macro (osl_add_all_tests) bug-array-heapoffsets bug-locallifetime bug-outputinit bug-param-duplicate bug-peep bug-return calculatenormal-reg - cellnoise closure closure-array closure-layered closure-parameters closure-zero closure-conditional + cellnoise closure closure-array closure-layered closure-parameters closure-string closure-zero closure-conditional color color-reg colorspace comparison complement-reg compile-buffer compassign-bool compassign-reg component-range diff --git a/src/include/OSL/llvm_util.h b/src/include/OSL/llvm_util.h index 7f112ccf52..be02f75b6a 100644 --- a/src/include/OSL/llvm_util.h +++ b/src/include/OSL/llvm_util.h @@ -1058,10 +1058,14 @@ class OSLEXECPUBLIC LLVM_Util { IRBuilder& builder(); int m_debug; - bool m_dumpasm = false; - bool m_jit_fma = false; - bool m_jit_aggressive = false; + bool m_dumpasm = false; + bool m_jit_fma = false; + bool m_jit_aggressive = false; +#ifndef OSL_USTRINGREP_IS_HASH UstringRep m_ustring_rep = UstringRep::charptr; +#else + UstringRep m_ustring_rep = UstringRep::hash; +#endif PerThreadInfo::Impl* m_thread; llvm::LLVMContext* m_llvm_context; llvm::Module* m_llvm_module; diff --git a/src/liboslexec/batched_backendllvm.cpp b/src/liboslexec/batched_backendllvm.cpp index e94122ef43..3acfbf2425 100644 --- a/src/liboslexec/batched_backendllvm.cpp +++ b/src/liboslexec/batched_backendllvm.cpp @@ -796,7 +796,17 @@ BatchedBackendLLVM::llvm_load_value(const Symbol& sym, int deriv, sym.forced_llvm_bool()); } +llvm::Value* +BatchedBackendLLVM::llvm_const_hash(string_view str) +{ + return llvm_const_hash(ustring(str)); +} +llvm::Value* +BatchedBackendLLVM::llvm_const_hash(ustring str) +{ + return ll.constant64((uint64_t)str.hash()); +} llvm::Value* BatchedBackendLLVM::llvm_load_mask(const Symbol& cond) diff --git a/src/liboslexec/batched_backendllvm.h b/src/liboslexec/batched_backendllvm.h index 9bafa77676..024e9f0463 100644 --- a/src/liboslexec/batched_backendllvm.h +++ b/src/liboslexec/batched_backendllvm.h @@ -102,6 +102,9 @@ class BatchedBackendLLVM : public OSOProcessorBase { bool op_is_uniform = true, bool index_is_uniform = true); + llvm::Value* llvm_const_hash(string_view str); + + llvm::Value* llvm_const_hash(ustring str); /// Given an llvm::Value* of a pointer (and the type of the data /// that it points to), Return the llvm::Value* corresponding to the diff --git a/src/liboslexec/batched_llvm_gen.cpp b/src/liboslexec/batched_llvm_gen.cpp index dacf4f0ba9..6bc55de0b9 100644 --- a/src/liboslexec/batched_llvm_gen.cpp +++ b/src/liboslexec/batched_llvm_gen.cpp @@ -3300,7 +3300,7 @@ LLVMGEN(llvm_gen_construct_triple) = { rop.sg_void_ptr(), rop.ll.void_ptr(transform), space_is_uniform ? rop.llvm_load_value(Space) : rop.llvm_void_ptr(Space), - rop.ll.constant(Strings::common), + rop.llvm_const_hash(Strings::common), rop.ll.mask_as_int(rop.ll.current_mask()) }; // Dynamically build function name diff --git a/src/liboslexec/llvm_util.cpp b/src/liboslexec/llvm_util.cpp index 3dd888cab0..470b8d2957 100644 --- a/src/liboslexec/llvm_util.cpp +++ b/src/liboslexec/llvm_util.cpp @@ -564,10 +564,13 @@ LLVM_Util::ustring_rep(UstringRep rep) } m_llvm_type_ustring_ptr = llvm::PointerType::get(m_llvm_type_ustring, 0); - // Batched versions haven't been updated to handle hash yet. - // For now leave them using the real ustring regardless of UstringRep - m_llvm_type_wide_ustring = llvm_vector_type(m_llvm_type_real_ustring, - m_vector_width); + if (m_ustring_rep == UstringRep::charptr) { + m_llvm_type_wide_ustring = llvm_vector_type(m_llvm_type_real_ustring, + m_vector_width); + } else { + OSL_ASSERT(m_ustring_rep == UstringRep::hash); + m_llvm_type_wide_ustring = llvm::Type::getInt64Ty(*m_llvm_context); + } m_llvm_type_wide_ustring_ptr = llvm::PointerType::get(m_llvm_type_wide_ustring, 0); } diff --git a/src/liboslexec/wide/wide_opmatrix.cpp b/src/liboslexec/wide/wide_opmatrix.cpp index 33b3ef2aac..d51249032f 100644 --- a/src/liboslexec/wide/wide_opmatrix.cpp +++ b/src/liboslexec/wide/wide_opmatrix.cpp @@ -532,33 +532,35 @@ makeIdentity(Masked wrm) OSL_FORCEINLINE Mask impl_get_uniform_from_matrix_masked(void* bsg_, Masked wrm, - const char* from) + ustringhash_pod from_) { + ustringhash from = ustringhash_from(from_); auto* bsg = reinterpret_cast(bsg_); ShadingContext* ctx = bsg->uniform.context; - if (USTR(from) == Strings::common - || USTR(from) == ctx->shadingsys().commonspace_synonym()) { + if (from == Hashes::common + || from == ctx->shadingsys().commonspace_synonym()) { makeIdentity(wrm); return wrm.mask(); } - if (USTR(from) == Strings::shader) { + if (from == Hashes::shader) { ctx->batched<__OSL_WIDTH>().renderer()->get_matrix( bsg, wrm, bsg->varying.shader2common, bsg->varying.time); // NOTE: matching scalar version of code which ignores the renderservices return value return wrm.mask(); } - if (USTR(from) == Strings::object) { + if (from == Hashes::object) { ctx->batched<__OSL_WIDTH>().renderer()->get_matrix( bsg, wrm, bsg->varying.object2common, bsg->varying.time); // NOTE: matching scalar version of code which ignores the renderservices return value return wrm.mask(); } - Mask succeeded = ctx->batched<__OSL_WIDTH>().renderer()->get_matrix( - bsg, wrm, USTR(from), bsg->varying.time); + Mask succeeded + = ctx->batched<__OSL_WIDTH>().renderer()->get_matrix(bsg, wrm, from, + bsg->varying.time); auto failedResults = wrm & succeeded.invert(); if (failedResults.mask().any_on()) { makeIdentity(failedResults); @@ -572,24 +574,24 @@ impl_get_uniform_from_matrix_masked(void* bsg_, Masked wrm, OSL_FORCEINLINE Mask impl_get_uniform_to_inverse_matrix_masked(void* bsg_, Masked wrm, - const char* to) + ustringhash_pod to_) { + ustringhash to = ustringhash_from(to_); auto* bsg = reinterpret_cast(bsg_); ShadingContext* ctx = bsg->uniform.context; - if (USTR(to) == Strings::common - || USTR(to) == ctx->shadingsys().commonspace_synonym()) { + if (to == Hashes::common || to == ctx->shadingsys().commonspace_synonym()) { makeIdentity(wrm); return wrm.mask(); } - if (USTR(to) == Strings::shader) { + if (to == Hashes::shader) { dispatch_get_inverse_matrix(ctx->batched<__OSL_WIDTH>().renderer(), bsg, wrm, bsg->varying.shader2common, bsg->varying.time); // NOTE: matching scalar version of code which ignores the renderservices return value return wrm.mask(); } - if (USTR(to) == Strings::object) { + if (to == Hashes::object) { dispatch_get_inverse_matrix(ctx->batched<__OSL_WIDTH>().renderer(), bsg, wrm, bsg->varying.object2common, bsg->varying.time); @@ -602,7 +604,7 @@ impl_get_uniform_to_inverse_matrix_masked(void* bsg_, Masked wrm, // so no need to make sure that the values are valid (assuming FP exceptions are disabled) Mask succeeded = dispatch_get_inverse_matrix(ctx->batched<__OSL_WIDTH>().renderer(), - bsg, wrm, USTR(to), bsg->varying.time); + bsg, wrm, to, bsg->varying.time); auto failedResults = wrm & succeeded.invert(); if (failedResults.mask().any_on()) { @@ -658,13 +660,13 @@ impl_get_varying_from_matrix_batched(BatchedShaderGlobals* bsg, for (int lane = 0; lane < __OSL_WIDTH; ++lane) { ustringhash from = wFrom[lane]; if (wMfrom.mask()[lane]) { - if (from == Strings::common || from == commonspace_synonym) { + if (from == Hashes::common || from == commonspace_synonym) { // inline of Mask::set_on(lane) common_space_bits |= 1 << lane; - } else if (from == Strings::shader) { + } else if (from == Hashes::shader) { // inline of Mask::set_on(lane) shader_space_bits |= 1 << lane; - } else if (from == Strings::object) { + } else if (from == Hashes::object) { // inline of Mask::set_on(lane) object_space_bits |= 1 << lane; } else { @@ -731,7 +733,7 @@ impl_get_varying_from_matrix_batched(BatchedShaderGlobals* bsg, OSL_BATCHOP void __OSL_MASKED_OP2(prepend_matrix_from, Wm, s)(void* bsg_, void* wr, - const char* from, + ustringhash_pod from_, unsigned int mask_value) { auto* bsg = reinterpret_cast(bsg_); @@ -739,7 +741,7 @@ __OSL_MASKED_OP2(prepend_matrix_from, Wm, s)(void* bsg_, void* wr, Block wMfrom; Masked from_matrix(wMfrom, Mask(mask_value)); /*Mask succeeded =*/ - impl_get_uniform_from_matrix_masked(bsg, from_matrix, from); + impl_get_uniform_from_matrix_masked(bsg, from_matrix, from_); Masked wrm(wr, Mask(mask_value)); @@ -796,13 +798,13 @@ impl_get_varying_to_matrix_masked(BatchedShaderGlobals* bsg, for (int lane = 0; lane < __OSL_WIDTH; ++lane) { ustringhash to = wTo[lane]; if (wMto.mask()[lane]) { - if (to == Strings::common || to == commonspace_synonym) { + if (to == Hashes::common || to == commonspace_synonym) { // inline of Mask::set_on(lane) common_space_bits |= 1 << lane; - } else if (to == Strings::shader) { + } else if (to == Hashes::shader) { // inline of Mask::set_on(lane) shader_space_bits |= 1 << lane; - } else if (to == Strings::object) { + } else if (to == Hashes::object) { // inline of Mask::set_on(lane) object_space_bits |= 1 << lane; } else { @@ -867,18 +869,19 @@ impl_get_varying_to_matrix_masked(BatchedShaderGlobals* bsg, OSL_FORCEINLINE Mask impl_get_uniform_from_to_matrix_masked(BatchedShaderGlobals* bsg, - Masked wrm, const char* from, - const char* to) + Masked wrm, + ustringhash_pod from_, + ustringhash_pod to_) { Block wMfrom, wMto; Masked from_matrix(wMfrom, wrm.mask()); Mask succeeded = impl_get_uniform_from_matrix_masked(bsg, from_matrix, - from); + from_); // NOTE: even if we failed to get a from matrix, it should have been set to // identity, so we still need to try to get the to matrix for the original mask Masked to_matrix(wMto, wrm.mask()); - succeeded &= impl_get_uniform_to_inverse_matrix_masked(bsg, to_matrix, to); + succeeded &= impl_get_uniform_to_inverse_matrix_masked(bsg, to_matrix, to_); impl_wide_mat_multiply(wrm, from_matrix, to_matrix); return succeeded; @@ -886,27 +889,27 @@ impl_get_uniform_from_to_matrix_masked(BatchedShaderGlobals* bsg, } // namespace OSL_BATCHOP int -__OSL_MASKED_OP3(get_from_to_matrix, Wm, s, s)(void* bsg_, void* wr, - const char* from, const char* to, - unsigned int mask_value) +__OSL_MASKED_OP3(get_from_to_matrix, Wm, s, + s)(void* bsg_, void* wr, ustringhash_pod from_, + ustringhash_pod to_, unsigned int mask_value) { auto* bsg = reinterpret_cast(bsg_); Masked wrm(wr, Mask(mask_value)); - return impl_get_uniform_from_to_matrix_masked(bsg, wrm, from, to).value(); + return impl_get_uniform_from_to_matrix_masked(bsg, wrm, from_, to_).value(); } OSL_BATCHOP int __OSL_MASKED_OP3(get_from_to_matrix, Wm, s, - Ws)(void* bsg_, void* wr, const char* from, void* w_to_ptr, - unsigned int mask_value) + Ws)(void* bsg_, void* wr, ustringhash_pod from_, + void* w_to_ptr, unsigned int mask_value) { auto* bsg = reinterpret_cast(bsg_); ShadingContext* ctx = bsg->uniform.context; Block wMfrom; Masked from_matrix(wMfrom, Mask(mask_value)); Mask succeeded = impl_get_uniform_from_matrix_masked(bsg, from_matrix, - from); + from_); Block bwToSpace; block_ustringhash_from_ptr(bwToSpace, w_to_ptr); @@ -927,7 +930,7 @@ __OSL_MASKED_OP3(get_from_to_matrix, Wm, s, OSL_BATCHOP int __OSL_MASKED_OP3(get_from_to_matrix, Wm, Ws, - s)(void* bsg_, void* wr, void* w_from_ptr, const char* to, + s)(void* bsg_, void* wr, void* w_from_ptr, ustringhash_pod to_, unsigned int mask_value) { auto* bsg = reinterpret_cast(bsg_); @@ -940,7 +943,7 @@ __OSL_MASKED_OP3(get_from_to_matrix, Wm, Ws, Block wMto; Masked to_matrix(wMto, Mask(mask_value)); Mask succeeded = impl_get_uniform_to_inverse_matrix_masked(bsg, to_matrix, - to); + to_); Block wMfrom; // NOTE: even if we failed to get a to matrix, it should have been set to @@ -1001,34 +1004,30 @@ __OSL_MASKED_OP3(get_from_to_matrix, Wm, Ws, OSL_BATCHOP int __OSL_MASKED_OP3(build_transform_matrix, Wm, s, - s)(void* bsg_, void* WM_, ustring_pod from_, ustring_pod to_, - unsigned int mask_value) + s)(void* bsg_, void* WM_, ustringhash_pod from_, + ustringhash_pod to_, unsigned int mask_value) { - auto* bsg = reinterpret_cast(bsg_); + ustringhash from = ustringhash_from(from_); + ustringhash to = ustringhash_from(to_); + auto* bsg = reinterpret_cast(bsg_); Mask mask(mask_value); Masked mm(WM_, mask); ShadingContext* ctx = bsg->uniform.context; - ustring from = USTR(from_); - ustring to = USTR(to_); - Mask succeeded; // Avoid matrix concatenation if possible by detecting when the // adjacent matrix would be identity // We don't expect both from and to == common, so we are not // optimizing for it - if (from == Strings::common + if (from == Hashes::common || from == ctx->shadingsys().commonspace_synonym()) { - succeeded = impl_get_uniform_to_inverse_matrix_masked(bsg, mm, - to.c_str()); - } else if (to == Strings::common + succeeded = impl_get_uniform_to_inverse_matrix_masked(bsg, mm, to_); + } else if (to == Hashes::common || to == ctx->shadingsys().commonspace_synonym()) { - succeeded = impl_get_uniform_from_matrix_masked(bsg, mm, from.c_str()); + succeeded = impl_get_uniform_from_matrix_masked(bsg, mm, from_); } else { - succeeded = impl_get_uniform_from_to_matrix_masked(bsg, mm, - from.c_str(), - to.c_str()); + succeeded = impl_get_uniform_from_to_matrix_masked(bsg, mm, from_, to_); } return succeeded.value(); } @@ -1037,7 +1036,7 @@ __OSL_MASKED_OP3(build_transform_matrix, Wm, s, OSL_BATCHOP int __OSL_MASKED_OP3(build_transform_matrix, Wm, Ws, - s)(void* bsg_, void* WM_, void* wfrom_, ustring_pod to_, + s)(void* bsg_, void* WM_, void* wfrom_, ustringhash_pod to_, unsigned int mask_value) { auto* bsg = reinterpret_cast(bsg_); @@ -1049,8 +1048,6 @@ __OSL_MASKED_OP3(build_transform_matrix, Wm, Ws, block_ustringhash_from_ptr(bwfrom_space, wfrom_); Wide wfrom_space(bwfrom_space); - ustring to_space = USTR(to_); - Block wMfrom, wMto; Masked from_matrix(wMfrom, wrm.mask()); ShadingContext* ctx = bsg->uniform.context; @@ -1058,8 +1055,7 @@ __OSL_MASKED_OP3(build_transform_matrix, Wm, Ws, Mask succeeded = impl_get_varying_from_matrix_batched(bsg, ctx, wfrom_space, from_matrix); Masked to_matrix(wMto, wrm.mask() & succeeded); - succeeded &= impl_get_uniform_to_inverse_matrix_masked(bsg, to_matrix, - to_space.c_str()); + succeeded &= impl_get_uniform_to_inverse_matrix_masked(bsg, to_matrix, to_); impl_wide_mat_multiply(wrm, from_matrix, to_matrix); return succeeded.value(); @@ -1069,7 +1065,7 @@ __OSL_MASKED_OP3(build_transform_matrix, Wm, Ws, OSL_BATCHOP int __OSL_MASKED_OP3(build_transform_matrix, Wm, s, - Ws)(void* bsg_, void* WM_, ustring_pod from_, void* wto_, + Ws)(void* bsg_, void* WM_, ustringhash_pod from_, void* wto_, unsigned int mask_value) { auto* bsg = reinterpret_cast(bsg_); @@ -1077,7 +1073,6 @@ __OSL_MASKED_OP3(build_transform_matrix, Wm, s, Mask mask(mask_value); Masked wrm(WM_, mask); - ustring from = USTR(from_); Block bwto_space; block_ustringhash_from_ptr(bwto_space, wto_); Wide wto_space(bwto_space); @@ -1087,7 +1082,7 @@ __OSL_MASKED_OP3(build_transform_matrix, Wm, s, ShadingContext* ctx = bsg->uniform.context; Mask succeeded = impl_get_uniform_from_matrix_masked(bsg, from_matrix, - from.c_str()); + from_); Masked to_matrix(wMto, wrm.mask() & succeeded); succeeded &= impl_get_varying_to_matrix_masked(bsg, ctx, wto_space, to_matrix); diff --git a/src/testminimal/CMakeLists.txt b/src/testminimal/CMakeLists.txt new file mode 100644 index 0000000000..42e6e9d115 --- /dev/null +++ b/src/testminimal/CMakeLists.txt @@ -0,0 +1,30 @@ +# Copyright Contributors to the Open Shading Language project. +# SPDX-License-Identifier: BSD-3-Clause +# https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + +# The 'testminimal' executable +set ( testminimal_srcs + testminimal.cpp + oslmaterial.cpp ) + +set(include_dirs ${CMAKE_CURRENT_SOURCE_DIR}) +list(APPEND include_dirs ${CMAKE_SOURCE_DIR}/src/include) +list(APPEND include_dirs ${CMAKE_BINARY_DIR}/include) +list(APPEND include_dirs ${IMATH_INCLUDES}) +list(APPEND include_dirs ${OPENEXR_INCLUDES}) +list(APPEND include_dirs ${OpenImageIO_INCLUDES}) + +set ( rs_srcs + oslmaterial.cpp ) + +EMBED_LLVM_BITCODE_IN_CPP ( "${rs_srcs}" "_host" "testminimal_llvm_compiled_rs" testminimal_srcs "-DOSL_HOST_RS_BITCODE=1" "${include_dirs}") + +add_executable ( testminimal ${testminimal_srcs} ) + +target_link_libraries (testminimal + PRIVATE + oslexec oslquery) + +install (TARGETS testminimal RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) + +osl_optix_target(testminimal) diff --git a/src/testminimal/oslmaterial.cpp b/src/testminimal/oslmaterial.cpp new file mode 100644 index 0000000000..9bc5470f73 --- /dev/null +++ b/src/testminimal/oslmaterial.cpp @@ -0,0 +1,199 @@ +// Copyright Contributors to the Open Shading Language project. +// SPDX-License-Identifier: BSD-3-Clause +// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + + +#include "oslmaterial.h" +#include + +using std::cout; +using std::endl; + +#if OSL_USE_BATCHED +template +CustomBatchedRendererServices::CustomBatchedRendererServices( + BatchedOSLMaterial& m) + : OSL::BatchedRendererServices(m.texturesys()), m_sr(m) +{ +} +#endif + +OSLMaterial::OSLMaterial() {} + +#if OSL_USE_BATCHED +template +BatchedOSLMaterial::BatchedOSLMaterial() : m_batch(*this) +{ +} + +template BatchedOSLMaterial<8>::BatchedOSLMaterial(); +template BatchedOSLMaterial<16>::BatchedOSLMaterial(); +#endif + +// Supported closures and parameters +struct EmptyParams {}; + +enum ClosureIDs { + EMISSION_ID, + BACKGROUND_ID, + MICROFACET_ID, +}; + +struct MicrofacetParams { + OSL::ustringhash dist; + OSL::Vec3 N, U; + float xalpha, yalpha, eta; + int refract; +}; + +void +register_closures(OSL::ShadingSystem* ss) +{ + // "Describe the memory layout of each closure type to the OSL runtime" + constexpr int MaxParams = 32; + struct BuiltinClosures { + const char* name; + int id; + OSL::ClosureParam params[MaxParams]; // "upper bound" + }; + + using namespace OSL; + + // Closures with support built into OSL, connected by the 1st string + BuiltinClosures supported[] = { + { "emission", EMISSION_ID, { CLOSURE_FINISH_PARAM(EmptyParams) } }, + { "background", BACKGROUND_ID, { CLOSURE_FINISH_PARAM(EmptyParams) } }, + { "microfacet", + MICROFACET_ID, + { CLOSURE_STRING_PARAM(MicrofacetParams, dist), + CLOSURE_VECTOR_PARAM(MicrofacetParams, N), + CLOSURE_VECTOR_PARAM(MicrofacetParams, U), + CLOSURE_FLOAT_PARAM(MicrofacetParams, xalpha), + CLOSURE_FLOAT_PARAM(MicrofacetParams, yalpha), + CLOSURE_FLOAT_PARAM(MicrofacetParams, eta), + CLOSURE_INT_PARAM(MicrofacetParams, refract), + CLOSURE_FINISH_PARAM(MicrofacetParams) } }, + }; + // Closure registration here enables that type of closure, when executing or compiling a shader + for (const BuiltinClosures& c : supported) + ss->register_closure(c.name, c.id, c.params, nullptr, nullptr); +} + +void +process_bsdf_closure(const OSL::ClosureColor* closure) +{ + static const ::OSL::ustringhash uh_ggx(OIIO::Strutil::strhash("ggx")); + //static const ::OSL::ustringhash uh_beckmann(OIIO::Strutil::strhash("beckmann")); + if (!closure) + return; + switch (closure->id) { + case OSL::ClosureColor::MUL: { + process_bsdf_closure(closure->as_mul()->closure); + break; + } + case OSL::ClosureColor::ADD: { + process_bsdf_closure(closure->as_add()->closureA); + process_bsdf_closure(closure->as_add()->closureB); + break; + } + default: { + const OSL::ClosureComponent* comp = closure->as_comp(); + switch (comp->id) { + case EMISSION_ID: cout << "parsing emission closure" << endl; break; + case MICROFACET_ID: { + cout << "parsing microfacet closure" << endl; + const MicrofacetParams* mp = comp->as(); + if (mp->dist.hash() == uh_ggx.hash()) { + cout << "uh_ggx" << endl; + } else { + cout << "uh_beckmann or default" << endl; + } + } break; + default: + OSL_ASSERT(false && "Invalid closure invoked in surface shader"); + break; + } + } break; + } +} + +void +OSLMaterial::run_test(OSL::ShadingSystem* ss, OSL::PerThreadInfo* thread_info, + OSL::ShadingContext* context, char* shader_name) +{ + register_closures(ss); + OSL::ShaderGlobals globals; + globals_from_hit(globals); + + std::vector options; + + // Create a new shader group + m_shaders.emplace_back(); + m_shaders[0] = ss->ShaderGroupBegin(std::to_string(0)); + OSL::ShaderGroupRef group = m_shaders[0]; + + //{ + // OSL::OSLCompiler compiler; + // std::string name = std::string(shader_name) + ".osl"; + // compiler.compile(name.c_str(), options); + //} + + ss->Shader(*group, "surface", shader_name, "Test"); + ss->ShaderGroupEnd(*group); + + ss->execute(context, *group, globals); + const OSL::ClosureColor* closure = globals.Ci; + process_bsdf_closure(closure); +} + +#if OSL_USE_BATCHED +template +void +BatchedOSLMaterial::run_test(OSL::ShadingSystem* ss, + OSL::PerThreadInfo* thread_info, + OSL::ShadingContext* context, + char* shader_name) +{ + register_closures(ss); + OSL::BatchedShaderGlobals batched_globals; + + m_batch.globals_from_hit(batched_globals); + + std::vector options; + + // Create a new shader group + m_shaders.emplace_back(); + m_shaders[0] = ss->ShaderGroupBegin(std::to_string(0)); + OSL::ShaderGroupRef group = m_shaders[0]; + + //{ + // OSL::OSLCompiler compiler; + // std::string name = std::string(shader_name) + ".osl"; + // compiler.compile(name.c_str(), options); + //} + + ss->Shader(*group, "surface", shader_name, "Test"); + ss->ShaderGroupEnd(*group); + + // Run the shader that was just created + OSL::Block wide_shadeindex_block; + char* userdata_base_ptr = NULL; + char* output_base_ptr = NULL; + ss->batched().execute(*context, *group, batch_width, + wide_shadeindex_block, batched_globals, + userdata_base_ptr, output_base_ptr); + const OSL::ClosureColor* closure = batched_globals.varying.Ci[0]; + process_bsdf_closure(closure); +} + +template void +BatchedOSLMaterial<8>::run_test(OSL::ShadingSystem* ss, + OSL::PerThreadInfo* thread_info, + OSL::ShadingContext* context, + char* shader_name); +template void +BatchedOSLMaterial<16>::run_test(OSL::ShadingSystem* ss, + OSL::PerThreadInfo* thread_info, + OSL::ShadingContext* context, + char* shader_name); +#endif diff --git a/src/testminimal/oslmaterial.h b/src/testminimal/oslmaterial.h new file mode 100644 index 0000000000..12e49fca33 --- /dev/null +++ b/src/testminimal/oslmaterial.h @@ -0,0 +1,235 @@ +// Copyright Contributors to the Open Shading Language project. +// SPDX-License-Identifier: BSD-3-Clause +// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#if OSL_USE_BATCHED +# include +# include +#endif + +class OSLMaterial; + +#if OSL_USE_BATCHED +template class BatchedOSLMaterial; + +using OSL::Vec3; + +/// Custom BatchedRendererServices +template +class CustomBatchedRendererServices + : public OSL::BatchedRendererServices { +public: + explicit CustomBatchedRendererServices(BatchedOSLMaterial& m); + + //OIIO::ErrorHandler& errhandler() const { return *m_errhandler; } + /// Turn information at hitpoint into ShaderGlobals for OSL + void globals_from_hit(OSL::BatchedShaderGlobals& bsg) + { + // Uniform + auto& usg = bsg.uniform; + // Zero it all + std::memset(&usg, 0, sizeof(OSL::UniformShaderGlobals)); + usg.raytype = 1; // 1 stands for camera ray? + // Varying + auto& vsg = bsg.varying; + + //assign_all(vsg.shader2common, TransformationPtr(&Mshad)); + //assign_all(vsg.object2common, TransformationPtr(&Mobj)); + + for (int i = 0; i < batch_width; i++) + vsg.P[i] = { 0.0f, 0.0f, 0.0f }; + + for (int i = 0; i < batch_width; i++) + vsg.I[i] = { 0.0f, 0.0f, -1.0f }; // incident ray + for (int i = 0; i < batch_width; i++) + vsg.N[i] = { 0.0f, 0.0f, 1.0f }; // shading normal + for (int i = 0; i < batch_width; i++) + vsg.Ng[i] = { 0.0f, 0.0f, 1.0f }; // true geometric normal + + assign_all(vsg.u, + 0.5f); // 2D surface parameter u, and its differentials. + assign_all(vsg.v, + 0.5f); // 2D surface parameter u, and its differentials. + + + //if (false == vary_udxdy) { + assign_all(vsg.dudx, 0.0f); //uscale / xres); + assign_all(vsg.dudy, 0.0f); + //} + //if (false == vary_vdxdy) { + assign_all(vsg.dvdx, 0.0f); + assign_all(vsg.dvdy, 0.0f); //vscale / yres); + //} + + + //if (false == vary_Pdxdy) { + // assign_all(vsg.dPdx, Vec3(vsg.dudx[0], vsg.dudy[0], 0.0f)); + // assign_all(vsg.dPdy, Vec3(vsg.dvdx[0], vsg.dvdy[0], 0.0f)); + //} + + assign_all(vsg.dPdz, + Vec3(0.0f, 0.0f, 0.0f)); // just use 0 for volume tangent + + // Tangents of P with respect to surface u,v + assign_all(vsg.dPdu, Vec3(1.0f, 0.0f, 0.0f)); + assign_all(vsg.dPdv, Vec3(0.0f, 1.0f, 0.0f)); + + assign_all(vsg.I, Vec3(0, 0, 0)); + assign_all(vsg.dIdx, Vec3(0, 0, 0)); + assign_all(vsg.dIdy, Vec3(0, 0, 0)); + + // That also implies that our normal points to (0,0,1) + assign_all(vsg.N, Vec3(0, 0, 1)); + assign_all(vsg.Ng, Vec3(0, 0, 1)); + + assign_all(vsg.time, 0.0f); + assign_all(vsg.dtime, 0.0f); + assign_all(vsg.dPdtime, Vec3(0, 0, 0)); + + assign_all(vsg.Ps, Vec3(0, 0, 0)); + assign_all(vsg.dPsdx, Vec3(0, 0, 0)); + assign_all(vsg.dPsdy, Vec3(0, 0, 0)); + + assign_all(vsg.surfacearea, 1.0f); + assign_all(vsg.flipHandedness, 0); + assign_all(vsg.backfacing, 0); + + assign_all(vsg.Ci, (::OSL::ClosureColor*)NULL); + } + + bool is_overridden_get_inverse_matrix_WmWxWf() const override + { + return false; + }; + bool is_overridden_get_matrix_WmWsWf() const override { return false; }; + bool is_overridden_get_inverse_matrix_WmsWf() const override + { + return false; + }; + bool is_overridden_get_inverse_matrix_WmWsWf() const override + { + return false; + }; + bool is_overridden_texture() const override { return false; }; + bool is_overridden_texture3d() const override { return false; }; + bool is_overridden_environment() const override { return false; }; + bool is_overridden_pointcloud_search() const override { return false; }; + bool is_overridden_pointcloud_get() const override { return false; }; + bool is_overridden_pointcloud_write() const override { return false; }; + + BatchedOSLMaterial& m_sr; + +private: +}; +#endif + +/// Custom RendererServices for non-batched case +class OSLMaterial : public OSL::RendererServices { +public: + OSLMaterial(); + + void run_test(OSL::ShadingSystem* ss, OSL::PerThreadInfo* thread_info, + OSL::ShadingContext* context, char* shader_name); + + OIIO::ErrorHandler& errhandler() const { return *m_errhandler; } + + /// Turn information at hitpoint into ShaderGlobals for OSL + void globals_from_hit(OSL::ShaderGlobals& sg) + { + sg.P = { 0.0f, 0.0f, 0.0f }; // surface pos + sg.dPdx = { 0.0f, 0.0f, 0.0f }; + sg.dPdy = { 0.0f, 0.0f, 0.0f }; + sg.dPdz = { 0.0f, 0.0f, 0.0f }; // for volume shading only + + sg.I = { 0.0f, 0.0f, -1.0f }; // incident ray + sg.dIdx = { 0.0f, 0.0f, 0.0f }; + sg.dIdy = { 0.0f, 0.0f, 0.0f }; + + sg.N = { 0.0f, 0.0f, 1.0f }; // shading normal + sg.Ng = { 0.0f, 0.0f, 1.0f }; // true geometric normal + + sg.u = 0.5f; // 2D surface parameter u, and its differentials. + sg.dudx = 0.0f; + sg.dudy = 0.0f; + sg.v = 0.5f; // 2D surface parameter v, and its differentials. + sg.dvdx = 0.0f; + sg.dvdy = 0.0f; + + // Surface tangents: derivative of P with respect to surface u and v. + sg.dPdu = { 1.0f, 0.0f, 0.0f }; + sg.dPdv = { 0.0f, 1.0f, 0.0f }; + + sg.time = 0.0f; + sg.dtime = 0.001f; + + // Velocity vector: derivative of position P with respect to time. + sg.dPdtime = { 0.0f, 0.0f, 0.0f }; + + // For lights or light attenuation shaders: the point being illuminated (???) + sg.Ps = { 0.0f, 0.0f, 0.0f }; + sg.dPsdx = { 0.0f, 0.0f, 0.0f }; + sg.dPsdy = { 0.0f, 0.0f, 0.0f }; + + // Renderer user pointers + sg.renderstate = NULL; + sg.tracedata = NULL; + sg.objdata = NULL; + + sg.renderer = this; + + sg.raytype = 1; // 1 stands for camera ray? + sg.flipHandedness = 0; + sg.backfacing = 0; + + // output closure, needs to be null initialized + sg.Ci = NULL; + } + + // ShaderGroupRef storage + std::vector& shaders() { return m_shaders; } + std::vector m_shaders; + +private: + std::unique_ptr m_errhandler; +}; + +#if OSL_USE_BATCHED + +/// Custom RendererServices for batched case +template +class BatchedOSLMaterial : public OSL::RendererServices { +public: + BatchedOSLMaterial(); + + void run_test(OSL::ShadingSystem* ss, OSL::PerThreadInfo* thread_info, + OSL::ShadingContext* context, char* shader_name); + + OIIO::ErrorHandler& errhandler() const { return *m_errhandler; } + + // ShaderGroupRef storage + std::vector& shaders() { return m_shaders; } + std::vector m_shaders; + + OSL::BatchedRendererServices* + batched(OSL::WidthOf) override + { + return &m_batch; + } + + CustomBatchedRendererServices m_batch; + +private: + std::unique_ptr m_errhandler; +}; + +#endif diff --git a/src/testminimal/testminimal.cpp b/src/testminimal/testminimal.cpp new file mode 100644 index 0000000000..94c9fcc378 --- /dev/null +++ b/src/testminimal/testminimal.cpp @@ -0,0 +1,143 @@ +// Copyright Contributors to the Open Shading Language project. +// SPDX-License-Identifier: BSD-3-Clause +// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + + +#include +#include +#include +#include "oslmaterial.h" + +using namespace OSL; + +int +main(int argc, char** argv) +{ + int batch_width; + char* shader_name; + bool use_fma = false; + if (argc < 2) { + std::cout + << "usage: shader_name(without .oso) [+optional] batch_width (0/8/16) [+optional] use_fma 0/1" + << std::endl; + return 0; + } else if (argc >= 3) { + shader_name = argv[1]; + batch_width = atoi(argv[2]); +#if OSL_USE_BATCHED + if (batch_width != -1 && batch_width != 1 && batch_width != 8 + && batch_width != 16) + batch_width = 1; +#else + if (batch_width != 1) + batch_width = 1; +#endif + if (argc >= 4) { + use_fma = atoi(argv[3]); + } + } else { + shader_name = argv[1]; + batch_width = -1; + } + + OSLMaterial* oslmat = NULL; +#if OSL_USE_BATCHED + BatchedOSLMaterial<8>* boslmat8 = NULL; + BatchedOSLMaterial<16>* boslmat16 = NULL; +#endif + + TextureSystem* texturesys = TextureSystem::create(); + ShadingSystem* ss = NULL; + + if (batch_width == -1) { +#if OSL_USE_BATCHED + oslmat = new OSLMaterial(); + ss = new ShadingSystem(oslmat, NULL, &oslmat->errhandler()); + if (use_fma) + ss->attribute("llvm_jit_fma", true); + if (ss->configure_batch_execution_at(16)) + batch_width = 16; + else if (ss->configure_batch_execution_at(8)) + batch_width = 8; + else + batch_width = 1; + delete oslmat; + oslmat = NULL; + delete ss; + ss = NULL; +#else + if (use_fma) + ss->attribute("llvm_jit_fma", true); + batch_width = 1; +#endif + } + + switch (batch_width) { + case 1: + oslmat = new OSLMaterial(); + ss = new ShadingSystem(oslmat, texturesys, &oslmat->errhandler()); + break; +#if OSL_USE_BATCHED + case 8: + boslmat8 = new BatchedOSLMaterial<8>(); + ss = new ShadingSystem(boslmat8, texturesys, &boslmat8->errhandler()); + break; + case 16: + boslmat16 = new BatchedOSLMaterial<16>(); + ss = new ShadingSystem(boslmat16, texturesys, &boslmat16->errhandler()); + break; +#endif + } + +#if OSL_USE_BATCHED + if (batch_width > 1) { + if (use_fma) + ss->attribute("llvm_jit_fma", true); + ss->configure_batch_execution_at(batch_width); + + // build searchpath for ISA specific OSL shared libraries based on expected + // location of library directories relative to the executables path. + static const char* relative_lib_dirs[] = +# if (defined(_WIN32) || defined(_WIN64)) + { "\\..\\lib64", "\\..\\lib" }; +# else + { "/../lib64", "/../lib" }; +# endif + auto executable_directory = OIIO::Filesystem::parent_path( + OIIO::Sysutil::this_program_path()); + int dirNum = 0; + std::string librarypath; + for (const char* relative_lib_dir : relative_lib_dirs) { + if (dirNum++ > 0) + librarypath += ":"; + librarypath += executable_directory + relative_lib_dir; + } + ss->attribute("searchpath:library", librarypath); + } +#endif + + PerThreadInfo* thread_info; + ShadingContext* context; + thread_info = ss->create_thread_info(); + context = ss->get_context(thread_info); + + switch (batch_width) { + case 1: oslmat->run_test(ss, thread_info, context, shader_name); break; +#if OSL_USE_BATCHED + case 8: boslmat8->run_test(ss, thread_info, context, shader_name); break; + case 16: boslmat16->run_test(ss, thread_info, context, shader_name); break; +#endif + } + + ss->release_context(context); + ss->destroy_thread_info(thread_info); + + delete oslmat; +#if OSL_USE_BATCHED + delete boslmat8; + delete boslmat16; +#endif + delete ss; + + return 0; +} diff --git a/testsuite/closure-string/BATCHED b/testsuite/closure-string/BATCHED new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testsuite/closure-string/ref/out.txt b/testsuite/closure-string/ref/out.txt new file mode 100644 index 0000000000..d497c44d99 --- /dev/null +++ b/testsuite/closure-string/ref/out.txt @@ -0,0 +1,3 @@ +Compiled test.osl -> test.oso +parsing microfacet closure +uh_ggx diff --git a/testsuite/closure-string/run.py b/testsuite/closure-string/run.py new file mode 100755 index 0000000000..5e688f5cb9 --- /dev/null +++ b/testsuite/closure-string/run.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +# Copyright Contributors to the Open Shading Language project. +# SPDX-License-Identifier: BSD-3-Clause +# https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + +command = testminimal("test") diff --git a/testsuite/closure-string/test.osl b/testsuite/closure-string/test.osl new file mode 100644 index 0000000000..4071f5d9e4 --- /dev/null +++ b/testsuite/closure-string/test.osl @@ -0,0 +1,4 @@ +shader test(string distribution = "ggx") +{ + Ci = microfacet(distribution, N, N, 0.1, 0.1, 0.0, 0); +} diff --git a/testsuite/runtest.py b/testsuite/runtest.py index befe278a46..eceec81be2 100755 --- a/testsuite/runtest.py +++ b/testsuite/runtest.py @@ -249,6 +249,14 @@ def oiiodiff (fileA, fileB, extraargs="", silent=True, concat=True) : command += " ;\n" return command +# Construct a command that run testminimal with the specified arguments, +# appending output to the file "out.txt". +def testminimal (args) : + if os.environ.__contains__('OSL_TESTMINIMAL_NAME') : + testminimalname = os.environ['OSL_TESTMINIMAL_NAME'] + " " + else : + testminimalname = osl_app("testminimal") + return (testminimalname + args + redirect + " ;\n") # Construct a command that run testshade with the specified arguments, # appending output to the file "out.txt".