Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rs fmt specification #1702

Merged
merged 9 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/cmake/testing.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,17 @@ macro ( TESTSUITE )
add_one_testsuite ("${_testname}.opt" "${_testsrcdir}"
ENV TESTSHADE_OPT=2 )
endif ()
# Run the same test again with aggressive -O2 runtime
# optimization, triggered by setting TESTSHADE_OPT env variable.
# Skip OptiX-only tests and those with a NOOPTIMIZE marker file.
if (NOT _testname MATCHES "optix"
AND NOT EXISTS "${_testsrcdir}/NOSCALAR"
AND NOT EXISTS "${_testsrcdir}/BATCHED_REGRESSION"
AND NOT EXISTS "${_testsrcdir}/NOOPTIMIZE"
AND NOT EXISTS "${_testsrcdir}/NORSBITCODE")
add_one_testsuite ("${_testname}.opt.rs_bitcode" "${_testsrcdir}"
ENV TESTSHADE_OPT=2 TESTSHADE_RS_BITCODE=1)
endif ()
# When building for OptiX support, also run it in OptiX mode
# if there is an OPTIX marker file in the directory.
# If an environment variable $TESTSUITE_OPTIX is nonzero, then
Expand Down Expand Up @@ -346,6 +357,9 @@ macro (osl_add_all_tests)
struct-nested struct-nested-assign struct-nested-deep
ternary
testshade-expr
test-fmt-arrays test-fmt-fileprint
test-fmt-cxpf test-fmt-noise test-fmt-matrixcolor
test-fmt-stpf test-fmt-errorwarning test-fmt-errorwarning-repeats
texture-alpha texture-alpha-derivs
texture-blur texture-connected-options
texture-derivs texture-environment texture-errormsg
Expand Down
217 changes: 217 additions & 0 deletions src/include/OSL/encodedtypes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
// Copyright Contributors to the Open Shading Language project.
// SPDX-License-Identifier: BSD-3-Clause
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage

#pragma once

#include <OSL/oslconfig.h>

OSL_NAMESPACE_ENTER


// Substitute for variable argument list, which is traditionally used with a printf, with arrays
// of EncodedTypes to identify types contained in a blind payload of values.
enum class EncodedType : uint8_t {
// OSL Shaders could encode these types
kUstringHash = 0,
kInt32,
kFloat,

// OSL library functions or renderer services encode additional native types
kInt64,
kDouble,
kUInt32,
kUInt64,
kPointer,
kTypeDesc,
kCount
};

// Decode will use each EncodedType in the array to interpret the contents of the arg_values
// parameter alongwith a fmtlib specifier identified by the ustring that the format_hash represents.
// Contents of decoded_str are written over (not appended).
// Returns # of bytes read from arg_values
int
decode_message(uint64_t format_hash, int32_t arg_count,
const EncodedType* arg_types, const uint8_t* arg_values,
std::string& decoded_str);

namespace pvt {

constexpr inline uint32_t
size_of_encoded_type(EncodedType et)
{
constexpr uint32_t SizeByEncodedType[] = {
sizeof(OSL::ustringhash), sizeof(int32_t), sizeof(float),
sizeof(int64_t), sizeof(double), sizeof(uint32_t),
sizeof(uint64_t), sizeof(void*), sizeof(OSL::TypeDesc),
};
static_assert(sizeof(SizeByEncodedType) / sizeof(SizeByEncodedType[0])
== size_t(EncodedType::kCount),
"Keep array contents lined up with enum");
return SizeByEncodedType[static_cast<int>(et)];
}

template<typename T>
struct TypeEncoder; //Undefined on purpose; all types must have a specialization

template<> struct TypeEncoder<OSL::ustringhash> {
using DataType = ustringhash_pod;
static constexpr EncodedType value = EncodedType::kUstringHash;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const OSL::ustringhash& val) { return val.hash(); }
};

template<> struct TypeEncoder<OSL::ustring> {
using DataType = ustringhash_pod;
static constexpr EncodedType value = EncodedType::kUstringHash;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const OSL::ustring& val) { return val.hash(); }
};

template<> struct TypeEncoder<const char*> {
using DataType = ustringhash_pod;
static constexpr EncodedType value = EncodedType::kUstringHash;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const char* val) { return ustring(val).hash(); }
};

template<> struct TypeEncoder<std::string> {
using DataType = ustringhash_pod;
static constexpr EncodedType value = EncodedType::kUstringHash;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const std::string& val)
{
return ustring(val).hash();
}
};

template<> struct TypeEncoder<int32_t> {
using DataType = int32_t;
static constexpr EncodedType value = EncodedType::kInt32;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const int32_t val) { return val; }
};

template<> struct TypeEncoder<float> {
using DataType = float;
static constexpr EncodedType value = EncodedType::kFloat;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const float val) { return val; }
};


template<> struct TypeEncoder<int64_t> {
using DataType = int64_t;
static constexpr EncodedType value = EncodedType::kInt64;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const int64_t val) { return val; }
};

// On macOS 10.+ ptrdiff_t is a long, but on linux this
// specialization would conflict with int64_t
#if defined(__APPLE__) && defined(__MACH__)
template<> struct TypeEncoder<ptrdiff_t> {
using DataType = int64_t;
static constexpr EncodedType value = EncodedType::kInt64;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const ptrdiff_t val) { return val; }
};
#endif

template<> struct TypeEncoder<double> {
using DataType = double;
static constexpr EncodedType value = EncodedType::kDouble;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const double val) { return val; }
};

template<> struct TypeEncoder<uint64_t> {
using DataType = uint64_t;
static constexpr EncodedType value = EncodedType::kUInt64;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const uint64_t val) { return val; }
};

template<> struct TypeEncoder<uint32_t> {
using DataType = uint32_t;
static constexpr EncodedType value = EncodedType::kUInt32;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const uint32_t val) { return val; }
};

template<typename T> struct TypeEncoder<T*> {
// fmtlib only supports const void *, all other pointers must be
// converted with a cast or helper like fmt::ptr(p)
static_assert(
(std::is_same<void*, T*>::value || std::is_same<const void*, T*>::value),
"formatting of non-void pointers is disallowed, wrap with fmt::ptr");

using DataType = const T*;
static constexpr EncodedType value = EncodedType::kPointer;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const T* val) { return val; }
};

template<> struct TypeEncoder<OSL::TypeDesc> {
// To avoid warnings about non-pod types when we PackArgs
// we will encode into builtin type
using DataType = uint64_t;
static_assert(sizeof(OSL::TypeDesc) == sizeof(DataType), "unexpected");
static constexpr EncodedType value = EncodedType::kTypeDesc;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const OSL::TypeDesc& val)
{
return OSL::bitcast<DataType>(val);
}
};

// Promote thinner types
template<> struct TypeEncoder<int16_t> {
using DataType = int32_t;
static constexpr EncodedType value = EncodedType::kInt32;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const int16_t val) { return DataType(val); }
};

template<> struct TypeEncoder<int8_t> {
using DataType = int32_t;
static constexpr EncodedType value = EncodedType::kInt32;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const int8_t val) { return DataType(val); }
};

template<> struct TypeEncoder<uint16_t> {
using DataType = uint32_t;
static constexpr EncodedType value = EncodedType::kUInt32;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const uint16_t val) { return DataType(val); }
};

template<> struct TypeEncoder<uint8_t> {
using DataType = uint32_t;
static constexpr EncodedType value = EncodedType::kUInt32;
static_assert(size_of_encoded_type(value) == sizeof(DataType),
"unexpected");
static DataType Encode(const uint8_t val) { return DataType(val); }
};
} // namespace pvt


OSL_NAMESPACE_EXIT
134 changes: 134 additions & 0 deletions src/include/OSL/fmt_util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright Contributors to the Open Shading Language project.
// SPDX-License-Identifier: BSD-3-Clause
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage

#pragma once

#include <OSL/rs_free_function.h>

// OSL library functions as well as Renderer Service free functions
// can utilize OSL::filefmt, OSL::printfmt, OSL::errorfmt, and
// OSL::warningfmt wrappers to accept variable arguments to be
// encoded into a EncodeType array and PackedArgument buffer
// that underlying render service free functions require.
// NOTE: All strings arguments to the underlying render service free
// functions must be ustringhash. To make this easier, these wrappers
// automatically convert ustring and const char * to ustringhash.

OSL_NAMESPACE_ENTER

namespace pvt {
// PackedArgs is similar to tuple but packs its data back to back
// in memory layout, which is what we need to build up payload
// to the fmt reporting system
template<int IndexT, typename TypeT> struct PackedArg {
explicit PackedArg(const TypeT& a_value) : m_value(a_value) {}
TypeT m_value;
} __attribute__((packed, aligned(1)));

template<typename IntSequenceT, typename... TypeListT> struct PackedArgsBase;
// Specialize to extract a parameter pack of the IntegerSquence
// so it can be expanded alongside the TypeListT parameter pack
template<int... IntegerListT, typename... TypeListT>
struct PackedArgsBase<std::integer_sequence<int, IntegerListT...>, TypeListT...>
: public PackedArg<IntegerListT, TypeListT>... {
explicit PackedArgsBase(const TypeListT&... a_values)
// multiple inheritance of individual components
// uniquely identified by the <Integer,Type> combo
: PackedArg<IntegerListT, TypeListT>(a_values)...
{
}
} __attribute__((packed, aligned(1)));

template<typename... TypeListT> struct PackedArgs {
typedef std::make_integer_sequence<int, sizeof...(TypeListT)>
IndexSequenceType;
PackedArgsBase<IndexSequenceType, TypeListT...> m_components;

explicit PackedArgs(const TypeListT&... a_values)
: m_components(a_values...)
{
}
};
} // namespace pvt



template<typename FilenameT, typename SpecifierT, typename... ArgListT>
void
filefmt(OpaqueExecContextPtr oec, const FilenameT& filename_hash,
const SpecifierT& fmt_specification, ArgListT... args)
{
constexpr int32_t count = sizeof...(args);
constexpr OSL::EncodedType argTypes[]
= { pvt::TypeEncoder<ArgListT>::value... };
pvt::PackedArgs<typename pvt::TypeEncoder<ArgListT>::DataType...> argValues {
pvt::TypeEncoder<ArgListT>::Encode(args)...
};

rs_filefmt(oec, OSL::ustringhash { filename_hash },
OSL::ustringhash { fmt_specification }, count,
(count == 0) ? nullptr : argTypes,
static_cast<uint32_t>((count == 0) ? 0 : sizeof(argValues)),
(count == 0) ? nullptr : reinterpret_cast<uint8_t*>(&argValues));
}

template<typename SpecifierT, typename... ArgListT>
void
printfmt(OpaqueExecContextPtr oec, const SpecifierT& fmt_specification,
ArgListT... args)
{
constexpr int32_t count = sizeof...(args);
constexpr OSL::EncodedType argTypes[]
= { pvt::TypeEncoder<ArgListT>::value... };
pvt::PackedArgs<typename pvt::TypeEncoder<ArgListT>::DataType...> argValues {
pvt::TypeEncoder<ArgListT>::Encode(args)...
};

rs_printfmt(oec, OSL::ustringhash { fmt_specification }, count,
(count == 0) ? nullptr : argTypes,
static_cast<uint32_t>((count == 0) ? 0 : sizeof(argValues)),
(count == 0) ? nullptr
: reinterpret_cast<uint8_t*>(&argValues));
}

template<typename SpecifierT, typename... ArgListT>
void
errorfmt(OpaqueExecContextPtr oec, const SpecifierT& fmt_specification,
ArgListT... args)
{
constexpr int32_t count = sizeof...(args);
constexpr OSL::EncodedType argTypes[]
= { pvt::TypeEncoder<ArgListT>::value... };
pvt::PackedArgs<typename pvt::TypeEncoder<ArgListT>::DataType...> argValues {
pvt::TypeEncoder<ArgListT>::Encode(args)...
};

rs_errorfmt(oec, OSL::ustringhash { fmt_specification }, count,
(count == 0) ? nullptr : argTypes,
static_cast<uint32_t>((count == 0) ? 0 : sizeof(argValues)),
(count == 0) ? nullptr
: reinterpret_cast<uint8_t*>(&argValues));
}

template<typename SpecifierT, typename... ArgListT>
void
warningfmt(OpaqueExecContextPtr oec, const SpecifierT& fmt_specification,
ArgListT... args)
{
constexpr int32_t count = sizeof...(args);
constexpr OSL::EncodedType argTypes[]
= { pvt::TypeEncoder<ArgListT>::value... };
pvt::PackedArgs<typename pvt::TypeEncoder<ArgListT>::DataType...> argValues {
pvt::TypeEncoder<ArgListT>::Encode(args)...
};

rs_warningfmt(oec, OSL::ustringhash { fmt_specification }, count,
(count == 0) ? nullptr : argTypes,
static_cast<uint32_t>((count == 0) ? 0 : sizeof(argValues)),
(count == 0) ? nullptr
: reinterpret_cast<uint8_t*>(&argValues));
}


OSL_NAMESPACE_EXIT
Loading
Loading