1 // Copyright Contributors to the Open Shading Language project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4 
5 #include "optix_stringtable.h"
6 
7 using OIIO::ustring;
8 
9 OSL_NAMESPACE_ENTER
10 
11 
OptiXStringTable(optix::Context ctx)12 OptiXStringTable::OptiXStringTable(optix::Context ctx)
13     : m_ptr (nullptr),
14       m_size (1 << 16),
15       m_offset (0),
16       m_optix_ctx (ctx)
17 {
18 }
19 
20 
21 
~OptiXStringTable()22 OptiXStringTable::~OptiXStringTable()
23 {
24     freetable();
25 }
26 
27 
28 
29 void
freetable()30 OptiXStringTable::freetable()
31 {
32     if (m_ptr)
33         OSL::cudaFree (m_ptr);
34     m_ptr = nullptr;
35 }
36 
37 
38 
init(optix::Context ctx OSL_MAYBE_UNUSED)39 void OptiXStringTable::init (optix::Context ctx OSL_MAYBE_UNUSED)
40 {
41 #ifdef OSL_USE_OPTIX
42     OSL_ASSERT (! m_ptr && "StringTable should only be initialized once");
43     m_optix_ctx = ctx;
44 
45 #if (OPTIX_VERSION < 70000)
46     OSL_ASSERT ((m_optix_ctx->getEnabledDeviceCount() == 1) &&
47             "Only one CUDA device is currently supported");
48 #endif
49 
50     OSL::cudaMalloc (reinterpret_cast<void**>(&m_ptr), (m_size));
51 
52     // Add the statically-declared strings to the table, and create OptiX
53     // variables for them in the OSL::DeviceStrings namespace.
54     //
55     // The names of the variables created here must match the extern variables
56     // declared in OSL/device_string.h for OptiX's variable scoping mechanisms
57     // to work.
58 
59 #define STRDECL(str,var_name)                                           \
60     addString (ustring(str), ustring(OSL_NAMESPACE_STRING "::DeviceStrings::" #var_name));
61 #include <OSL/strdecls.h>
62 #undef STRDECL
63 #endif
64 }
65 
66 
addString(ustring str OSL_MAYBE_UNUSED,ustring var_name OSL_MAYBE_UNUSED)67 uint64_t OptiXStringTable::addString (ustring str OSL_MAYBE_UNUSED,
68                                       ustring var_name OSL_MAYBE_UNUSED)
69 {
70 #ifdef OSL_USE_OPTIX
71     OSL_ASSERT (m_ptr && "StringTable has not been initialized");
72 
73     // The strings are laid out in the table as a struct:
74     //
75     //   struct TableRep {
76     //       size_t len;
77     //       size_t hash;
78     //       char   str[len+1];
79     //   };
80 
81     // Compute the size of the entry before adding it to the table
82     size_t size = sizeof(size_t) + sizeof(size_t) + str.size() + 1;
83     if (((m_offset + size) >= m_size)) {
84         reallocTable();
85     }
86 
87     // It should be hard to trigger this assert, unless the table size is
88     // very small and the string is very large.
89     OSL_ASSERT (m_offset + size <= m_size && "String table allocation error");
90 
91     int offset = getOffset(str.string());
92     if (offset < 0) {
93         // Place the hash and length of the string before the characters
94         size_t hash = str.hash();
95         cudaMemcpy (m_ptr + m_offset, (void*)&hash, sizeof(size_t), cudaMemcpyHostToDevice);
96         m_offset += sizeof(size_t);
97 
98         size_t len = str.length();
99         cudaMemcpy (m_ptr + m_offset, (void*)&len, sizeof(size_t), cudaMemcpyHostToDevice);
100         m_offset += sizeof(size_t);
101 
102         offset = m_offset;
103         m_offset_map [str] = offset;
104         m_name_map   [str] = var_name;
105 
106         // Copy the raw characters to the table
107         cudaMemcpy (m_ptr + m_offset, str.c_str(), str.size() + 1, cudaMemcpyHostToDevice);
108         m_offset += str.size() + 1;
109 
110         // Align the offset for the next entry to 8-byte boundaries
111         m_offset = (m_offset + 0x7u) & ~0x7u;
112     }
113     else if (!var_name.empty()) {
114         // update what str points to
115         m_name_map[str] = var_name;
116     }
117 
118     uint64_t addr = reinterpret_cast<uint64_t>(m_ptr + offset);
119 
120     // Optionally create an OptiX variable for the string. It's not necessary to
121     // create a variable for strings that do not appear by name in compiled code
122     // (in either the OSL library functions or in the renderer).
123 #if (OPTIX_VERSION < 70000)
124     if (! var_name.empty())
125         m_optix_ctx [var_name.string()]->setUserData (8, &addr);
126 #else
127     if (! var_name.empty())
128         m_addr_table [var_name] = addr;
129 #endif
130     return addr;
131 #else
132     return 0;
133 #endif
134 }
135 
136 
getOffset(const std::string & str) const137 int OptiXStringTable::getOffset (const std::string& str) const
138 {
139     auto it = m_offset_map.find (ustring(str));
140     return (it != m_offset_map.end()) ? it->second : -1;
141 }
142 
143 
reallocTable()144 void OptiXStringTable::reallocTable()
145 {
146 #ifdef OSL_USE_OPTIX
147 
148 #if (OPTIX_VERSION < 70000)
149     OSL_ASSERT ((m_optix_ctx->getEnabledDeviceCount() == 1) &&
150             "Only one CUDA device is currently supported");
151 #endif
152 
153     m_size *= 2;
154     OSL::cudaFree (m_ptr);
155     OSL::cudaMalloc (reinterpret_cast<void**>(&m_ptr), (m_size));
156 
157     // The offsets need to be recomputed
158     m_offset = 0;
159     m_offset_map.clear();
160 
161     // Add the string collection to the newly-allocated memory
162     for (auto& entry : m_name_map) {
163         addString (entry.first, entry.second);
164     }
165 #endif
166 }
167 
168 OSL_NAMESPACE_EXIT
169