nanovdb-editor 0.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.2
2
+ Name: nanovdb-editor
3
+ Version: 0.0.3
4
+ Summary: NanoVDB Editor Python Module
5
+ Keywords: nanovdb,volume,rendering,gaussian,splatting,3d
6
+ Author: The OpenVDB Project contributors
7
+ License: Apache-2.0
8
+ Project-URL: Homepage, https://github.com/openvdb/nanovdb-editor
9
+ Project-URL: Repository, https://github.com/openvdb/nanovdb-editor
10
+ Project-URL: Documentation, https://github.com/openvdb/nanovdb-editor/tree/main/pymodule
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+
14
+ ## NanoVDB Editor
15
+
16
+ WIP
17
+
18
+ ### Shader Parameters
19
+ Shaders can have defined struct with shader parameters which are intended to be shown in the editor's UI:
20
+ ```hlsl
21
+ struct shader_params_t
22
+ {
23
+ float4 color;
24
+ bool use_color;
25
+ bool3 _pad1;
26
+ int _pad2;
27
+ };
28
+ ConstantBuffer<shader_params_t> shader_params;
29
+ ```
30
+
31
+ Shader parameters can have defined default values in the json file:
32
+ ```json
33
+ {
34
+ "ShaderParams": {
35
+ "color": {
36
+ "value": [1.0, 0.0, 1.0, 1.0],
37
+ "min": 0.0,
38
+ "max": 1.0,
39
+ "step": 0.01
40
+ }
41
+ }
42
+ }
43
+ ```
44
+ Supported types: `bool`, `int`, `uint`, `int64`, `uint64`, `float` and its vectors and 4x4 matrix.
45
+ Variables with `_pad` in the name are not shown in the UI.
46
+ Those parameters can be interactively changed with generated UI in the editor's Params tab.
47
+
48
+ To display a group of shader parameters from different shaders define a json file with various shader paths:
49
+ ```json
50
+ {
51
+ "ShaderParams": [
52
+ "editor/editor.slang",
53
+ "test/test.slang"
54
+ ]
55
+ }
56
+ ```
@@ -0,0 +1,43 @@
1
+ ## NanoVDB Editor
2
+
3
+ WIP
4
+
5
+ ### Shader Parameters
6
+ Shaders can have defined struct with shader parameters which are intended to be shown in the editor's UI:
7
+ ```hlsl
8
+ struct shader_params_t
9
+ {
10
+ float4 color;
11
+ bool use_color;
12
+ bool3 _pad1;
13
+ int _pad2;
14
+ };
15
+ ConstantBuffer<shader_params_t> shader_params;
16
+ ```
17
+
18
+ Shader parameters can have defined default values in the json file:
19
+ ```json
20
+ {
21
+ "ShaderParams": {
22
+ "color": {
23
+ "value": [1.0, 0.0, 1.0, 1.0],
24
+ "min": 0.0,
25
+ "max": 1.0,
26
+ "step": 0.01
27
+ }
28
+ }
29
+ }
30
+ ```
31
+ Supported types: `bool`, `int`, `uint`, `int64`, `uint64`, `float` and its vectors and 4x4 matrix.
32
+ Variables with `_pad` in the name are not shown in the UI.
33
+ Those parameters can be interactively changed with generated UI in the editor's Params tab.
34
+
35
+ To display a group of shader parameters from different shaders define a json file with various shader paths:
36
+ ```json
37
+ {
38
+ "ShaderParams": [
39
+ "editor/editor.slang",
40
+ "test/test.slang"
41
+ ]
42
+ }
43
+ ```
@@ -0,0 +1 @@
1
+ 0.0.3
@@ -0,0 +1,12 @@
1
+ name: nanovdb_editor
2
+ channels:
3
+ - conda-forge
4
+ - defaults
5
+ dependencies:
6
+ - python=3.11
7
+ - pip
8
+ - numpy<2.3.0,>=2
9
+ - scikit-build-core
10
+ - pytest
11
+ - parameterized
12
+ - black
@@ -0,0 +1,8 @@
1
+ name: nanovdb_editor
2
+ channels:
3
+ - conda-forge
4
+ - defaults
5
+ dependencies:
6
+ - python=3.11
7
+ - numpy<2.3.0,>=2
8
+
@@ -0,0 +1,30 @@
1
+ # Copyright Contributors to the OpenVDB Project
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ import os
5
+ import sys
6
+
7
+ from .compiler import Compiler, pnanovdb_CompileTarget, MemoryBuffer
8
+ from .compute import Compute
9
+ from .device import DeviceInterface
10
+ from .editor import Editor, pnanovdb_EditorConfig
11
+ from .raster import Raster
12
+
13
+ if sys.platform == "win32":
14
+ lib_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "lib")
15
+ if os.path.exists(lib_dir):
16
+ from .utils import add_dll_search_directory
17
+
18
+ add_dll_search_directory(lib_dir)
19
+
20
+
21
+ __all__ = [
22
+ "Compiler",
23
+ "Compute",
24
+ "DeviceInterface",
25
+ "Editor",
26
+ "Raster",
27
+ "pnanovdb_CompileTarget",
28
+ "MemoryBuffer",
29
+ "pnanovdb_EditorConfig",
30
+ ]
@@ -0,0 +1,178 @@
1
+ # Copyright Contributors to the OpenVDB Project
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ from ctypes import *
5
+ from enum import Enum
6
+ import numpy as np
7
+ from typing import Tuple
8
+ from .utils import load_library
9
+
10
+ COMPILER_LIB = "pnanovdbcompiler"
11
+
12
+ # Match pnanovdb_bool_t (int32_t)
13
+ pnanovdb_bool_t = c_int32
14
+
15
+
16
+ class pnanovdb_CompileTarget(Enum):
17
+ UNDEFINED = 0
18
+ VULKAN = 1
19
+ CPU = 2
20
+
21
+
22
+ class pnanovdb_CompilerSettings(Structure):
23
+ """Definition equivalent to pnanovdb_compiler_settings_t."""
24
+
25
+ _fields_ = [
26
+ ("is_row_major", c_int32),
27
+ ("use_glslang", c_int32),
28
+ ("hlsl_output", c_int32),
29
+ ("compile_target", c_uint32),
30
+ ("entry_point_name", c_char * 64),
31
+ ]
32
+
33
+
34
+ class pnanovdb_CompilerInstance(Structure):
35
+ """Definition equivalent to pnanovdb_compiler_instance_t."""
36
+
37
+
38
+ class pnanovdb_Compiler(Structure):
39
+ """Definition equivalent to pnanovdb_compiler_t."""
40
+
41
+ _fields_ = [
42
+ ("interface_pnanovdb_reflect_data_type", c_void_p), # PNANOVDB_REFLECT_INTERFACE()
43
+ ("create_instance", CFUNCTYPE(POINTER(pnanovdb_CompilerInstance))),
44
+ ("set_diagnostic_callback", CFUNCTYPE(None, POINTER(pnanovdb_CompilerInstance), CFUNCTYPE(None, c_char_p))),
45
+ (
46
+ "compile_shader_from_file",
47
+ CFUNCTYPE(
48
+ pnanovdb_bool_t,
49
+ POINTER(pnanovdb_CompilerInstance),
50
+ c_char_p,
51
+ POINTER(pnanovdb_CompilerSettings),
52
+ POINTER(pnanovdb_bool_t),
53
+ ),
54
+ ),
55
+ (
56
+ "execute_cpu",
57
+ CFUNCTYPE(
58
+ pnanovdb_bool_t,
59
+ POINTER(pnanovdb_CompilerInstance),
60
+ c_char_p,
61
+ c_uint32,
62
+ c_uint32,
63
+ c_uint32,
64
+ c_void_p,
65
+ c_void_p,
66
+ ),
67
+ ),
68
+ ("destroy_instance", CFUNCTYPE(None, POINTER(pnanovdb_CompilerInstance))),
69
+ ("module", c_void_p),
70
+ ]
71
+
72
+
73
+ class MemoryBuffer(Structure):
74
+ """Python wrapper for memory buffer passed to the shader function run on CPU."""
75
+
76
+ _fields_ = [
77
+ ("data", c_void_p),
78
+ ("size", c_uint64),
79
+ ]
80
+
81
+ def __init__(self, data: np.ndarray):
82
+ """Initialize the MemoryBuffer with a numpy array."""
83
+ self.data = data.ctypes.data_as(c_void_p)
84
+ self.size = data.size
85
+
86
+ def to_ndarray(self, dtype):
87
+ """Convert memory buffer to numpy array of specified dtype."""
88
+ if not self.data or not self.size:
89
+ return np.array([], dtype=dtype)
90
+
91
+ # Create array from buffer without copying
92
+ return np.frombuffer((c_byte * (self.size * np.dtype(dtype).itemsize)).from_address(self.data), dtype=dtype)
93
+
94
+
95
+ class Compiler:
96
+ """Python wrapper for pnanovdb_compiler_t."""
97
+
98
+ def __init__(self):
99
+ """Mirrors what is in pnanovdb_compiler_load"""
100
+ self._lib = load_library(COMPILER_LIB)
101
+
102
+ get_compiler = self._lib.pnanovdb_get_compiler
103
+ get_compiler.restype = POINTER(pnanovdb_Compiler)
104
+ get_compiler.argtypes = []
105
+ self._compiler = get_compiler()
106
+ if not self._compiler:
107
+ raise RuntimeError("Failed to get compiler interface")
108
+
109
+ self._instance = None
110
+ self._compiler.contents.module = self._lib._handle
111
+
112
+ def get_compiler(self) -> POINTER(pnanovdb_Compiler):
113
+ return self._compiler
114
+
115
+ def create_instance(self) -> None:
116
+ if self._instance:
117
+ raise RuntimeError("Compiler instance already exists")
118
+
119
+ create_func = self._compiler.contents.create_instance
120
+ self._instance = create_func()
121
+
122
+ if not self._instance:
123
+ raise RuntimeError("Failed to create compiler instance")
124
+
125
+ def compile_shader(
126
+ self, filename: str, entry_point_name="main", is_row_major=False, compile_target=pnanovdb_CompileTarget.VULKAN
127
+ ) -> bool:
128
+ if not self._instance:
129
+ raise RuntimeError("No compiler instance exists")
130
+
131
+ settings = pnanovdb_CompilerSettings(
132
+ entry_point_name=entry_point_name.encode("utf-8"),
133
+ is_row_major=is_row_major,
134
+ use_glslang=False,
135
+ hlsl_output=False,
136
+ compile_target=(c_uint32)(compile_target.value),
137
+ )
138
+
139
+ compile_func = self._compiler.contents.compile_shader_from_file
140
+ return compile_func(self._instance, filename.encode("utf-8"), byref(settings), None)
141
+
142
+ def execute_cpu(
143
+ self,
144
+ filename: str,
145
+ grid_dims: Tuple[int, int, int],
146
+ uniform_params: POINTER(c_void_p),
147
+ uniform_state: POINTER(c_void_p),
148
+ ) -> bool:
149
+ if not self._instance:
150
+ raise RuntimeError("No compiler instance exists")
151
+
152
+ execute_func = self._compiler.contents.execute_cpu
153
+ return execute_func(
154
+ self._instance,
155
+ filename.encode("utf-8"),
156
+ c_uint32(grid_dims[0]),
157
+ c_uint32(grid_dims[1]),
158
+ c_uint32(grid_dims[2]),
159
+ uniform_params,
160
+ uniform_state,
161
+ )
162
+
163
+ def destroy_instance(self) -> None:
164
+ if not self._instance:
165
+ return
166
+
167
+ destroy_func = self._compiler.contents.destroy_instance
168
+ destroy_func(self._instance)
169
+
170
+ self._instance = None
171
+
172
+ def __del__(self):
173
+ # Do not call into native library from Python finalizer; just drop refs
174
+ try:
175
+ self._instance = None
176
+ self._compiler = None
177
+ except Exception:
178
+ pass
@@ -0,0 +1,275 @@
1
+ # Copyright Contributors to the OpenVDB Project
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ from ctypes import *
5
+ from typing import Tuple
6
+ import numpy as np
7
+
8
+ from .utils import load_library
9
+ from .device import DeviceInterface, pnanovdb_DeviceInterface, pnanovdb_Device
10
+ from .compiler import Compiler, pnanovdb_Compiler
11
+
12
+ COMPUTE_LIB = "pnanovdbcompute"
13
+
14
+ # Match pnanovdb_bool_t (int32_t)
15
+ pnanovdb_bool_t = c_int32
16
+
17
+ VULKAN_API = 1
18
+ PNANOVDB_TRUE = 1
19
+
20
+
21
+ class pnanovdb_ComputeShaderSource(Structure):
22
+ pass
23
+
24
+
25
+ class pnanovdb_ComputeShaderBuild(Structure):
26
+ pass
27
+
28
+
29
+ class pnanovdb_ShaderInterface(Structure):
30
+ """Definition equivalent to pnanovdb_compute_shader_interface_t."""
31
+
32
+ _fields_ = [
33
+ ("interface_pnanovdb_reflect_data_type", c_void_p), # PNANOVDB_REFLECT_INTERFACE()
34
+ ("create_shader", CFUNCTYPE(c_void_p, POINTER(pnanovdb_ComputeShaderSource))),
35
+ ("map_shader_build", CFUNCTYPE(pnanovdb_bool_t, c_void_p, POINTER(POINTER(pnanovdb_ComputeShaderBuild)))),
36
+ ("destroy_shader", CFUNCTYPE(None, c_void_p)),
37
+ ]
38
+
39
+
40
+ class pnanovdb_ComputeArray(Structure):
41
+ """Definition equivalent to pnanovdb_compute_array_t."""
42
+
43
+ _fields_ = [("data", c_void_p), ("element_size", c_uint64), ("element_count", c_uint64), ("filepath", c_char_p)]
44
+
45
+ def __init__(self, data=None, element_size=0, element_count=0, filepath=None):
46
+ super().__init__()
47
+ self.data = data
48
+ self.element_size = element_size
49
+ self.element_count = element_count
50
+ self.filepath = filepath.encode("utf-8") if isinstance(filepath, str) else filepath
51
+
52
+
53
+ class pnanovdb_Compute(Structure):
54
+ """Definition equivalent to pnanovdb_compute_t."""
55
+
56
+ _fields_ = [
57
+ ("interface_pnanovdb_reflect_data_type", c_void_p), # PNANOVDB_REFLECT_INTERFACE()
58
+ ("module", c_void_p),
59
+ ("compiler", POINTER(pnanovdb_Compiler)),
60
+ ("shader_interface", pnanovdb_ShaderInterface),
61
+ ("device_interface", pnanovdb_DeviceInterface),
62
+ ("load_nanovdb", CFUNCTYPE(POINTER(pnanovdb_ComputeArray), c_char_p)),
63
+ ("save_nanovdb", CFUNCTYPE(pnanovdb_bool_t, POINTER(pnanovdb_ComputeArray), c_char_p)),
64
+ ("create_shader_context", CFUNCTYPE(c_void_p, c_char_p)),
65
+ ("destroy_shader_context", CFUNCTYPE(None, c_void_p)),
66
+ (
67
+ "init_shader",
68
+ CFUNCTYPE(
69
+ pnanovdb_bool_t,
70
+ c_void_p, # POINTER(pnanovdb_Compute)
71
+ c_void_p, # pnanovdb_compute_queue_t*
72
+ c_void_p, # pnanovdb_shader_context_t*
73
+ c_void_p,
74
+ ),
75
+ ), # pnanovdb_compiler_settings_t*
76
+ (
77
+ "destroy_shader",
78
+ CFUNCTYPE(
79
+ None,
80
+ c_void_p, # POINTER(pnanovdb_Compute)
81
+ POINTER(pnanovdb_ShaderInterface),
82
+ c_void_p, # pnanovdb_compute_context_t*
83
+ c_void_p,
84
+ ),
85
+ ), # pnanovdb_shader_context_t*
86
+ (
87
+ "dispatch_shader",
88
+ CFUNCTYPE(
89
+ None,
90
+ c_void_p, # POINTER(pnanovdb_Compute)
91
+ c_void_p, # pnanovdb_compute_context_t*
92
+ c_void_p, # const pnanovdb_shader_context_t*
93
+ c_void_p, # pnanovdb_compute_resource_t*
94
+ c_uint32, # grid_dim_x
95
+ c_uint32, # grid_dim_y
96
+ c_uint32, # grid_dim_z
97
+ c_char_p,
98
+ ),
99
+ ), # debug_label
100
+ (
101
+ "dispatch_shader_on_array",
102
+ CFUNCTYPE(
103
+ pnanovdb_bool_t,
104
+ c_void_p, # POINTER(pnanovdb_Compute)
105
+ POINTER(pnanovdb_Device),
106
+ c_char_p,
107
+ c_uint32,
108
+ c_uint32,
109
+ c_uint32,
110
+ POINTER(pnanovdb_ComputeArray),
111
+ POINTER(pnanovdb_ComputeArray),
112
+ POINTER(pnanovdb_ComputeArray),
113
+ c_uint32,
114
+ c_uint64,
115
+ c_uint64,
116
+ ),
117
+ ),
118
+ (
119
+ "dispatch_shader_on_nanovdb_array",
120
+ CFUNCTYPE(
121
+ pnanovdb_bool_t,
122
+ c_void_p, # POINTER(pnanovdb_Compute)
123
+ c_void_p, # const pnanovdb_compute_device_t*
124
+ c_void_p, # const pnanovdb_shader_context_t*
125
+ POINTER(pnanovdb_ComputeArray), # nanovdb_array
126
+ c_int32, # image_width
127
+ c_int32, # image_height
128
+ c_void_p, # background_image
129
+ c_void_p, # upload_buffer
130
+ c_void_p, # user_upload_buffer
131
+ POINTER(c_void_p), # nanovdb_buffer
132
+ POINTER(c_void_p),
133
+ ),
134
+ ), # readback_buffer
135
+ ("create_array", CFUNCTYPE(POINTER(pnanovdb_ComputeArray), c_size_t, c_uint64, c_void_p)),
136
+ ("destroy_array", CFUNCTYPE(None, POINTER(pnanovdb_ComputeArray))),
137
+ ("map_array", CFUNCTYPE(c_void_p, POINTER(pnanovdb_ComputeArray))),
138
+ ("unmap_array", CFUNCTYPE(None, POINTER(pnanovdb_ComputeArray))),
139
+ (
140
+ "compute_array_print_range",
141
+ CFUNCTYPE(
142
+ None,
143
+ c_void_p, # POINTER(pnanovdb_Compute)
144
+ c_void_p, # pnanovdb_compute_log_print_t
145
+ c_char_p, # name
146
+ POINTER(pnanovdb_ComputeArray), # arr
147
+ c_uint32,
148
+ ),
149
+ ), # channel_count
150
+ ]
151
+
152
+
153
+ class Compute:
154
+ """Python wrapper for pnanovdb_compute_t."""
155
+
156
+ def __init__(self, compiler: Compiler):
157
+ """Mirrors what is in pnanovdb_compute_load"""
158
+ self._compiler = compiler
159
+
160
+ self._lib = load_library(COMPUTE_LIB)
161
+
162
+ # only vulkan is supported for now
163
+ self._device_interface = DeviceInterface(VULKAN_API)
164
+
165
+ get_compute = self._lib.pnanovdb_get_compute
166
+ get_compute.restype = POINTER(pnanovdb_Compute)
167
+ get_compute.argtypes = []
168
+
169
+ self._compute = get_compute()
170
+ if not self._compute:
171
+ raise RuntimeError("Failed to get compute interface")
172
+
173
+ compiler_ptr = compiler.get_compiler()
174
+ if not compiler_ptr:
175
+ raise RuntimeError("Failed to get compiler interface")
176
+
177
+ self._compute.contents.compiler = compiler_ptr
178
+ self._compute.contents.device_interface = self._device_interface.get_device_interface().contents
179
+ self._compute.contents.module = self._lib._handle
180
+
181
+ get_shader_interface = self._lib.pnanovdb_get_compute_shader_interface
182
+ get_shader_interface.restype = POINTER(pnanovdb_ShaderInterface)
183
+ get_shader_interface.argtypes = []
184
+
185
+ self._compute.contents.shader_interface = get_shader_interface().contents
186
+
187
+ def get_compute(self) -> POINTER(pnanovdb_Compute):
188
+ return self._compute
189
+
190
+ def compiler(self) -> Compiler:
191
+ return self._compiler
192
+
193
+ def device_interface(self) -> DeviceInterface:
194
+ return self._device_interface
195
+
196
+ def load_nanovdb(self, filepath: str) -> pnanovdb_ComputeArray:
197
+ load_func = self._compute.contents.load_nanovdb
198
+ array = load_func(filepath.encode("utf-8"))
199
+ if not array:
200
+ raise RuntimeError(f"Failed to load NanoVDB file: {filepath}")
201
+ return array.contents
202
+
203
+ def save_nanovdb(self, array: pnanovdb_ComputeArray, filepath: str) -> None:
204
+ save_func = self._compute.contents.save_nanovdb
205
+ save_func(pointer(array), filepath.encode("utf-8"))
206
+
207
+ def create_array(self, data: np.ndarray) -> pnanovdb_ComputeArray:
208
+ create_func = self._compute.contents.create_array
209
+ array = create_func(data.itemsize, data.size, data.ctypes.data_as(c_void_p))
210
+ if not array:
211
+ raise RuntimeError("Failed to create compute array")
212
+ return array.contents
213
+
214
+ def destroy_array(self, array: pnanovdb_ComputeArray) -> None:
215
+ destroy_func = self._compute.contents.destroy_array
216
+ destroy_func(pointer(array))
217
+
218
+ def dispatch_shader_on_array(
219
+ self,
220
+ shader_path: str,
221
+ grid_dims: Tuple[int, int, int],
222
+ data_in: pnanovdb_ComputeArray,
223
+ constants: pnanovdb_ComputeArray,
224
+ data_out: pnanovdb_ComputeArray,
225
+ dispatch_count: int = 1,
226
+ scratch_size: int = 0,
227
+ scratch_clear_size: int = 0,
228
+ ) -> bool:
229
+ if not data_in or not constants or not data_out:
230
+ raise ValueError("ComputeArray parameters cannot be None")
231
+
232
+ dispatch_func = self._compute.contents.dispatch_shader_on_array
233
+ result = dispatch_func(
234
+ self._compute,
235
+ self._device_interface.get_device(),
236
+ shader_path.encode("utf-8"),
237
+ c_uint32(grid_dims[0]),
238
+ c_uint32(grid_dims[1]),
239
+ c_uint32(grid_dims[2]),
240
+ pointer(data_in),
241
+ pointer(constants),
242
+ pointer(data_out),
243
+ c_uint32(dispatch_count),
244
+ c_uint64(scratch_size),
245
+ c_uint64(scratch_clear_size),
246
+ )
247
+
248
+ return result == PNANOVDB_TRUE
249
+
250
+ def map_array(self, array: pnanovdb_ComputeArray, np_dtype: np.dtype) -> np.ndarray:
251
+ if array.element_size != np_dtype.itemsize:
252
+ raise ValueError("Array element size mismatches the provided dtype")
253
+
254
+ map_func = self._compute.contents.map_array
255
+ data_ptr = map_func(pointer(array))
256
+ if not data_ptr:
257
+ raise RuntimeError("Failed to map array")
258
+
259
+ buffer = (c_byte * (array.element_size * array.element_count)).from_address(data_ptr)
260
+ return np.frombuffer(buffer, dtype=np_dtype)
261
+
262
+ def unmap_array(self, array: pnanovdb_ComputeArray) -> None:
263
+ unmap_func = self._compute.contents.unmap_array
264
+ unmap_func(pointer(array))
265
+
266
+ def array_exists(self, array: pnanovdb_ComputeArray) -> bool:
267
+ return array and array.data is not None
268
+
269
+ def __del__(self):
270
+ try:
271
+ self._compute = None
272
+ self._compiler = None
273
+ self._device_interface = None
274
+ except Exception:
275
+ pass