diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index ba0efa95..b040125d 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -35,11 +35,6 @@ target_include_directories(laikago_opengl_tiny_example PRIVATE ../third_party/ti target_include_directories(laikago_opengl_tiny_example PRIVATE ../third_party ../src) target_compile_definitions(laikago_opengl_tiny_example PRIVATE USE_TINY ) -add_executable(inclined_plane_gui inclined_plane_gui.cpp ../third_party/stb_image/stb_image.cpp ../third_party/tinyobjloader/tiny_obj_loader.cc ${TDS_HDRS}) -target_link_libraries(inclined_plane_gui Eigen3::Eigen tinyxml2 opengl_window ${CMAKE_DL_LIBS}) -target_include_directories(inclined_plane_gui PRIVATE ../third_party/tinyobjloader) -target_include_directories(inclined_plane_gui PRIVATE ../third_party ../src) -target_compile_definitions(inclined_plane_gui PRIVATE USE_TINY ) endif(Eigen3_FOUND) diff --git a/examples/opengl_imgui_test.cpp b/examples/opengl_imgui_test.cpp index 25a89b49..63e252c3 100644 --- a/examples/opengl_imgui_test.cpp +++ b/examples/opengl_imgui_test.cpp @@ -725,7 +725,7 @@ int main(int argc, char* argv[]) { ImGui::StyleColorsDark(); if (1) { - float SCALE = 2.0f; + float SCALE = 1.0f; ImFontConfig cfg; cfg.SizePixels = 13 * SCALE; ImGui::GetIO().Fonts->AddFontDefault(&cfg)->DisplayOffset.y = SCALE; diff --git a/examples/opengl_urdf_visualizer.h b/examples/opengl_urdf_visualizer.h index d83e1cc5..c03d310b 100644 --- a/examples/opengl_urdf_visualizer.h +++ b/examples/opengl_urdf_visualizer.h @@ -30,6 +30,7 @@ #include "visualizer/opengl/tiny_opengl3_app.h" #include "visualizer/opengl/utils/tiny_mesh_utils.h" + // disabled #define USE_SDF_TO_MESH, since code crashes when radius == 0 // #define USE_SDF_TO_MESH #ifdef USE_SDF_TO_MESH @@ -39,6 +40,15 @@ struct UrdfInstancePair { int m_link_index; int m_visual_instance; + ::TINY::TinyVector3f viz_origin_xyz; + ::TINY::TinyVector3f viz_origin_rpy; + + UrdfInstancePair() + : m_link_index(-1), + m_visual_instance(-1), + viz_origin_xyz(0.f, 0.f, 0.f), + viz_origin_rpy(0.f, 0.f, 0.f) {} + }; template @@ -75,8 +85,12 @@ struct OpenGLUrdfVisualizer { TinyOpenGL3App m_opengl_app; OpenGLUrdfVisualizer(int width = 1024, int height = 768, - const char *title = "Tiny Differentiable Simulator") - : m_uid(1234), m_opengl_app(title, width, height) { + const char *title = "Tiny Differentiable Simulator", + bool allowRetina = true, int window_type = 0, + int render_device = -1, int max_num_object_capacity = 128 * 1024, + int max_shape_capacity_in_bytes = 128 * 1024 * 1024) + : m_uid(1234), m_opengl_app(title, width, height, allowRetina, window_type, + render_device, max_num_object_capacity, max_shape_capacity_in_bytes) { m_opengl_app.m_renderer->init(); m_opengl_app.set_up_axis(2); m_opengl_app.m_renderer->get_active_camera()->set_camera_distance(4); @@ -272,7 +286,15 @@ struct OpenGLUrdfVisualizer { b2v.visual_shape_uids.push_back(shape_id); ::TINY::TinyVector3f color(1, 1, 1); - b2v.shape_colors.push_back(color); + if (v.has_local_material) { + if (urdf.materials.count(v.material_name)) { + color = ::TINY::TinyVector3f ( + Algebra::to_double(urdf.materials[v.material_name].material_rgb[0]), + Algebra::to_double(urdf.materials[v.material_name].material_rgb[1]), + Algebra::to_double(urdf.materials[v.material_name].material_rgb[2])); + } + } + b2v.shape_colors.push_back(::TINY::TinyVector3f (color[0],color[1],color[2])); break; } case ::tds::TINY_CAPSULE_TYPE: { @@ -298,7 +320,7 @@ struct OpenGLUrdfVisualizer { b2v.visual_shape_uids.push_back(shape_id); ::TINY::TinyVector3f color(1, 1, 1); - b2v.shape_colors.push_back(color); + b2v.shape_colors.push_back(::TINY::TinyVector3f (color[0],color[1],color[2])); break; } #ifdef USE_SDF_TO_MESH @@ -316,6 +338,24 @@ struct OpenGLUrdfVisualizer { b2v.shape_colors.push_back(color); break; } +#else + case ::tds::TINY_CYLINDER_TYPE: { + //::tds::Cylinder gen_cylinder(v.geometry.cylinder.radius, + // v.geometry.cylinder.length); + + float radius = Algebra::to_double(v.geometry.cylinder.radius); + float half_height = + Algebra::to_double(v.geometry.cylinder.length) * 0.5; + int up_axis = 2; + + int shape_id = m_opengl_app.register_graphics_cylinder_shape( + radius, half_height, up_axis, -1); + + b2v.visual_shape_uids.push_back(shape_id); + ::TINY::TinyVector3f color(1, 1, 1); + b2v.shape_colors.push_back(::TINY::TinyVector3f (color[0],color[1],color[2])); + break; + } #endif case ::tds::TINY_BOX_TYPE: { float half_extentsx = @@ -328,6 +368,14 @@ struct OpenGLUrdfVisualizer { half_extentsx, half_extents_y, half_extents_z); b2v.visual_shape_uids.push_back(shape_id); ::TINY::TinyVector3f color(1, 1, 1); + if (v.has_local_material) { + if (urdf.materials.count(v.material_name)) { + color = ::TINY::TinyVector3f( + Algebra::to_double(urdf.materials[v.material_name].material_rgb[0]), + Algebra::to_double(urdf.materials[v.material_name].material_rgb[1]), + Algebra::to_double(urdf.materials[v.material_name].material_rgb[2])); + } + } b2v.shape_colors.push_back(color); break; @@ -412,12 +460,17 @@ struct OpenGLUrdfVisualizer { for (int v = 0; v < vis_link.visual_shape_uids.size(); v++) { int sphere_shape = vis_link.visual_shape_uids[v]; ::TINY::TinyVector3f color(1, 1, 1); + if (v < vis_link.shape_colors.size()) { + color = vis_link.shape_colors[v]; + } // visualizer.m_b2vis for (int ni = 0; ni < num_instances; ni++) { instance = m_opengl_app.m_renderer->register_graphics_instance( sphere_shape, pos, orn, color, scaling, 1.0, false); pair.m_link_index = -1; pair.m_visual_instance = instance; + pair.viz_origin_xyz = vis_link.origin_xyz; + pair.viz_origin_rpy = vis_link.origin_rpy; all_instances[ni].push_back(pair); } } @@ -435,12 +488,18 @@ struct OpenGLUrdfVisualizer { for (int v = 0; v < vis_link.visual_shape_uids.size(); v++) { int sphere_shape = vis_link.visual_shape_uids[v]; ::TINY::TinyVector3f color(1, 1, 1); + if (v < vis_link.shape_colors.size()) { + color = vis_link.shape_colors[v]; + + } // visualizer.m_b2vis for (int ni = 0; ni < num_instances; ni++) { instance = m_opengl_app.m_renderer->register_graphics_instance( sphere_shape, pos, orn, color, scaling, 1.0, false); pair.m_link_index = i; pair.m_visual_instance = instance; + pair.viz_origin_xyz = vis_link.origin_xyz; + pair.viz_origin_rpy = vis_link.origin_rpy; all_instances[ni].push_back(pair); } } @@ -523,32 +582,81 @@ struct OpenGLUrdfVisualizer { void sync_visual_transforms2( const std::vector < std::vector> & all_instances, const std::vector < std::vector> & visual_world_transforms_array, - int visual_offset, float sim_spacing) { + int visual_offset, float sim_spacing, bool apply_visual_offset, + const std::vector& link_mapping) { int batch_size = all_instances.size(); + int viz_sz = visual_world_transforms_array.size(); for (int ni = 0; ni < batch_size; ni++) { // visual_world_transforms.size(); const auto& pairs = all_instances[ni]; + if (ni>=visual_world_transforms_array.size()) + continue; + const auto &visual_world_transforms = visual_world_transforms_array[ni]; const int square_id = (int)std::sqrt((double)batch_size); // for (int ni = 0; ni < batch_size; ni++) { int index = visual_offset; for (int i = 0; i < pairs.size(); i++) { auto pair = pairs[i]; + int link_index = pair.m_link_index; + bool skip=false; + if (link_mapping.size()==pairs.size()) + { + int m = link_mapping[i]; + if (m<0) + skip=true; + else + index = visual_offset+(link_mapping[i]*7); + } + if ((index+6)>visual_world_transforms.size()) + skip=true; + + if (!skip) + { + // Transform geom_X_world = body->base_X_world() * body->X_visuals()[v]; + // visual_world_transforms + ::TINY::TinyPosef pose_rbd; + pose_rbd.m_position.setValue(visual_world_transforms[index + 0], + visual_world_transforms[index + 1], + visual_world_transforms[index + 2]); + + pose_rbd.m_orientation.setValue(visual_world_transforms[index + 3], + visual_world_transforms[index + 4], + visual_world_transforms[index + 5], + visual_world_transforms[index + 6]); + + ::TINY::TinyPosef pose_viz; + if (apply_visual_offset) { + pose_viz.m_position = pair.viz_origin_xyz; + pose_viz.m_orientation.set_euler_rpy(pair.viz_origin_rpy); + }else { + pose_viz.set_identity(); + } - ::TINY::TinyVector3f pos( - visual_world_transforms[index + 0] + - sim_spacing * (ni % square_id) - square_id * sim_spacing / 2, - visual_world_transforms[index + 1] + - sim_spacing * (ni / square_id) - square_id * sim_spacing / 2, - visual_world_transforms[index + 2]); - - ::TINY::TinyQuaternionf orn(visual_world_transforms[index + 3], - visual_world_transforms[index + 4], - visual_world_transforms[index + 5], - visual_world_transforms[index + 6]); - index += 7; - m_opengl_app.m_renderer->write_single_instance_transform_to_cpu( - pos, orn, pair.m_visual_instance); + ::TINY::TinyPosef pose_w = pose_rbd * pose_viz; + + ::TINY::TinyVector3f pos = m_opengl_app.get_up_axis()==2? + + ::TINY::TinyVector3f( + pose_w.m_position.x() + + sim_spacing * (ni % square_id) - square_id * sim_spacing / 2, + pose_w.m_position.y() + + sim_spacing * (ni / square_id) - square_id * sim_spacing / 2, + pose_w.m_position.z()): + ::TINY::TinyVector3f( + pose_w.m_position.x() + + sim_spacing * (ni % square_id) - square_id * sim_spacing / 2, + pose_w.m_position.y(), + pose_w.m_position.z() + + sim_spacing * (ni / square_id) - square_id * sim_spacing / 2 + ); + + ::TINY::TinyQuaternionf orn = pose_w.m_orientation; + + index += 7; + m_opengl_app.m_renderer->write_single_instance_transform_to_cpu( + pos, orn, pair.m_visual_instance); + } } } } @@ -588,23 +696,73 @@ struct OpenGLUrdfVisualizer { } } } - void render() { - int upAxis = 2; - m_opengl_app.m_renderer->write_transforms(); + + void render(bool do_swap_buffer = true, bool render_segmentation_mask=false, int upAxis=2, bool write_transforms = true) { + if (write_transforms) + m_opengl_app.m_renderer->write_transforms(); m_opengl_app.m_renderer->update_camera(upAxis); double lightPos[3] = {-50, 30, 40}; m_opengl_app.m_renderer->set_light_position(lightPos); float specular[3] = {1, 1, 1}; m_opengl_app.m_renderer->set_light_specular_intensity(specular); DrawGridData data; - data.upAxis = 2; + data.upAxis = upAxis; data.drawAxis = true; m_opengl_app.draw_grid(data); // const char* bla = "3d label"; // m_opengl_app.draw_text_3d(bla, 0, 0, 1, 1); - m_opengl_app.m_renderer->render_scene(); - m_opengl_app.swap_buffer(); + if (render_segmentation_mask) + { + std::vector tiles; + m_opengl_app.m_renderer->render_scene_internal( + tiles, B3_SEGMENTATION_MASK_RENDERMODE); + } + else + { + m_opengl_app.m_renderer->render_scene(); + } + if (do_swap_buffer) + { + m_opengl_app.swap_buffer(); + } } -}; + + void render_tiled(std::vector &tiles, + bool do_swap_buffer = true, + bool render_segmentation_mask = false, + int upAxis =2, + bool write_transforms = true ) { + if (write_transforms) + m_opengl_app.m_renderer->write_transforms(); + m_opengl_app.m_renderer->update_camera(upAxis); + double lightPos[3] = {-50, 30, 40}; + m_opengl_app.m_renderer->set_light_position(lightPos); + float specular[3] = {1, 1, 1}; + m_opengl_app.m_renderer->set_light_specular_intensity(specular); +// m_opengl_app.m_renderer->render_scene2(tiles); + + if (render_segmentation_mask) + { + m_opengl_app.m_renderer->render_scene_internal( + tiles, B3_SEGMENTATION_MASK_RENDERMODE); + } + else + { + m_opengl_app.m_renderer->render_scene_internal( + tiles, B3_DEFAULT_RENDERMODE); + + } + if (do_swap_buffer) + { + m_opengl_app.swap_buffer(); + } + } + + + void swap_buffer() { + m_opengl_app.swap_buffer(); + } + + }; #endif // OPENGL_URDF_VISUALIZER_H diff --git a/python/examples/render_tiled.py b/python/examples/render_tiled.py new file mode 100644 index 00000000..c64149b4 --- /dev/null +++ b/python/examples/render_tiled.py @@ -0,0 +1,333 @@ +#for pytinyopengl3: pip install pytinydiffsim, use latest, at least version >= 0.5.0 +import pytinyopengl3 as g +import math +from numpngw import write_apng +frames = [] + +import torch +import numpy as np +use_cv2 = False +if use_cv2: + import cv2 + +#from omni.isaac.core.utils.nucleus import get_assets_root_path +#assets_root_path = get_assets_root_path() +#if assets_root_path is None: +# print("Cannot find Isaac Asset Root Path") +#else: +# print("assets_root_path=",assets_root_path) + +use_cuda_interop = True + + + +use_image_observation = True + +class CartpoleTest: + def __init__(self, num_envs=256, enable_tiled = False, use_matplot_lib = False): + self.num_envs = num_envs + self.use_camera = True + self.camera_image_stack = 1 + self.camera_type = "rgbd" + self.enable_tiled = enable_tiled + self.once = True + self.use_matplot_lib = use_matplot_lib + if enable_tiled and self.use_matplot_lib: + import matplotlib.pyplot as plt + import matplotlib + self.plt = plt + self.plt.ion() + img = np.random.rand(2000, 2000) + self.matplotlib_image = self.plt.imshow(img, interpolation='none')#, cmap='gray', vmin=0.8, vmax=1) + self.ax = self.plt.gca() + else: + self.use_matplot_lib = False + + self.counter=0 + if self.camera_type == "rgb": + self.camera_channels = 3 + elif self.camera_type == "grey": + self.camera_channels = 1 + elif self.camera_type == "depth": + self.camera_channels = 1 + elif self.camera_type == "rgbd": + self.camera_channels = 4 + else: + raise NotImplementedError(f"Unsupported camera type {self.camera_type}") + + self.camera_width = 512 + self.camera_height = 512 + self.device = "cuda" + if self.use_camera: + + self.max_x = math.ceil(math.sqrt(self.num_envs)) + self.tile_width = self.camera_width + self.tile_height = self.camera_height + self.width=self.tile_width * self.max_x + self.height=self.tile_height * self.max_x + + + num_actors=self.num_envs + + self.camera_image_stack=1 + self.num_stacked_channels = self.camera_channels + self.camera_image_stack*self.camera_channels + #self.obs_space = spaces.Box(np.ones((self.camera_height, self.camera_width, self.num_stacked_channels)) * -np.Inf, + # np.ones((self.camera_height, self.camera_width, self.num_stacked_channels)) * np.Inf) + + #self.obs_buf = torch.zeros( + # (self.num_envs, self.camera_height, self.camera_width, self.num_stacked_channels), device=self.device, dtype=torch.float) + self.obs_buf = torch.zeros( + (self.num_envs, self.camera_height, self.camera_width, self.num_stacked_channels), device=self.device, dtype=torch.float) + print("####################################self.obs_buf.shape=",self.obs_buf.shape) + + #self.obs_buf = torch.zeros( + # (self.num_envs, self.camera_height* self.camera_width* self.camera_channels), device=self.device, dtype=torch.float) + #print("self.obs_buf.shape=",self.obs_buf.shape) + + if self.enable_tiled: + window_type = 2 + else: + window_type = 0 + self.viz = g.OpenGLUrdfVisualizer(width=self.width, height=self.height, window_type=window_type) + self.viz.opengl_app.set_background_color(1.,1.,1.) + self.viz.opengl_app.swap_buffer() + self.viz.opengl_app.swap_buffer() + + if use_cuda_interop: + self.render_texid = self.viz.opengl_app.enable_render_to_texture(self.width, self.height) + self.viz.opengl_app.swap_buffer() + self.viz.opengl_app.swap_buffer() + self.cuda_tex = self.viz.opengl_app.cuda_register_texture_image(self.render_texid, True) + self.cuda_num_bytes = self.width*self.height*4*2 #4 component half-float, each half-float 2 bytes + print("cuda_num_bytes=", self.cuda_num_bytes) + self.ttensor = torch.zeros(self.width*self.height*4, dtype=torch.float16, device=self.device) + self.cuda_mem = self.ttensor.data_ptr() + + + urdf = g.OpenGLUrdfStructures() + parser = g.UrdfParser() + file_name = "can.urdf" + urdf = parser.load_urdf(file_name) + print("urdf=",urdf) + texture_path = "texture_map.png" + self.viz.path_prefix = g.extract_path(file_name) + print("viz.path_prefix=",self.viz.path_prefix) + self.viz.convert_visuals(urdf, texture_path) + print("create_instances") + + self.all_instances = self.viz.create_instances(urdf, texture_path, num_actors) + #print("self.all_instances=",self.all_instances) + verbose_print = False + if verbose_print: + print("len(self.all_instances)=",len(self.all_instances)) + for pairs in self.all_instances: + print("len(pairs)=", len(pairs)) + for pair in pairs: + print("pair.visual_instance=",pair.visual_instance) + + if self.enable_tiled: + + print("tile_width=", self.tile_width) + print("tile_height=", self.tile_height) + print("self.num_envs=", self.num_envs) + self.tiles=[] + x=0 + y=0 + for t in range (self.num_envs): + tile = g.TinyViewportTile() + pairs = self.all_instances[t] + viz_instances = [] + for pair in pairs: + viz_instances.append(pair.visual_instance) + print("viz_instances=",viz_instances) + tile.visual_instances = viz_instances#[t, 512+t, 1024+t] + print("tile.visual_instances=",tile.visual_instances) + cam = self.viz.opengl_app.renderer.get_active_camera() + tile.projection_matrix = cam.get_camera_projection_matrix() + tile.view_matrix = cam.get_camera_view_matrix() + tile.viewport_dims=[x*self.tile_width,y*self.tile_height,self.tile_width, self.tile_height] + self.tiles.append(tile) + x+=1 + if x>=self.max_x: + x=0 + y+=1 + + cam = g.TinyCamera() + cam.set_camera_up_axis(2) + cam.set_camera_distance(0.7) + cam.set_camera_pitch(0)#-30) + cam.set_camera_yaw(90)#-30) + cam.set_camera_target_position(0.,0.,0.0) + #if not self.enable_tiled: + self.viz.opengl_app.renderer.write_transforms() + self.viz.opengl_app.renderer.set_camera(cam) + def sync_transforms_cpu(self, rb_transforms): + skip = 0 + if self.enable_tiled: + sim_spacing = 0 + else: + sim_spacing = 10 + self.viz.sync_visual_transforms(self.all_instances, rb_transforms, skip, sim_spacing,apply_visual_offset=True) + + + def update_observations(self, write_transforms, camera_positions=None): + if self.use_camera: + import time + start_time = time.time() + + #print("len(camera_positions)=",len(camera_positions)) + if use_cuda_interop: + self.viz.opengl_app.enable_render_to_texture(self.width, self.height) + + if self.enable_tiled: + + cam = self.viz.opengl_app.renderer.get_active_camera() + tile_index = 0 + x=0 + y=0 + #self.max_x + for tile_index in range (self.num_envs): + tile = self.tiles[tile_index] + + if camera_positions is None: + tile.view_matrix = cam.get_camera_view_matrix() + else: + cam_target = g.TinyVector3f(camera_positions[tile_index][0], + camera_positions[tile_index][1], + camera_positions[tile_index][2]) + cam_up = g.TinyVector3f(0. ,0., 1.) + cam_pos = g.TinyVector3f(camera_positions[tile_index][0], + camera_positions[tile_index][1]-1.5, + camera_positions[tile_index][2]) + + view_mat = g.compute_camera_view_matrix(cam_pos, cam_target, cam_up) + tile.view_matrix = view_mat + + tile.viewport_dims=[x*self.tile_width,y*self.tile_height,self.tile_width, self.tile_height] + x+=1 + if x>=self.max_x: + x=0 + y+=1 + + self.viz.render_tiled(self.tiles, do_swap_buffer = False, render_segmentation_mask=False, write_transforms=write_transforms) + else: + self.viz.render(do_swap_buffer=False, render_segmentation_mask=False, write_transforms=write_transforms) + #up_axis = 2 + #self.viz.opengl_app.renderer.update_camera(up_axis) + #self.viz.opengl_app.renderer.render_scene() + #self.viz.opengl_app.swap_buffer() + + ct = time.time() + if use_cuda_interop: + self.viz.opengl_app.cuda_copy_texture_image(self.cuda_tex, self.cuda_mem, self.cuda_num_bytes) + #print("self.ttensor.shape=",self.ttensor.shape) + #print("self.ttensor=",self.ttensor) + else: + pixels = g.ReadPixelBuffer(self.viz.opengl_app) + et = time.time() + #print("cuda_copy_texture_image dt=", et-ct) + + + end_time = time.time() + #print("duration =", end_time-start_time) + #print("fps =", float(self.num_envs)/(end_time-start_time)) + + + if self.use_matplot_lib: + if use_cuda_interop: + ftensor = self.ttensor.type(torch.float32) + np_img_arr = ftensor.cpu().numpy() + np_img_arr = np.reshape(np_img_arr, (self.height, self.width, 4)) + np_img_arr = np.flipud(np_img_arr) + else: + np_img_arr = pixels.rgba + np_img_arr = np.reshape(np_img_arr, (self.height, self.width, 4)) + np_img_arr = np_img_arr * (1. / 255.) + np_img_arr = np.flipud(np_img_arr) + + self.matplotlib_image.set_data(np_img_arr) + self.ax.plot([0]) + self.plt.show() + self.plt.pause(0.0001) + + self.viz.swap_buffer() + + + #print("self.dof_pos[env_ids, 0].shape=",self.dof_pos[env_ids, 0].shape) + #sq = self.dof_pos[env_ids, 0].squeeze() + + #print("sq.shape=",sq.shape) + if use_image_observation: + #this copy/reshaping is sub-optimal, need a person with some pytorch-fu + #ftensor = self.ttensor.type(torch.float32)*255. + ftensor = self.ttensor.type(torch.float32) + ftensor = torch.reshape(ftensor, (self.height, self.width, 4)) + #ftensor = torch.flipud(ftensor) + ftensor = ftensor.reshape(self.max_x, self.tile_width, self.max_x, self.tile_height, 4) + ftensor = ftensor.swapaxes(1,2) + ftensor = ftensor.reshape(self.max_x*self.max_x, self.tile_width*self.tile_height*4) + ftensor = ftensor[:self.num_envs,] + #print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") + #print("ftensor.shape=",ftensor.shape) + ftensor = ftensor.reshape(self.max_x*self.max_x, self.tile_width,self.tile_height,4) + #self.obs_buf = ftensor + #print("ftensor.shape=", ftensor.shape) + #self.obs_buf = ftensor + + #self.gym.render_all_camera_sensors(self.sim) + #self.gym.start_access_image_tensors(self.sim) + stk = self.camera_image_stack + #print("stk=", stk) + if stk > 0: + # move the previous (stack-1) frames 1 step forward in the buffer + self.obs_buf[:, :, :, self.camera_channels] = self.obs_buf[:, :, :, 0] + #self.obs_buf[:, :, :, (1) * self.camera_channels: (stk) * self.camera_channels] + #self.obs_buf[:, :, :, (0) * self.camera_channels: (stk-1) * self.camera_channels] + + #print("self.obs_buf.shape=",self.obs_buf.shape) + #print("ftensor.shape=", ftensor.shape) + #print("self.obs_buf.shape=", self.obs_buf.shape) + + + self.obs_buf[:, :, :, 0:self.camera_channels] = ftensor + #print("self.camera_channels=",self.camera_channels) + #print("self.obs_buf.shape=", self.obs_buf.shape) + self.counter += 1 + if self.counter == 1: + for id in np.arange(self.num_envs): + # camera_gpu_tensor = self.camera_tensors[id][:, :, 0:self.camera_channels].clone() + # #if (id==0): + # # print("camera_gpu_tensor=",camera_gpu_tensor.float()) + # #print(camera_gpu_tensor.shape) + # self.obs_buf[id, :, :, 0:self.camera_channels] = camera_gpu_tensor.float() + if use_cv2: + if id == 13: + cv2.imshow("image", self.obs_buf[id, :, :, 0:3].cpu().numpy() ) + #cv2.imshow("image", self.obs_buf[id, :, :, 0:3].cpu().numpy()) + cv2.waitKey(1000) + + else: + self.obs_buf[env_ids, 0] = self.dof_pos[env_ids, 0].squeeze() + self.obs_buf[env_ids, 1] = self.dof_vel[env_ids, 0].squeeze() + self.obs_buf[env_ids, 2] = self.dof_pos[env_ids, 1].squeeze() + self.obs_buf[env_ids, 3] = self.dof_vel[env_ids, 1].squeeze() + + return self.obs_buf + + +if __name__ == '__main__': + cam = CartpoleTest(num_envs = 4, enable_tiled = True, use_matplot_lib = True) + + for f in range (10): + + #cam.sync_transforms_cpu(rb) + cam.update_observations(write_transforms=True) + ftensor = cam.ttensor.type(torch.float32) + np_img_arr = ftensor.cpu().numpy() + np_img_arr = np.reshape(np_img_arr, (cam.height, cam.width, 4)) + np_img_arr = np.flipud(np_img_arr) + frame_img = np_img_arr[:,:,:3] + frame_img = np.array(frame_img * 255, dtype=np.uint8) + frames.append(frame_img) + + write_apng("tiled_image.png", frames, delay=100) diff --git a/python/examples/tiled_vec_laikago_render.py b/python/examples/tiled_vec_laikago_render.py new file mode 100644 index 00000000..3c5bb0e3 --- /dev/null +++ b/python/examples/tiled_vec_laikago_render.py @@ -0,0 +1,183 @@ +import pytinydiffsim as tds +import time +import sys +import numpy +numpy.set_printoptions(threshold=sys.maxsize) +import numpy as np +import matplotlib.pyplot as plt +plt.ion() +img = np.random.rand(400, 400) +image = plt.imshow(img, interpolation='none')#, cmap='gray', vmin=0.8, vmax=1) +ax = plt.gca() + +width = 1024 +height = 768 + +if 1: + num_actors = 2 #1024#4096 + auto_reset_when_done = True + tds_robot = tds.VectorizedLaikagoEnv(num_actors, auto_reset_when_done) + tds_robot.reset() + actions = [[0] * tds_robot.action_dim()]*num_actors + res = tds_robot.step(actions) + + + import pytinyopengl3 as g + viz = g.OpenGLUrdfVisualizer(width=width, height=height) + viz.opengl_app.set_background_color(1,0,0) + viz.opengl_app.swap_buffer() + viz.opengl_app.swap_buffer() + + urdf = g.OpenGLUrdfStructures() + parser = g.UrdfParser() + file_name = tds_robot.urdf_filename() + urdf = parser.load_urdf(file_name) + print("urdf=",urdf) + texture_path = "laikago_tex.jpg" + viz.path_prefix = g.extract_path(file_name) + print("viz.path_prefix=",viz.path_prefix) + viz.convert_visuals(urdf, texture_path) + print("create_instances") + + all_instances_prev = viz.create_instances(urdf, texture_path, num_actors) + all_instances = viz.create_instances(urdf, texture_path, num_actors) + + #print("all_instances=",all_instances) + #print("all_instances[0]=",all_instances[0]) + + for i in all_instances[1]: + print(i.visual_instance) + + #sync transforms + #for pairs in all_instances: + # for pair in pairs: + # print("pair.link=", pair.link_index, " pair.visual_instance=", pair.visual_instance) + sim_spacing = 0 + + + + + print("len(all_instances)=",len(all_instances)) + print("\nhold CTRL and right/left/middle mouse button to rotate/zoom/move camera") + + + st = time.time() + + if 1: + width = viz.opengl_app.renderer.get_screen_width() + print("screen_width=",width) + height = viz.opengl_app.renderer.get_screen_height() + print("screen_height=",height) + + nx=40 + ny=40 + tile_width = int(width/nx) + tile_height = int(height/ny) + + tiles=[] + + + for x in range (nx): + for y in range (ny): + tile = g.TinyViewportTile() + tile.visual_instances = [35,37,39,41,43,45,47,49,51,53,55,57,59,61,63,65,67] + #tile.visual_instances = [35,37,39,41]^M + cam = viz.opengl_app.renderer.get_active_camera() + tile.projection_matrix = cam.get_camera_projection_matrix() + tile.view_matrix = cam.get_camera_view_matrix() + tile.viewport_dims=[x*tile_width,y*tile_height,tile_width, tile_height] + tiles.append(tile) + + + cam = g.TinyCamera() + cam.set_camera_up_axis(2) + cam.set_camera_distance(1) + cam.set_camera_pitch(-30) + cam.set_camera_target_position(0.,0.,0.) + viz.opengl_app.renderer.set_camera(cam) + + + + frame = 0 + #for frame in range (100): + while not viz.opengl_app.window.requested_exit(): + frame=frame+1 + + width = viz.opengl_app.renderer.get_screen_width() + height = viz.opengl_app.renderer.get_screen_height() + tile_width = int(width/nx) + tile_height = int(height/ny) + + ct = time.time() + + cam = viz.opengl_app.renderer.get_active_camera() + tile_index = 0 + for x in range (nx): + for y in range (ny): + tile = tiles[tile_index] + tile_index+=1 + tile.view_matrix = cam.get_camera_view_matrix() + tile.viewport_dims=[x*tile_width,y*tile_height,tile_width, tile_height] + + + et = time.time() + print("update viewports dt=",et-ct) + + + ct = time.time() + res = tds_robot.step(actions) + et = time.time() + print("tds_robot.step dt=",et-ct) + + #viz.sync_visual_transforms(all_instances, res.visual_world_transforms, tds_robot.obs_dim(), sim_spacing) + + #viz.render(do_swap_buffer=False, render_segmentation_mask=True) + #name = "test_"+str(frame)+".png" + #viz.opengl_app.dump_next_frame_to_png(filename=name, render_to_texture=False, width=19200, height=10800) + ct = time.time() + viz.render_tiled(tiles, do_swap_buffer = False, render_segmentation_mask=False) + et = time.time() + print("render dt=",et-ct) + + #viz.render() + + ct = time.time() + pixels = g.ReadPixelBuffer(viz.opengl_app) + et = time.time() + print("ReadPixelBuffer dt=",et-ct) + #print('pixels.rgba=', pixels.rgba) + + #np_img_arr = np.reshape(pixels.rgba, (height, width, 4)) + #np_img_arr = np_img_arr * (1. / 255.) + #np_img_arr = np.flipud(np_img_arr) + + #image.set_data(np_img_arr) + + #np_depth_arr = np.flipud(np.reshape(pixels.depth, (height, width, 1))) + #image.set_data(np_depth_arr) + ct = time.time() + viz.swap_buffer() + et = time.time() + print("swap_buffer dt=",et-ct) + et = time.time() + dt = et-st + st = et + + #image.set_data(np_img_arr) + ax.plot([0]) + #plt.draw() + #plt.show() + #plt.pause(0.0001) + + print(dt) + print("fps = ", len(tiles)*(1./dt)) + + + + #cam = viz.opengl_app.renderer.get_active_camera() + #print("cam=",cam) + #print("cam.get_camera_projection_matrix=",cam.get_camera_projection_matrix()) + #print("cam.get_camera_view_matrix=",cam.get_camera_view_matrix()) + + + diff --git a/python/examples/warp_gl_interop.py b/python/examples/warp_gl_interop.py new file mode 100644 index 00000000..fe46d2d1 --- /dev/null +++ b/python/examples/warp_gl_interop.py @@ -0,0 +1,231 @@ +import pytinyopengl3 as p +import math, time +import warp as wp +import numpy as np + +import nvtx + +wp.init() + +device = "cuda" #wp.get_preferred_device() + +num_objects = 1000000 +#num_objects = 1000 +#num_objects = 10 + +app = p.TinyOpenGL3App("warp_gl_interop", maxNumObjectCapacity=num_objects+10) +app.renderer.init() + + + + +cam = p.TinyCamera() +cam.set_camera_distance(2.) +cam.set_camera_pitch(-20) +app.renderer.set_camera(cam) +width = 256 +height = 256 +pixels = [255] * width * height * 3 +colorR = 255 +colorG = 255 +colorB = 255 + +for i in range(width): + for j in range(height): + a = i < width / 2 + b = j < width / 2 + if (a == b): + pixels[(i + j * width) * 3 + 0] = 0 + pixels[(i + j * width) * 3 + 1] = 255 + pixels[(i + j * width) * 3 + 2] = 255 + else: + pixels[(i + j * width) * 3 + 0] = colorR + pixels[(i + j * width) * 3 + 1] = colorG + pixels[(i + j * width) * 3 + 2] = colorB + + +textureIndex = app.renderer.register_texture(pixels, width, height, False) +pos = p.TinyVector3f(0.,0.,1.) +orn = p.TinyQuaternionf(0.,0.,0.,1.) +color = p.TinyVector3f(1.,1.,1.) +scaling = p.TinyVector3f(0.1,0.1,0.1) +opacity = 1 +rebuild = True +#shape = app.register_graphics_unit_sphere_shape(p.EnumSphereLevelOfDetail.SPHERE_LOD_HIGH, textureIndex) +shape = app.register_cube_shape(1,1,1, textureIndex,4) + + +@wp.kernel +def init_vec(a: wp.array(dtype=wp.vec4)): + tid = wp.tid() + a[tid] = wp.vec4(0.,0.,float(tid)*1.5, 1.0) + + + +vec_pos = [p.TinyVector3f(0.,0.,0.)]*num_objects +vec_orn = [p.TinyQuaternionf(0.,0.,0.,1.)]*num_objects +vec_scaling = [p.TinyVector3f(1.,1.,1.)]*num_objects +vec_color = [p.TinyVector3f(1.,1.,1.)]*num_objects +vec_scaling = [p.TinyVector3f(0.1,0.1,0.1)]*num_objects + +sphere = app.renderer.register_graphics_instances(shape, vec_pos, vec_orn, vec_color, vec_scaling, opacity, rebuild) + + +shape = app.register_cube_shape(1, 1, 0.01, textureIndex, 40) +#print("shape=",shape) + + +ground_plane=True +if ground_plane: + pos = p.TinyVector3f(0.,0.,0.) + scaling = p.TinyVector3f(1.,1.,1.) + app.renderer.register_graphics_instance(shape, pos, orn, color, scaling, opacity, rebuild) + + +app.renderer.write_transforms() + + + + + + + +velocities = wp.array( + np.array([0.0, 0.0, 0.1, 0.0]*num_objects), + device=device, + dtype=wp.vec4, + requires_grad=False, + ) + +@wp.kernel +def init_kernel(a: wp.array(dtype=wp.vec4), sim_spacing: float, square_id: int): + tid = wp.tid() + a[tid] = wp.vec4(sim_spacing * float(tid % square_id) - float(square_id) * sim_spacing / 2., + sim_spacing * float(tid / square_id) - float(square_id) * sim_spacing / 2., + 0.,1.) + + + + +vbo = app.cuda_map_vbo() +sim_spacing = 0.3 +positions = wp.array(ptr=vbo.positions,dtype=wp.vec4, shape=(num_objects,), length=num_objects,capacity=num_objects,device=device, owner=False, ndim=1) +wp.launch(init_kernel, device=device, dim=num_objects, inputs=[positions, sim_spacing, int(math.sqrt(num_objects))]) +app.cuda_unmap_vbo() + + +# Example reading/writing OpenGL vertices (in VBO) using CUDA interop +if 1: + print("shape=",shape) + vcnt = app.renderer.get_shape_vertex_count() + total_vertices = 0 + for vc in vcnt: + total_vertices += vc + + print("vcnt2 =",vcnt ) + #vertex offset into VBO for each shape + voffsets = app.renderer.get_shape_vertex_offsets() + print("voffsets2=",voffsets) + + vbo = app.cuda_map_vbo() + + num_vertices = vcnt[shape] + print("num_vertices=",num_vertices) + offset = voffsets[shape] + vertex_stride = 9*4 #9 floats [x,y,z,w, nx,ny,nz, u,v] + print("total_vertices=",total_vertices) + + a = np.array([-1.0, -1.0, -1.0, -1.0]*num_vertices) + + vertices_dest = wp.array( + a, + dtype=wp.vec4, + device=device, + requires_grad=False, + ) + + vertices_src = wp.array(ptr=vbo.vertices,dtype=wp.vec4, shape=(total_vertices,), strides=(vertex_stride,), length=total_vertices,capacity=total_vertices,device=device, owner=False, ndim=1) + + @wp.kernel + def modify_vertices_kernel(src: wp.array(dtype=wp.vec4), dst: wp.array(dtype=wp.vec4), offset: wp.int32, x_y_dim: float): + tid = wp.tid() + v = src[tid+offset] + src[tid+offset] = wp.vec4(x_y_dim*v[0],x_y_dim*v[1],-0.01,v[3]) + dst[tid] = src[tid+offset] + + x_y_dim = sim_spacing*math.sqrt(num_objects)/2. + wp.launch(modify_vertices_kernel, device=device, dim=num_vertices, inputs=[vertices_src, vertices_dest, offset, x_y_dim]) + + app.cuda_unmap_vbo() + + #debug values + print("vertices_dest=",vertices_dest) + + +@wp.kernel +def test_kernel(a: wp.array(dtype=wp.vec4), + b: wp.array(dtype=wp.vec4), + dt: float): + + tid = wp.tid() + a[tid] = a[tid] + b[tid]*dt + + +vcnt = app.renderer.get_shape_vertex_count() +print("vcnt =",vcnt ) +app.renderer.update_camera(2) +dg = p.DrawGridData() +dg.drawAxis = True +app.draw_grid(dg) + +#stop_app = False + +def my_mouse_move_callback(a,b): + print("mouse move:",a,b) + +app.window.set_mouse_move_callback(my_mouse_move_callback) + +def my_mouse_button_callback(a,b,c,d): + print("mouse button:",a,b,c,d) + +app.window.set_mouse_button_callback(my_mouse_button_callback) + +def my_resize_callback(a,b): + print("resize:",a,b) +app.window.set_resize_callback(my_resize_callback) + +def my_wheel_callback(a,b): + print("wheel:",a,b) +app.window.set_wheel_callback(my_wheel_callback) + +def my_keyboard_callback(a,b): + print("key:", a,b) + if a==27: + print("requesting exit!") + app.window.set_request_exit() +app.window.set_keyboard_callback(my_keyboard_callback) + +while not app.window.requested_exit(): + + with nvtx.annotate("sync_visual_transforms", color="orange"): + vbo = app.cuda_map_vbo() + positions = wp.array(ptr=vbo.positions,dtype=wp.vec4, shape=(num_objects,), length=num_objects,capacity=num_objects,device=device, owner=False, ndim=1) + wp.launch(test_kernel, device=device, dim=num_objects, inputs=[positions, velocities, 1./240.]) + app.cuda_unmap_vbo() + + + if 0: + from_line=p.TinyVector3f(0,0,0) + to_line=p.TinyVector3f(1,1,1) + color=p.TinyVector3f(1,0,0) + width=2 + app.renderer.draw_line(from_line,to_line,color,width) + + with nvtx.annotate("render_scene", color="blue"): + app.renderer.render_scene() + + #app.draw_text_3d("hi",1,1,1,1) + with nvtx.annotate("swap_buffer", color="green"): + app.swap_buffer() + + diff --git a/python/pytinyopengl3.cc b/python/pytinyopengl3.cc index f2e01c4f..53cff0c8 100644 --- a/python/pytinyopengl3.cc +++ b/python/pytinyopengl3.cc @@ -19,7 +19,10 @@ #include #include +#include #include +#include + #include "math/tiny/tiny_float_utils.h" @@ -35,10 +38,27 @@ #include "math/tiny/tiny_algebra.hpp" #include "stb_image/stb_image.h" +//to expose OpenGL constants +#include "visualizer/opengl/tiny_opengl_include.h" + using namespace TINY; typedef ::TINY::FloatUtils MyTinyConstants; typedef TinyAlgebra MyAlgebra; + +extern std::string DYNAMIC_CUDA_PATH; +extern std::string DYNAMIC_CUDART_PATH; + +void set_cuda_path(const std::string& cuda_path) +{ + DYNAMIC_CUDA_PATH = cuda_path; +} +void set_cudart_path(const std::string& cudart_path) +{ + DYNAMIC_CUDART_PATH = cudart_path; +} + + std::string file_open_dialog(TinyWindowInterface* window) { std::string file_name=""; @@ -53,6 +73,121 @@ std::string file_open_dialog(TinyWindowInterface* window) return file_name; } +extern std::string triangleVertexShaderTextInit; +extern std::string triangleFragmentShaderInit; +extern std::string useShadowMapInstancingVertexShaderInit; +extern std::string useShadowMapInstancingFragmentShaderInit; +extern std::string createShadowMapInstancingVertexShaderInit; +extern std::string createShadowMapInstancingFragmentShaderInit; +extern std::string segmentationMaskInstancingVertexShaderInit; +extern std::string segmentationMaskInstancingFragmentShaderInit; +extern std::string instancingVertexShaderInit; +extern std::string instancingFragmentShaderInit; + +std::string get_triangle_vertex_shader() +{ + return triangleVertexShaderTextInit; +} + +void set_triangle_vertex_shader(const std::string& vertex_shader) +{ + triangleVertexShaderTextInit = vertex_shader; +} + +std::string get_triangle_fragment_shader() +{ + return triangleFragmentShaderInit; +} + +void set_triangle_fragment_shader(const std::string& fragment_shader) +{ + triangleFragmentShaderInit = fragment_shader; +} + +std::string get_use_shadowmap_instancing_vertex_shader() +{ + return useShadowMapInstancingVertexShaderInit; +} + +void set_use_shadowmap_instancing_vertex_shader(const std::string& vertex_shader) +{ + useShadowMapInstancingVertexShaderInit = vertex_shader; +} + +std::string get_create_shadowmap_instancing_vertex_shader() +{ + return createShadowMapInstancingVertexShaderInit; +} + +void set_create_shadowmap_instancing_vertex_shader(const std::string& vertex_shader) +{ + createShadowMapInstancingVertexShaderInit = vertex_shader; +} + +std::string get_use_shadowmap_instancing_fragment_shader() +{ + return useShadowMapInstancingFragmentShaderInit; +} + +void set_use_shadowmap_instancing_fragment_shader(const std::string& fragment_shader) +{ + useShadowMapInstancingFragmentShaderInit = fragment_shader; +} + +std::string get_create_shadowmap_instancing_fragment_shader() +{ + return createShadowMapInstancingFragmentShaderInit; +} + +void set_create_shadowmap_instancing_fragment_shader(const std::string& fragment_shader) +{ + createShadowMapInstancingFragmentShaderInit = fragment_shader; +} + + +std::string get_segmentation_mask_instancing_vertex_shader() +{ + return segmentationMaskInstancingVertexShaderInit; +} + +void set_segmentation_mask_instancing_vertex_shader(const std::string& vertex_shader) +{ + segmentationMaskInstancingVertexShaderInit = vertex_shader; +} + +std::string get_segmentation_mask_instancing_fragment_shader() +{ + return segmentationMaskInstancingFragmentShaderInit; +} + +void set_segmentation_mask_instancing_fragment_shader(const std::string& fragment_shader) +{ + segmentationMaskInstancingFragmentShaderInit = fragment_shader; +} + + +std::string get_instancing_vertex_shader() +{ + return instancingVertexShaderInit; +} + +void set_instancing_vertex_shader(const std::string& vertex_shader) +{ + instancingVertexShaderInit = vertex_shader; +} + +std::string get_instancing_fragment_shader() +{ + return instancingFragmentShaderInit; +} + +void set_instancing_fragment_shader(const std::string& fragment_shader) +{ + instancingFragmentShaderInit = fragment_shader; +} + + + std::string my_extract_path(const std::string& file_name) { char full_path[TINY_MAX_EXE_PATH_LEN]; tds::FileUtils::extract_path(file_name.c_str(), full_path, @@ -60,6 +195,68 @@ std::string my_extract_path(const std::string& file_name) { return full_path; } +std::array compute_camera_view_matrix(const ::TINY::TinyVector3f& cam_pos, const ::TINY::TinyVector3f& cam_target, const ::TINY::TinyVector3f& cam_up) +{ + const auto& eye = cam_pos; + const auto& center = cam_target; + const auto& up = cam_up; + auto f = (center - eye).normalized(); + auto u = up.normalized(); + auto s = (f.cross(u)).normalized(); + u = s.cross(f); + + std::array viewMatrix; + + viewMatrix[0 * 4 + 0] = s.x(); + viewMatrix[1 * 4 + 0] = s.y(); + viewMatrix[2 * 4 + 0] = s.z(); + + viewMatrix[0 * 4 + 1] = u.x(); + viewMatrix[1 * 4 + 1] = u.y(); + viewMatrix[2 * 4 + 1] = u.z(); + + viewMatrix[0 * 4 + 2] = -f.x(); + viewMatrix[1 * 4 + 2] = -f.y(); + viewMatrix[2 * 4 + 2] = -f.z(); + + viewMatrix[0 * 4 + 3] = 0.f; + viewMatrix[1 * 4 + 3] = 0.f; + viewMatrix[2 * 4 + 3] = 0.f; + + viewMatrix[3 * 4 + 0] = -s.dot(eye); + viewMatrix[3 * 4 + 1] = -u.dot(eye); + viewMatrix[3 * 4 + 2] = f.dot(eye); + viewMatrix[3 * 4 + 3] = 1.f; + return viewMatrix; +} + + +std::array compute_camera_projection_matrix(float left, float right, float bottom, float top, float nearVal, float farVal) +{ + std::array projectionMatrix; + + projectionMatrix[0 * 4 + 0] = (float(2) * nearVal) / (right - left); + projectionMatrix[0 * 4 + 1] = float(0); + projectionMatrix[0 * 4 + 2] = float(0); + projectionMatrix[0 * 4 + 3] = float(0); + + projectionMatrix[1 * 4 + 0] = float(0); + projectionMatrix[1 * 4 + 1] = (float(2) * nearVal) / (top - bottom); + projectionMatrix[1 * 4 + 2] = float(0); + projectionMatrix[1 * 4 + 3] = float(0); + + projectionMatrix[2 * 4 + 0] = (right + left) / (right - left); + projectionMatrix[2 * 4 + 1] = (top + bottom) / (top - bottom); + projectionMatrix[2 * 4 + 2] = -(farVal + nearVal) / (farVal - nearVal); + projectionMatrix[2 * 4 + 3] = float(-1); + + projectionMatrix[3 * 4 + 0] = float(0); + projectionMatrix[3 * 4 + 1] = float(0); + projectionMatrix[3 * 4 + 2] = -(float(2) * farVal * nearVal) / (farVal - nearVal); + projectionMatrix[3 * 4 + 3] = float(0); + return projectionMatrix; +} + std::vector my_load_obj_shapes(TinyOpenGL3App& opengl_app, const std::string& obj_filename, const ::TINY::TinyVector3f& pos, const ::TINY::TinyQuaternionf& orn, const ::TINY::TinyVector3f& scaling) { @@ -115,6 +312,37 @@ std::vector my_load_obj_shapes(TinyOpenGL3App& opengl_app, const std::strin } +struct ReadPixelBuffer +{ + + pybind11::capsule buffer_handle; + pybind11::array_t rgba; + pybind11::array_t depth; + + std::vector rgba_data; + std::vector depth_data; + + ReadPixelBuffer(TinyOpenGL3App& gl_app) + :buffer_handle(pybind11::capsule ([](){})) + { + gl_app.get_screen_pixels(rgba_data,depth_data); + + unsigned char* rgba_ptr = rgba_data.size()? &rgba_data[0] : 0; + rgba = pybind11::array_t(rgba_data.size(), rgba_ptr, + buffer_handle); + + float* depth_ptr = depth_data.size() ? &depth_data[0] : 0; + depth = + pybind11::array_t(depth_data.size(), depth_ptr, buffer_handle); + } + + virtual ~ReadPixelBuffer() + { + + } + + +}; namespace py = pybind11; @@ -137,7 +365,27 @@ PYBIND11_MODULE(pytinyopengl3, m) { .def_readwrite("drawAxis", &DrawGridData::drawAxis) .def_readwrite("upOffset", &DrawGridData::upOffset) .def_readwrite("gridSize", &DrawGridData::gridSize); - + + py::class_< TinyCudaVbo>(m,"TinyCudaVbo") + .def_readwrite("num_instances",&TinyCudaVbo::num_instances) + .def_readwrite("positions",&TinyCudaVbo::positions_int) + .def_readwrite("orientations",&TinyCudaVbo::orientations_int) + .def_readwrite("colors",&TinyCudaVbo::colors_int) + .def_readwrite("scalings",&TinyCudaVbo::scalings_int) + .def_readwrite("vertices",&TinyCudaVbo::vertices_int) + ; + + py::class_< GfxVertexFormat1>(m,"GfxVertexFormat") + .def_readwrite("x",&GfxVertexFormat1::x) + .def_readwrite("y",&GfxVertexFormat1::y) + .def_readwrite("z",&GfxVertexFormat1::z) + .def_readwrite("w",&GfxVertexFormat1::w) + .def_readwrite("nx",&GfxVertexFormat1::nx) + .def_readwrite("ny",&GfxVertexFormat1::ny) + .def_readwrite("nz",&GfxVertexFormat1::nz) + .def_readwrite("u",&GfxVertexFormat1::u) + .def_readwrite("v",&GfxVertexFormat1::v) + ; py::class_(m,"TinyOpenGL3App") .def(py::init(), @@ -150,6 +398,44 @@ PYBIND11_MODULE(pytinyopengl3, m) { py::arg("maxNumObjectCapacity")=256 * 1024, py::arg("maxShapeCapacityInBytes")= 256 * 1024 * 1024) .def("swap_buffer", &TinyOpenGL3App::swap_buffer) + .def("dump_next_frame_to_png", &TinyOpenGL3App::dump_next_frame_to_png, + py::arg("filename") = "image.png", + py::arg("render_to_texture") = 1, + py::arg("width") = -1, + py::arg("height") = -1 + ) + .def("enable_render_to_texture", + &TinyOpenGL3App::enable_render_to_texture) + + .def("cuda_register_texture_image", + &TinyOpenGL3App::cuda_register_texture_image, + py::arg("gl_texture_int"), + py::arg("verbose")=true + ) + + + + .def("set_up_axis", &TinyOpenGL3App::set_up_axis) + .def("cuda_copy_texture_image", &TinyOpenGL3App::cuda_copy_texture_image, + py::arg("cuda_resource_int"), + py::arg("dest_memory_int"), + py::arg("num_bytes"), + py::arg("gpu_device_destination") = true, + py::arg("w_offset") = 0, + py::arg("h_offset") = 0) + + .def("cuda_malloc", &TinyOpenGL3App::cuda_malloc, + py::arg("num_bytes")) + + .def("cuda_free", &TinyOpenGL3App::cuda_free, + py::arg("cuda_ptr")) + + .def("cuda_map_vbo", &TinyOpenGL3App::cuda_map_vbo , + py::arg("verbose")=false) + .def("cuda_unmap_vbo", &TinyOpenGL3App::cuda_unmap_vbo ) + + .def("set_background_color", &TinyOpenGL3App::set_background_color) + .def("dump_frames_to_video", &TinyOpenGL3App::dump_frames_to_video) .def("register_cube_shape", &TinyOpenGL3App::register_cube_shape) .def("register_graphics_unit_sphere_shape", &TinyOpenGL3App::register_graphics_unit_sphere_shape) .def("register_graphics_capsule_shape", &TinyOpenGL3App::register_graphics_capsule_shape) @@ -162,6 +448,39 @@ PYBIND11_MODULE(pytinyopengl3, m) { ; + py::class_(m, "ReadPixelBuffer") + .def(py::init()) + .def_readonly("rgba", &ReadPixelBuffer::rgba, R"pbdoc( rgba pixel data)pbdoc") + .def_readonly("depth", &ReadPixelBuffer::depth, + R"pbdoc( depth pixel data)pbdoc") + + ; + + + py::class_(m, "TinyViewportTile") + .def(py::init<>()) + .def_readwrite("visual_instances", + &TinyViewportTile::visual_instances) + .def_readwrite("projection_matrix", + &TinyViewportTile::projection_matrix) + .def_readwrite("view_matrix", + &TinyViewportTile::view_matrix) + .def_readwrite("viewport_dims", + &TinyViewportTile::viewport_dims) + + ; + + + + py::class_(m, "UrdfInstancePair") + .def(py::init<>()) + .def_readwrite("link_index", &UrdfInstancePair::m_link_index) + .def_readwrite("visual_instance", &UrdfInstancePair::m_visual_instance) + .def_readwrite("viz_origin_xyz", &UrdfInstancePair::viz_origin_xyz) + .def_readwrite("viz_origin_rpy", &UrdfInstancePair::viz_origin_rpy); + + + py::class_(m, "TinyCamera") .def(py::init<>()) .def("update", &TinyCamera::update) @@ -171,31 +490,66 @@ PYBIND11_MODULE(pytinyopengl3, m) { .def("set_camera_up_vector",&TinyCamera::set_camera_up_vector) .def("set_camera_up_axis", &TinyCamera::set_camera_up_axis) .def("set_camera_target_position", &TinyCamera::set_camera_target_position) + .def("get_camera_projection_matrix", &TinyCamera::get_camera_projection_matrix2) + .def("get_camera_view_matrix", &TinyCamera::get_camera_view_matrix2) ; py::class_(m, "TinyGLInstancingRenderer") .def("init", &TinyGLInstancingRenderer::init) - .def("update_camera", &TinyGLInstancingRenderer::update_camera) + .def("get_active_camera", &TinyGLInstancingRenderer::get_active_camera2) + + .def_property("view_matrix", + [](const TinyGLInstancingRenderer& self) { + std::array viewMatrix; + self.get_view_matrix(viewMatrix.data()); + return viewMatrix; + }, + [](TinyGLInstancingRenderer& self, const std::array& viewMatrix) { + self.set_view_matrix(viewMatrix.data()); + }) + .def_property("projection_matrix", + [](const TinyGLInstancingRenderer& self) { + std::array projectionMatrix; + self.get_projection_matrix(projectionMatrix.data()); + return projectionMatrix; + }, + [](TinyGLInstancingRenderer& self, const std::array& projectionMatrix) { + self.set_projection_matrix(projectionMatrix.data()); + }) - .def("register_shape", &TinyGLInstancingRenderer::register_shape1) + .def("register_shape", &TinyGLInstancingRenderer::register_shape1, + py::arg("vertices"), py::arg("indices"), py::arg("textureIndex")=-1, py::arg("double_sided")=true) + + //.def("update_shape", &TinyGLInstancingRenderer::update_shape) + .def("get_shape_vertex_count", &TinyGLInstancingRenderer::get_shape_vertex_count) + .def("get_shape_vertex_offsets", &TinyGLInstancingRenderer::get_shape_vertex_offsets) + + .def("register_texture", &TinyGLInstancingRenderer::register_texture1) //.def("update_texture", &TinyGLInstancingRenderer::update_texture) .def("remove_texture", &TinyGLInstancingRenderer::remove_texture) .def("register_graphics_instance", &TinyGLInstancingRenderer::register_graphics_instance) + .def("register_graphics_instances", &TinyGLInstancingRenderer::register_graphics_instances) + .def("write_single_instance_transform_to_cpu", &TinyGLInstancingRenderer::write_single_instance_transform_to_cpu) .def("write_single_instance_color_to_cpu", &TinyGLInstancingRenderer::write_single_instance_color_to_cpu2) + .def("write_single_instance_flags_to_cpu", &TinyGLInstancingRenderer::write_single_instance_flags_to_cpu) .def("render_scene", &TinyGLInstancingRenderer::render_scene) + .def("render_scene2", &TinyGLInstancingRenderer::render_scene2) + + .def("render_scene_tiled", &TinyGLInstancingRenderer::render_scene_internal) + .def("write_transforms", &TinyGLInstancingRenderer::write_transforms) .def("remove_all_instances", &TinyGLInstancingRenderer::remove_all_instances) .def("remove_graphics_instance", &TinyGLInstancingRenderer::remove_graphics_instance) //.def("get_active_camera", (TinyCamera* (TinyGLInstancingRenderer::*)()) &TinyGLInstancingRenderer::get_active_camera) - .def("set_camera", &TinyGLInstancingRenderer::set_active_camera) + .def("set_camera", &TinyGLInstancingRenderer::set_camera) .def("draw_line", &TinyGLInstancingRenderer::draw_line) .def("draw_lines", &TinyGLInstancingRenderer::draw_lines) @@ -207,31 +561,68 @@ PYBIND11_MODULE(pytinyopengl3, m) { py::class_(m, "TinyWindowInterface") .def("requested_exit", &TinyWindowInterface::requested_exit) - .def("set_request_exit", &TinyWindowInterface::requested_exit) - //.def("set_mouse_button_callback", &TinyWindowInterface::set_mouse_button_callback) + .def("set_request_exit", &TinyWindowInterface::set_request_exit2) + .def("set_window_title", &TinyWindowInterface::set_window_title) + .def("set_keyboard_callback", &TinyWindowInterface::set_keyboard_callback2) + .def("set_mouse_move_callback", &TinyWindowInterface::set_mouse_move_callback2) + .def("set_mouse_button_callback", &TinyWindowInterface::set_mouse_button_callback2) + .def("set_wheel_callback", &TinyWindowInterface::set_wheel_callback2) + .def("set_resize_callback", &TinyWindowInterface::set_resize_callback2) ; m.def("file_open_dialog", &file_open_dialog); - + m.def("set_cuda_path", &set_cuda_path); + m.def("set_cudart_path", &set_cudart_path); + m.def("load_obj_shapes", &my_load_obj_shapes); m.def("extract_path", &my_extract_path); + m.def("compute_camera_view_matrix", &compute_camera_view_matrix); + m.def("compute_camera_projection_matrix", &compute_camera_projection_matrix); + + m.def("get_triangle_vertex_shader", &get_triangle_vertex_shader); + m.def("set_triangle_vertex_shader", &set_triangle_vertex_shader); + + m.def("get_triangle_fragment_shader", &get_triangle_fragment_shader); + m.def("set_triangle_fragment_shader", &set_triangle_fragment_shader); + + m.def("get_use_shadowmap_instancing_vertex_shader",&get_use_shadowmap_instancing_vertex_shader); + m.def("set_use_shadowmap_instancing_vertex_shader",&set_use_shadowmap_instancing_vertex_shader); + + m.def("get_create_shadowmap_instancing_vertex_shader",&get_create_shadowmap_instancing_vertex_shader); + m.def("set_create_shadowmap_instancing_vertex_shader",&set_create_shadowmap_instancing_vertex_shader); + + m.def("get_use_shadowmap_instancing_fragment_shader",&get_use_shadowmap_instancing_fragment_shader); + m.def("set_use_shadowmap_instancing_fragment_shader",&set_use_shadowmap_instancing_fragment_shader); + m.def("get_create_shadowmap_instancing_fragment_shader",&get_create_shadowmap_instancing_fragment_shader); + m.def("set_create_shadowmap_instancing_fragment_shader",&set_create_shadowmap_instancing_fragment_shader); + + m.def("get_segmentation_mask_instancing_vertex_shader",&get_segmentation_mask_instancing_vertex_shader); + m.def("set_segmentation_mask_instancing_vertex_shader",&set_segmentation_mask_instancing_vertex_shader); + + m.def("get_segmentation_mask_instancing_fragment_shader",&get_segmentation_mask_instancing_fragment_shader); + m.def("get_segmentation_mask_instancing_fragment_shader",&get_segmentation_mask_instancing_fragment_shader); + + m.def("get_instancing_vertex_shader", &get_instancing_vertex_shader); + m.def("set_instancing_vertex_shader", &set_instancing_vertex_shader); + + m.def("get_instancing_fragment_shader", &get_instancing_fragment_shader); + m.def("set_instancing_fragment_shader", &set_instancing_fragment_shader); + + py::class_<::tds::UrdfParser>(m, "UrdfParser") .def(py::init<>()) - .def("load_urdf", &::tds::UrdfParser::load_urdf) + .def("load_urdf", &::tds::UrdfParser::load_urdf, + py::arg("file_name"), + py::arg("verbose")=false + ) .def("load_urdf_from_string", &::tds::UrdfParser::load_urdf_from_string) ; - - py::class_(m, "UrdfInstancePair") - .def(py::init<>()) - .def_readwrite("link_index", - &UrdfInstancePair::m_link_index) - .def_readwrite("visual_instance", &UrdfInstancePair::m_visual_instance); py::class_<::tds::UrdfStructures>(m,"OpenGLUrdfStructures") .def(py::init<>()) @@ -240,21 +631,55 @@ PYBIND11_MODULE(pytinyopengl3, m) { py::class_>(m, "OpenGLUrdfVisualizer") - .def(py::init<>()) + .def(py::init(), + py::arg("width")=1024, + py::arg("height")=768, + py::arg("title")="pytinyopengl3", + py::arg("allow_retina")=1, + py::arg("window_type")=0, + py::arg("render_device")=-1, + py::arg("max_num_object_capacity")=256 * 1024, + py::arg("max_shape_capacity_in_bytes")= 128 * 1024 * 1024) + .def("convert_visuals", &OpenGLUrdfVisualizer::convert_visuals2) - .def("render", &OpenGLUrdfVisualizer::render) + .def("render", &OpenGLUrdfVisualizer::render, + py::arg("do_swap_buffer")=true, + py::arg("render_segmentation_mask")=false, + py::arg("up_axis")=2, + py::arg("write_transforms")=true + ) + .def("render_tiled", &OpenGLUrdfVisualizer::render_tiled, + py::arg("tiles"), + py::arg("do_swap_buffer")=true, + py::arg("render_segmentation_mask")=false, + py::arg("up_axis")=2, + py::arg("write_transforms")=true + ) + .def("swap_buffer", &OpenGLUrdfVisualizer::swap_buffer) + .def("create_instances", &OpenGLUrdfVisualizer::create_instances) .def("sync_visual_transforms", - &OpenGLUrdfVisualizer::sync_visual_transforms2) + &OpenGLUrdfVisualizer::sync_visual_transforms2, + py::arg("all_instances"), py::arg("visual_world_transforms_array"), + py::arg("visual_offset"), py::arg("sim_spacing"), + py::arg("apply_visual_offset")=false, + py::arg("link_mapping")=std::vector() + ) .def_readwrite("opengl_app", &OpenGLUrdfVisualizer::m_opengl_app) .def_readwrite("path_prefix", &OpenGLUrdfVisualizer::m_path_prefix) ; + PyModule_AddIntConstant(m.ptr(), "GL_TEXTURE_2D", GL_TEXTURE_2D); + PyModule_AddIntConstant(m.ptr(), "SEGMENTATION_MASK_RENDERMODE", B3_SEGMENTATION_MASK_RENDERMODE); + PyModule_AddIntConstant(m.ptr(), "DEFAULT_RENDERMODE", B3_DEFAULT_RENDERMODE); + + py::class_>(m, "TinyPosef") .def(py::init<>()) + .def(py::init&,const TinyQuaternion&>()) .def(py::self * py::self) .def_readwrite("position", &TinyPose::m_position) .def_readwrite("orientation", &TinyPose::m_orientation) @@ -266,6 +691,7 @@ PYBIND11_MODULE(pytinyopengl3, m) { .def_readwrite("x", &TinyVector3::m_x) .def_readwrite("y", &TinyVector3::m_y) .def_readwrite("z", &TinyVector3::m_z) + .def(py::self * float()) .def(py::self + py::self) .def(py::self - py::self) .def(py::self += py::self) @@ -288,6 +714,7 @@ PYBIND11_MODULE(pytinyopengl3, m) { .def(py::init>()) .def("get_at", &TinyMatrix3x3::get_at) .def("get_row", &TinyMatrix3x3::getRow) + .def("get_column", &TinyMatrix3x3::get_column) .def("set_identity", &TinyMatrix3x3::set_identity) .def("setRotation", &TinyMatrix3x3::setRotation) .def("getRotation", &TinyMatrix3x3::getRotation2); @@ -322,6 +749,7 @@ PYBIND11_MODULE(pytinyopengl3, m) { .export_values(); ; + #ifdef VERSION_INFO m.attr("__version__") = VERSION_INFO; #else diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..b26e81fb --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +numpy + diff --git a/setup.py b/setup.py index 35e1e714..51b80f30 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -1# +# # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -126,6 +126,7 @@ def _single_compile(obj): "src/visualizer/opengl/tiny_open_sans.cpp",\ "src/visualizer/opengl/tiny_opengl_fontstashcallbacks.cpp",\ "src/visualizer/opengl/tiny_opengl3_app.cpp",\ +"src/visualizer/opengl/utils/tiny_logging.cpp",\ "third_party/stb_image/stb_image_write.cpp",\ "third_party/glad/gl.c",\ "third_party/stb_image/stb_image.cpp",\ @@ -139,6 +140,7 @@ def _single_compile(obj): print("linux") libraries = ['dl', 'pthread', 'stdc++fs'] CXX_FLAGS += '-D_LINUX ' + CXX_FLAGS += '-DTINY_USE_EGL ' CXX_FLAGS += '-DGLEW_STATIC ' CXX_FLAGS += '-DGLEW_INIT_OPENGL11_FUNCTIONS=1 ' CXX_FLAGS += '-DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 ' @@ -156,6 +158,8 @@ def _single_compile(obj): CXX_FLAGS += '-Wno-unused-but-set-variable ' pytinyopengl3_libraries += ['dl','pthread'] pytinyopengl3_sources += ["src/visualizer/opengl/tiny_x11_opengl_window.cpp",\ + "src/visualizer/opengl/tiny_egl_opengl_window.cpp",\ + "third_party/glad/egl.c",\ "third_party/glad/glx.c"] @@ -234,7 +238,7 @@ def _single_compile(obj): extra_compile_args=CXX_FLAGS.split(), include_dirs=include_dirs + ["."]) -extensions.append(pytinydiffsim_dual_ext) +#extensions.append(pytinydiffsim_dual_ext) if os.path.exists("third_party/CppAD/include"): platform_include_dirs = [] @@ -273,7 +277,7 @@ def _single_compile(obj): setup( name='pytinydiffsim', - version='0.1.0', + version='0.9.4', description= 'Tiny Differentiable Physics Library for Robotics Simulation and Reinforcement Learning', long_description= diff --git a/src/math/tiny/tiny_matrix3x3.h b/src/math/tiny/tiny_matrix3x3.h index 1a30156e..6422f836 100644 --- a/src/math/tiny/tiny_matrix3x3.h +++ b/src/math/tiny/tiny_matrix3x3.h @@ -609,19 +609,24 @@ class TinyMatrix3x3 { return TinyVector3((*this)[0].dot(v), (*this)[1].dot(v), (*this)[2].dot(v)); } #endif -#if 0 - /** @brief Get a column of the matrix as a vector +#ifdef TDS_USE_COLUMN_MAJOR + /** @brief Get a column of the matrix as a vector * @param i Column number 0 indexed */ inline TinyVector3 getColumn(int i) const { - return TinyVector3(m_el[0][i], m_el[1][i], m_el[2][i]); + return m_el[i]; } +#else - - - - + /** @brief Get a column of the matrix as a vector + * @param i Column number 0 indexed */ + inline TinyVector3 get_column(int i) const + { + return TinyVector3(m_el[0][i], m_el[1][i], m_el[2][i]); + } +#endif +#if 0 /** @brief Set from the rotational part of a 4x4 OpenGL matrix diff --git a/src/urdf/urdf_parser.hpp b/src/urdf/urdf_parser.hpp index 013e48f1..95483963 100644 --- a/src/urdf/urdf_parser.hpp +++ b/src/urdf/urdf_parser.hpp @@ -143,8 +143,11 @@ struct UrdfParser { const tinyxml2::XMLElement* inertia_xml = config->FirstChildElement("inertia"); if (!inertia_xml) { - logger.report_error("Inertial element must have inertia element"); - return false; + logger.report_warning("Inertial element lacks inertia element, using ixx=1.0, iyy=1.0 and izz=1.0"); + urdf_inertial.inertia_xxyyzz[0] = Scalar(1.0); + urdf_inertial.inertia_xxyyzz[1] = Scalar(1.0); + urdf_inertial.inertia_xxyyzz[2] = Scalar(1.0); + return true; } if ((inertia_xml->Attribute("ixx") && inertia_xml->Attribute("iyy") && @@ -704,7 +707,7 @@ struct UrdfParser { } } - UrdfStructures load_urdf(const std::string& file_name) { + UrdfStructures load_urdf(const std::string& file_name, bool verbose=false) { std::ifstream ifs(file_name); std::string urdf_string; @@ -716,8 +719,18 @@ struct UrdfParser { urdf_string = std::string((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); - //StdLogger logger; - NullLogger logger; + tds::NullLogger null_logger; + tds::StdLogger std_logger; + tds::Logger* loggerPtr = 0; + if (verbose) + { + loggerPtr = &std_logger; + } else + { + loggerPtr = &null_logger; + } + + auto& logger= *loggerPtr; int flags = 0; UrdfStructures urdf_structures; load_urdf_from_string(urdf_string, flags, logger, urdf_structures); diff --git a/src/visualizer/opengl/CMakeLists.txt b/src/visualizer/opengl/CMakeLists.txt index 93ba2f18..d2272f15 100644 --- a/src/visualizer/opengl/CMakeLists.txt +++ b/src/visualizer/opengl/CMakeLists.txt @@ -48,9 +48,16 @@ SET(OpenGLWindow_SRCS ${OpenGLWindow_SRCS} MESSAGE (${OpenGLWindowCommon_CPP}) -IF(BUILD_EGL) +if (UNIX AND NOT APPLE) + OPTION(ENABLE_EGL "Allow the EGL renderer" ON) +else() + OPTION(ENABLE_EGL "Allow the EGL renderer" OFF) +endif() + +IF(ENABLE_EGL) SET(OpenGLWindow_SRCS ${OpenGLWindow_SRCS} ../../../third_party/glad/egl.c) -ENDIF(BUILD_EGL) +ENDIF(ENABLE_EGL) + SET(OpenGLWindow_SRCS ${OpenGLWindow_SRCS} ../../../third_party/stb_image/stb_image_write.cpp @@ -59,6 +66,11 @@ SET(OpenGLWindow_SRCS ${OpenGLWindow_SRCS} MESSAGE("OpenGLWindow_SRCS=${OpenGLWindow_SRCS}") ADD_LIBRARY(opengl_window ${OpenGLWindow_SRCS} ${OpenGLWindow_HDRS}) + +IF(ENABLE_EGL) + target_compile_definitions(opengl_window PUBLIC TINY_USE_EGL PRIVATE TINY_USE_EGL) +ENDIF(ENABLE_EGL) + if (UNIX AND NOT APPLE) target_link_libraries(opengl_window ${DL}) elseif (APPLE) diff --git a/src/visualizer/opengl/tiny_camera.cpp b/src/visualizer/opengl/tiny_camera.cpp index 078ad5a9..99370bd7 100644 --- a/src/visualizer/opengl/tiny_camera.cpp +++ b/src/visualizer/opengl/tiny_camera.cpp @@ -50,8 +50,20 @@ struct TinyCameraInternalData { float m_projectionMatrixVR[16]; }; -TinyCamera::TinyCamera() { m_data = new TinyCameraInternalData; } -TinyCamera::~TinyCamera() { delete m_data; } + TinyCamera::TinyCamera(const TinyCamera& other) + { + m_data = new TinyCameraInternalData(*other.m_data); + } + +TinyCamera::TinyCamera() { + m_data = new TinyCameraInternalData; +} +TinyCamera::~TinyCamera() { + delete m_data; +} + + + static void b3CreateFrustum(float left, float right, float bottom, float top, float nearVal, float farVal, float frustum[16]) { @@ -277,3 +289,7 @@ void TinyCamera::set_camera_frustum_far(float far) { void TinyCamera::set_camera_frustum_near(float near) { m_data->m_frustumZNear = near; } + +void TinyCamera::copy_data(const TinyCamera& other) { + *this->m_data = *other.m_data; +} diff --git a/src/visualizer/opengl/tiny_camera.h b/src/visualizer/opengl/tiny_camera.h index 2c80171c..cec21622 100644 --- a/src/visualizer/opengl/tiny_camera.h +++ b/src/visualizer/opengl/tiny_camera.h @@ -16,12 +16,14 @@ #define TINY_CAMERA_H #include "math/tiny/tiny_float_utils.h" - +#include struct TinyCamera { struct TinyCameraInternalData* m_data; + TinyCamera(const TinyCamera& other); + TinyCamera(); virtual ~TinyCamera(); @@ -29,6 +31,18 @@ struct TinyCamera { virtual void get_camera_projection_matrix(float m[16]) const; virtual void get_camera_view_matrix(float m[16]) const; + virtual std::array get_camera_projection_matrix2() const + { + std::array proj; + get_camera_projection_matrix(&proj[0]); + return proj; + } + virtual std::array get_camera_view_matrix2() const + { + std::array view; + get_camera_view_matrix(&view[0]); + return view; + } virtual void get_camera_target_position(::TINY::TinyVector3f& pos) const; virtual void get_camera_position(::TINY::TinyVector3f& pos) const; @@ -62,6 +76,8 @@ struct TinyCamera { virtual void set_camera_frustum_far(float far); virtual void set_camera_frustum_near(float near); + + virtual void copy_data(const TinyCamera& other); }; #endif // TINY_CAMERA_H diff --git a/src/visualizer/opengl/tiny_egl_opengl_window.cpp b/src/visualizer/opengl/tiny_egl_opengl_window.cpp index a77b71b8..479abd90 100644 --- a/src/visualizer/opengl/tiny_egl_opengl_window.cpp +++ b/src/visualizer/opengl/tiny_egl_opengl_window.cpp @@ -290,7 +290,7 @@ void EGLOpenGLWindow::set_resize_callback(TinyResizeCallback resizeCallback) { m_data->m_resizeCallback = resizeCallback; } -TinyResizeCallback EGLOpenGLWindow::getResizeCallback() { +TinyResizeCallback EGLOpenGLWindow::get_resize_callback() { return m_data->m_resizeCallback; } diff --git a/src/visualizer/opengl/tiny_egl_opengl_window.h b/src/visualizer/opengl/tiny_egl_opengl_window.h index f54d1fe4..bd149cdf 100644 --- a/src/visualizer/opengl/tiny_egl_opengl_window.h +++ b/src/visualizer/opengl/tiny_egl_opengl_window.h @@ -42,6 +42,8 @@ class EGLOpenGLWindow : public TinyWindowInterface { virtual bool requested_exit() const; virtual void set_request_exit(); + virtual void pump_messages() {} + virtual void start_rendering(); virtual void end_rendering(); @@ -55,7 +57,7 @@ class EGLOpenGLWindow : public TinyWindowInterface { virtual TinyMouseButtonCallback get_mouse_button_callback(); virtual void set_resize_callback(TinyResizeCallback resizeCallback); - virtual TinyResizeCallback getResizeCallback(); + virtual TinyResizeCallback get_resize_callback(); virtual void set_wheel_callback(TinyWheelCallback wheelCallback); virtual TinyWheelCallback get_wheel_callback(); diff --git a/src/visualizer/opengl/tiny_gl_instancing_renderer.cpp b/src/visualizer/opengl/tiny_gl_instancing_renderer.cpp index d74b866d..5af2e20f 100644 --- a/src/visualizer/opengl/tiny_gl_instancing_renderer.cpp +++ b/src/visualizer/opengl/tiny_gl_instancing_renderer.cpp @@ -28,7 +28,7 @@ bool useShadowMap = true; #include "utils/tiny_min_max.h" #include "tiny_opengl_include.h" #include "tiny_window_interface.h" - +#include "utils/tiny_logging.h" struct caster2 { @@ -120,6 +120,7 @@ static const char* triangleVertexShaderText = " uv0 = vUV;\n" "}\n"; + static const char* triangleFragmentShader = "#version 330\n" "precision highp float;" @@ -133,6 +134,21 @@ static const char* triangleFragmentShader = " color = vec4(clr,texel.r)*texel;\n" "}\n"; +std::string triangleVertexShaderTextInit = triangleVertexShaderText; +std::string triangleFragmentShaderInit = triangleFragmentShader; + +std::string useShadowMapInstancingVertexShaderInit = useShadowMapInstancingVertexShader; +std::string useShadowMapInstancingFragmentShaderInit = useShadowMapInstancingFragmentShader; + +std::string createShadowMapInstancingVertexShaderInit = createShadowMapInstancingVertexShader; +std::string createShadowMapInstancingFragmentShaderInit = createShadowMapInstancingFragmentShader; + +std::string segmentationMaskInstancingVertexShaderInit = segmentationMaskInstancingVertexShader; +std::string segmentationMaskInstancingFragmentShaderInit = segmentationMaskInstancingFragmentShader; + +std::string instancingVertexShaderInit = instancingVertexShader; +std::string instancingFragmentShaderInit = instancingFragmentShader; + //#include //"../../opencl/gpu_rigidbody_pipeline/b3GpuNarrowphaseAndSolver.h"//for // m_maxNumObjectCapacity @@ -435,6 +451,9 @@ bool TinyGLInstancingRenderer::read_single_instance_transform_to_cpu( void TinyGLInstancingRenderer::write_single_instance_transform_to_cpu( const TinyVector3f& position, const TinyQuaternionf& orientation, int srcIndex2) { + if (srcIndex2 < 0) + return; + TinyPublicGraphicsInstance* pg = m_data->m_publicGraphicsInstances.get_handle(srcIndex2); assert(pg); @@ -919,6 +938,36 @@ int TinyGLInstancingRenderer::register_graphics_instance_internal( return newUid; // gfxObj->m_numGraphicsInstances; } + +std::vector TinyGLInstancingRenderer::register_graphics_instances(int shapeIndex, + const std::vector<::TINY::TinyVector3f>& positions, + const std::vector<::TINY::TinyQuaternionf>& quaternions, + const std::vector<::TINY::TinyVector3f>& colors, + const std::vector<::TINY::TinyVector3f>& scalings, + float opacity, + bool rebuild) +{ + std::vector uids; + + if ((positions.size() == quaternions.size()) && + (positions.size() == colors.size()) && + (positions.size() == scalings.size() )) + { + uids.reserve(positions.size()); + + for (int i=0;im_maxNumObjectCapacity - 1); - if (shapeIndex == (m_graphicsInstances.size() - 1)) { - register_graphics_instance_internal(newUid, position, quaternion, color, - scaling, opacity); - } else { + //if (shapeIndex == (m_graphicsInstances.size() - 1)) + //{ + // register_graphics_instance_internal(newUid, position, quaternion, color, + // scaling, opacity); + //} else + { int srcIndex = m_data->m_totalNumInstances++; pg->m_internalInstanceIndex = srcIndex; @@ -1083,16 +1134,44 @@ void TinyGLInstancingRenderer::update_shape(int shapeIndex, #endif } + +std::vector TinyGLInstancingRenderer::get_shape_vertex_count() const +{ + std::vector vertex_counts; + vertex_counts.resize(m_graphicsInstances.size()); + for (int i=0;im_numVertices; + } + return vertex_counts; +} + +std::vector TinyGLInstancingRenderer::get_shape_vertex_offsets() const +{ + std::vector vertex_offsets; + vertex_offsets.resize(m_graphicsInstances.size()); + for (int i=0;im_vertexArrayOffset; + } + return vertex_offsets; +} + + + int TinyGLInstancingRenderer::register_shape(const float* vertices, int numvertices, const int* indices, int numIndices, - int primitiveType, int textureId) { + int primitiveType, int textureId, bool double_sided) { b3GraphicsInstance* gfxObj = new b3GraphicsInstance; if (textureId >= 0) { gfxObj->m_textureIndex = textureId; gfxObj->m_flags |= B3_INSTANCE_TEXTURE; } + if (double_sided) { + gfxObj->m_flags |= B3_INSTANCE_DOUBLE_SIDED; + } gfxObj->m_primitiveType = primitiveType; @@ -1167,7 +1246,7 @@ void TinyGLInstancingRenderer::init_shaders() { { triangleShaderProgram = - gltLoadShaderPair(triangleVertexShaderText, triangleFragmentShader); + gltLoadShaderPair(triangleVertexShaderTextInit.c_str(), triangleFragmentShaderInit.c_str()); // triangle_vpos_location = glGetAttribLocation(triangleShaderProgram, // "vPos"); triangle_vUV_location = @@ -1269,7 +1348,7 @@ void TinyGLInstancingRenderer::init_shaders() { glUseProgram(0); useShadowMapInstancingShader = gltLoadShaderPair( - useShadowMapInstancingVertexShader, useShadowMapInstancingFragmentShader); + useShadowMapInstancingVertexShaderInit.c_str(), useShadowMapInstancingFragmentShaderInit.c_str()); glLinkProgram(useShadowMapInstancingShader); glUseProgram(useShadowMapInstancingShader); @@ -1298,8 +1377,8 @@ void TinyGLInstancingRenderer::init_shaders() { glGetUniformLocation(useShadowMapInstancingShader, "materialShininessIn"); createShadowMapInstancingShader = - gltLoadShaderPair(createShadowMapInstancingVertexShader, - createShadowMapInstancingFragmentShader); + gltLoadShaderPair(createShadowMapInstancingVertexShaderInit.c_str(), + createShadowMapInstancingFragmentShaderInit.c_str()); glLinkProgram(createShadowMapInstancingShader); glUseProgram(createShadowMapInstancingShader); createShadow_depthMVP = @@ -1308,8 +1387,8 @@ void TinyGLInstancingRenderer::init_shaders() { glUseProgram(0); segmentationMaskInstancingShader = - gltLoadShaderPair(segmentationMaskInstancingVertexShader, - segmentationMaskInstancingFragmentShader); + gltLoadShaderPair(segmentationMaskInstancingVertexShaderInit.c_str(), + segmentationMaskInstancingFragmentShaderInit.c_str()); glLinkProgram(segmentationMaskInstancingShader); glUseProgram(segmentationMaskInstancingShader); @@ -1321,7 +1400,7 @@ void TinyGLInstancingRenderer::init_shaders() { glUseProgram(0); instancingShader = - gltLoadShaderPair(instancingVertexShader, instancingFragmentShader); + gltLoadShaderPair(instancingVertexShaderInit.c_str(), instancingFragmentShaderInit.c_str()); glLinkProgram(instancingShader); glUseProgram(instancingShader); ModelViewMatrix = glGetUniformLocation(instancingShader, "ModelViewMatrix"); @@ -1458,7 +1537,7 @@ void TinyGLInstancingRenderer::set_active_camera(TinyCamera* cam) { } void TinyGLInstancingRenderer::set_camera(const TinyCamera& cam) { - *m_data->m_activeCamera = cam; + m_data->m_activeCamera->copy_data(cam); } void TinyGLInstancingRenderer::set_light_specular_intensity( @@ -1530,6 +1609,38 @@ void TinyGLInstancingRenderer::update_camera(int upAxis) { } } +void TinyGLInstancingRenderer::get_projection_matrix(float projMatrix[16]) const { + for (int i = 0; i < 16; i++) { + projMatrix[i] = m_data->m_projectionMatrix[i]; + } +} + +void TinyGLInstancingRenderer::set_projection_matrix(const float projMatrix[16]) { + for (int i = 0; i < 16; i++) { + m_data->m_projectionMatrix[i] = projMatrix[i]; + } +} + +void TinyGLInstancingRenderer::get_view_matrix(float viewMatrix[16]) const { + for (int i = 0; i < 16; i++) { + viewMatrix[i] = m_data->m_viewMatrix[i]; + } +} + +void TinyGLInstancingRenderer::set_view_matrix(const float viewMatrix[16]) { + for (int i = 0; i < 16; i++) { + m_data->m_viewMatrix[i] = viewMatrix[i]; + } + TinyPosef tr; + setFromOpenGLMatrix(tr, viewMatrix); + tr.inverse(); + float viewMatInverse[16]; + getOpenGLMatrix(tr, viewMatInverse); + for (int i = 0; i < 16; i++) { + m_data->m_viewMatrixInverse[i] = viewMatInverse[i]; + } +} + void writeTextureToPng(int textureWidth, int textureHeight, const char* fileName, int numComponents) { assert(glGetError() == GL_NO_ERROR); @@ -1597,11 +1708,22 @@ void TinyGLInstancingRenderer::render_scene() { // glFlush(); + std::vector tiles; + + render_scene2(tiles); +} + +void TinyGLInstancingRenderer::render_scene2(std::vector& tiles) { + // avoid some Intel driver on a Macbook Pro to lock-up + // todo: figure out what is going on on that machine + + // glFlush(); + if (m_data->m_useProjectiveTexture) { - render_scene_internal(B3_USE_PROJECTIVE_TEXTURE_RENDERMODE); + render_scene_internal(tiles, B3_USE_PROJECTIVE_TEXTURE_RENDERMODE); } else { if (useShadowMap) { - render_scene_internal(B3_CREATE_SHADOWMAP_RENDERMODE); + render_scene_internal(tiles, B3_CREATE_SHADOWMAP_RENDERMODE); if (m_planeReflectionShapeIndex >= 0) { /* Don't update color or depth. */ @@ -1614,7 +1736,7 @@ void TinyGLInstancingRenderer::render_scene() { glStencilFunc(GL_ALWAYS, 1, 0xffffffff); /* Now render floor; floor pixels just get their stencil set to 1. */ - render_scene_internal(B3_USE_SHADOWMAP_RENDERMODE_REFLECTION_PLANE); + render_scene_internal(tiles, B3_USE_SHADOWMAP_RENDERMODE_REFLECTION_PLANE); /* Re-enable update of color and depth. */ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); @@ -1625,18 +1747,17 @@ void TinyGLInstancingRenderer::render_scene() { glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // draw the reflection objects - render_scene_internal(B3_USE_SHADOWMAP_RENDERMODE_REFLECTION); + render_scene_internal(tiles, B3_USE_SHADOWMAP_RENDERMODE_REFLECTION); glDisable(GL_STENCIL_TEST); } - render_scene_internal(B3_USE_SHADOWMAP_RENDERMODE); + render_scene_internal(tiles, B3_USE_SHADOWMAP_RENDERMODE); } else { - render_scene_internal(); + render_scene_internal(tiles); } } } - struct PointerCaster { union { int m_baseIndex; @@ -2022,6 +2143,9 @@ struct SortableTransparentInstance { int m_shapeIndex; int m_instanceId; + std::vector tiles; + std::vector tile_instance; + }; struct TransparentDistanceSortPredicate { @@ -2031,7 +2155,27 @@ struct TransparentDistanceSortPredicate { } }; -void TinyGLInstancingRenderer::render_scene_internal(int orgRenderMode) { +TinyVector3f TinyGLInstancingRenderer::get_camera_position() const { + GLfloat* viewMatrixInverse = (GLfloat*)&m_data->m_viewMatrixInverse[0]; + TinyVector3f cameraPos(viewMatrixInverse[12], viewMatrixInverse[13], + viewMatrixInverse[14]); + return cameraPos; +} + +TinyVector3f TinyGLInstancingRenderer::get_camera_target() const { + // use 2 units forward, better would be to use the average of near/far plane? + TinyVector3f cameraTarget = get_camera_position() + 2.f*get_camera_forward_vector(); + return cameraTarget; +} + +TinyVector3f TinyGLInstancingRenderer::get_camera_forward_vector() const { + GLfloat* viewMatrix = (GLfloat*)&m_data->m_viewMatrix[0]; + TinyVector3f forward(-viewMatrix[2], -viewMatrix[6], -viewMatrix[10]); + return forward; +} + +void TinyGLInstancingRenderer::render_scene_internal(std::vector& tiles, int orgRenderMode) { + B3_PROFILE("render_scene_internal"); int renderMode = orgRenderMode; bool reflectionPass = false; bool reflectionPlanePass = false; @@ -2053,6 +2197,30 @@ void TinyGLInstancingRenderer::render_scene_internal(int orgRenderMode) { renderMode = B3_USE_PROJECTIVE_TEXTURE_RENDERMODE; } + +{ + B3_PROFILE("rebuild tiles"); +for (int tile = 0; tile< tiles.size();tile++) +{ + tiles[tile].internal_visual_instances.resize(tiles[tile].visual_instances.size()); + for (int vi = 0; vi< tiles[tile].visual_instances.size();vi++) + { + int viz_instance = tiles[tile].visual_instances[vi]; + + TinyPublicGraphicsInstance* pg = + viz_instance >= 0? + m_data->m_publicGraphicsInstances.get_handle(viz_instance) : 0; + + if (pg) + { + tiles[tile].internal_visual_instances[vi] = pg->m_internalInstanceIndex; + } else + { + tiles[tile].internal_visual_instances[vi] = -1; + } + } +} +} // glEnable(GL_DEPTH_TEST); GLint dims[4]; @@ -2167,8 +2335,10 @@ void TinyGLInstancingRenderer::render_scene_internal(int orgRenderMode) { -m_data->m_shadowMapWorldSize, m_data->m_shadowMapWorldSize, 1, 300, depthProjectionMatrix); //-14,14,-14,14,1,200, depthProjectionMatrix); float depthViewMatrix[4][4]; - TinyVector3f center = TinyVector3f(0, 0, 0); - m_data->m_activeCamera->get_camera_target_position(center); + // TinyVector3f center = TinyVector3f(0, 0, 0); + // m_data->m_activeCamera->get_camera_target_position(center); + TinyVector3f center = get_camera_target(); + TinyVector3f camPos = get_camera_position(); // float upf[3]; // m_data->m_activeCamera->get_camera_up_vector(upf); TinyVector3f up, lightFwd; @@ -2212,9 +2382,9 @@ void TinyGLInstancingRenderer::render_scene_internal(int orgRenderMode) { { // update_camera(); - m_data->m_activeCamera->get_camera_projection_matrix( - m_data->m_projectionMatrix); - m_data->m_activeCamera->get_camera_view_matrix(m_data->m_viewMatrix); + // m_data->m_activeCamera->get_camera_projection_matrix( + // m_data->m_projectionMatrix); + // m_data->m_activeCamera->get_camera_view_matrix(m_data->m_viewMatrix); } assert(glGetError() == GL_NO_ERROR); @@ -2239,10 +2409,11 @@ void TinyGLInstancingRenderer::render_scene_internal(int orgRenderMode) { transparentInstances.reserve(totalNumInstances); - float fwd[3]; - m_data->m_activeCamera->get_camera_forward_vector(fwd); - TinyVector3f camForwardVec; - camForwardVec.setValue(fwd[0], fwd[1], fwd[2]); + // float fwd[3]; + // m_data->m_activeCamera->get_camera_forward_vector(fwd); + // TinyVector3f camForwardVec; + // camForwardVec.setValue(fwd[0], fwd[1], fwd[2]); + TinyVector3f camForwardVec = get_camera_forward_vector(); for (int obj = 0; obj < m_graphicsInstances.size(); obj++) { b3GraphicsInstance* gfxObj = m_graphicsInstances[obj]; @@ -2281,8 +2452,47 @@ void TinyGLInstancingRenderer::render_scene_internal(int orgRenderMode) { TransparentDistanceSortPredicate sorter; // transparentInstances.quickSort(sorter); + { + B3_PROFILE("sort transparentInstances"); std::sort(transparentInstances.begin(), transparentInstances.end(), sorter); + } + } + + +bool precompute_tiles = true; +if (precompute_tiles) +{ + B3_PROFILE("build tile_instances"); + for (int a=0;am_numGraphicsInstances; + + + for (int vi = 0; vi< tiles[tile].internal_visual_instances.size();vi++) + { + + if ((tiles[tile].internal_visual_instances[vi] >= instanceIdStart)&& + (tiles[tile].internal_visual_instances[vi] < instanceIdEnd) + ) + { + transparentInstances[a].tiles.push_back(tile); + transparentInstances[a].tile_instance.push_back(tiles[tile].internal_visual_instances[vi]); + } + } + } + } + //printf("transparentInstances[%d].tiles.size()=%d\n", a, (int)transparentInstances[a].tiles.size()); } +} // two passes: first for opaque instances, second for transparent ones. for (int pass = 0; pass < 2; pass++) { @@ -2433,15 +2643,148 @@ void TinyGLInstancingRenderer::render_scene_internal(int orgRenderMode) { switch (renderMode) { case B3_SEGMENTATION_MASK_RENDERMODE: { - glUseProgram(segmentationMaskInstancingShader); - glUniformMatrix4fv(segmentationMaskProjectionMatrix, 1, false, - &m_data->m_projectionMatrix[0]); - glUniformMatrix4fv(segmentationMaskModelViewMatrix, 1, false, - &m_data->m_viewMatrix[0]); - glDrawElementsInstanced(GL_TRIANGLES, indexCount, - GL_UNSIGNED_INT, indexOffset, - gfxObj->m_numGraphicsInstances); + glUseProgram(segmentationMaskInstancingShader); + if (tiles.size()) + { + if (precompute_tiles) + { + for (int nt = 0; nt < transparentInstances[i].tiles.size();nt++) + { + + int tile = transparentInstances[i].tiles[nt]; + int instanceId = transparentInstances[i].tile_instance[nt]; + B3_PROFILE("tile_render_viewport_segmask"); + + glViewport( + tiles[tile].viewport_dims[0], + tiles[tile].viewport_dims[1], + tiles[tile].viewport_dims[2], + tiles[tile].viewport_dims[3]); + + glUniformMatrix4fv(segmentationMaskProjectionMatrix, 1, false, + &tiles[tile].projection_matrix[0]); + glUniformMatrix4fv(segmentationMaskModelViewMatrix, 1, false, + &tiles[tile].view_matrix[0]); + + + //if (0) { + // glDrawElementsInstancedBaseInstance( + // GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, + // indexOffset, 1, qq); + //} else + { + glVertexAttribPointer( + 1, 4, GL_FLOAT, GL_FALSE, 0, + (GLvoid*)((instanceId)*4 * sizeof(float) + + m_data->m_maxShapeCapacityInBytes)); + glVertexAttribPointer( + 2, 4, GL_FLOAT, GL_FALSE, 0, + (GLvoid*)((instanceId)*4 * sizeof(float) + + m_data->m_maxShapeCapacityInBytes + + POSITION_BUFFER_SIZE)); + glVertexAttribPointer( + 5, 4, GL_FLOAT, GL_FALSE, 0, + (GLvoid*)((instanceId)*4 * sizeof(float) + + m_data->m_maxShapeCapacityInBytes + + POSITION_BUFFER_SIZE + + ORIENTATION_BUFFER_SIZE)); + glVertexAttribPointer( + 6, 4, GL_FLOAT, GL_FALSE, 0, + (GLvoid*)((instanceId)*4 * sizeof(float) + + m_data->m_maxShapeCapacityInBytes + + POSITION_BUFFER_SIZE + + ORIENTATION_BUFFER_SIZE + + COLOR_BUFFER_SIZE)); + + glDrawElements(GL_TRIANGLES, indexCount, + GL_UNSIGNED_INT, 0); + } + + } + } else + { + for (unsigned int qq = 0; qq < gfxObj->m_numGraphicsInstances; + qq++) { + + B3_PROFILE("for each qq"); + + for (int tile = 0; tile< tiles.size();tile++) + { + + B3_PROFILE("for each tile"); + + int instanceId = transparentInstances[i].m_instanceId+qq; + + for (int vi = 0; vi< tiles[tile].internal_visual_instances.size();vi++) + { + B3_PROFILE("for each tile visual instance"); + + if (tiles[tile].internal_visual_instances[vi] == instanceId) + { + B3_PROFILE("tile_render_viewport_segmask"); + + glViewport( + tiles[tile].viewport_dims[0], + tiles[tile].viewport_dims[1], + tiles[tile].viewport_dims[2], + tiles[tile].viewport_dims[3]); + + glUniformMatrix4fv(segmentationMaskProjectionMatrix, 1, false, + &tiles[tile].projection_matrix[0]); + glUniformMatrix4fv(segmentationMaskModelViewMatrix, 1, false, + &tiles[tile].view_matrix[0]); + + + //if (0) { + // glDrawElementsInstancedBaseInstance( + // GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, + // indexOffset, 1, qq); + //} else + { + glVertexAttribPointer( + 1, 4, GL_FLOAT, GL_FALSE, 0, + (GLvoid*)((instanceId)*4 * sizeof(float) + + m_data->m_maxShapeCapacityInBytes)); + glVertexAttribPointer( + 2, 4, GL_FLOAT, GL_FALSE, 0, + (GLvoid*)((instanceId)*4 * sizeof(float) + + m_data->m_maxShapeCapacityInBytes + + POSITION_BUFFER_SIZE)); + glVertexAttribPointer( + 5, 4, GL_FLOAT, GL_FALSE, 0, + (GLvoid*)((instanceId)*4 * sizeof(float) + + m_data->m_maxShapeCapacityInBytes + + POSITION_BUFFER_SIZE + + ORIENTATION_BUFFER_SIZE)); + glVertexAttribPointer( + 6, 4, GL_FLOAT, GL_FALSE, 0, + (GLvoid*)((instanceId)*4 * sizeof(float) + + m_data->m_maxShapeCapacityInBytes + + POSITION_BUFFER_SIZE + + ORIENTATION_BUFFER_SIZE + + COLOR_BUFFER_SIZE)); + + glDrawElements(GL_TRIANGLES, indexCount, + GL_UNSIGNED_INT, 0); + } + } + + } + } + } + } + glViewport(dims[0], dims[1], dims[2], dims[3]); + } else + { + glUniformMatrix4fv(segmentationMaskProjectionMatrix, 1, false, + &m_data->m_projectionMatrix[0]); + glUniformMatrix4fv(segmentationMaskModelViewMatrix, 1, false, + &m_data->m_viewMatrix[0]); + glDrawElementsInstanced(GL_TRIANGLES, indexCount, + GL_UNSIGNED_INT, indexOffset, + gfxObj->m_numGraphicsInstances); + } break; } case B3_DEFAULT_RENDERMODE: { @@ -2490,9 +2833,156 @@ void TinyGLInstancingRenderer::render_scene_internal(int orgRenderMode) { glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0); } else { - glDrawElementsInstanced(GL_TRIANGLES, indexCount, - GL_UNSIGNED_INT, indexOffset, - gfxObj->m_numGraphicsInstances); + + if (tiles.size()) { + + if (precompute_tiles) + { + for (int nt = 0; nt < transparentInstances[i].tiles.size();nt++) + { + + int tile = transparentInstances[i].tiles[nt]; + int instanceId = transparentInstances[i].tile_instance[nt]; + + B3_PROFILE("tile_render_viewpor"); + + glViewport( + tiles[tile].viewport_dims[0], + tiles[tile].viewport_dims[1], + tiles[tile].viewport_dims[2], + tiles[tile].viewport_dims[3]); + + glUniformMatrix4fv(ProjectionMatrix, 1, false, + &tiles[tile].projection_matrix[0]); + glUniformMatrix4fv(ModelViewMatrix, 1, false, + &tiles[tile].view_matrix[0]); + + + //if (0) { + // glDrawElementsInstancedBaseInstance( + // GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, + // indexOffset, 1, qq); + //} else + { + glVertexAttribPointer( + 1, 4, GL_FLOAT, GL_FALSE, 0, + (GLvoid*)((instanceId)*4 * sizeof(float) + + m_data->m_maxShapeCapacityInBytes)); + glVertexAttribPointer( + 2, 4, GL_FLOAT, GL_FALSE, 0, + (GLvoid*)((instanceId)*4 * sizeof(float) + + m_data->m_maxShapeCapacityInBytes + + POSITION_BUFFER_SIZE)); + glVertexAttribPointer( + 5, 4, GL_FLOAT, GL_FALSE, 0, + (GLvoid*)((instanceId)*4 * sizeof(float) + + m_data->m_maxShapeCapacityInBytes + + POSITION_BUFFER_SIZE + + ORIENTATION_BUFFER_SIZE)); + glVertexAttribPointer( + 6, 4, GL_FLOAT, GL_FALSE, 0, + (GLvoid*)((instanceId)*4 * sizeof(float) + + m_data->m_maxShapeCapacityInBytes + + POSITION_BUFFER_SIZE + + ORIENTATION_BUFFER_SIZE + + COLOR_BUFFER_SIZE)); + + glDrawElements(GL_TRIANGLES, indexCount, + GL_UNSIGNED_INT, 0); + } + } + } else + { + B3_PROFILE("tile_render_search"); + + //for (unsigned int qq = 0; qq < gfxObj->m_numGraphicsInstances;qq++) + { + + B3_PROFILE("for each qq"); + + for (int tile = 0; tile< tiles.size();tile++) + { + + B3_PROFILE("for each tile"); + + + int instanceIdStart = transparentInstances[i].m_instanceId; + int instanceIdEnd = instanceIdStart+gfxObj->m_numGraphicsInstances; + + + for (int vi = 0; vi< tiles[tile].internal_visual_instances.size();vi++) + { + + B3_PROFILE("for each tile visual "); + + if ((tiles[tile].internal_visual_instances[vi] >= instanceIdStart)&& + (tiles[tile].internal_visual_instances[vi] < instanceIdEnd) + ) + { + + int instanceId = tiles[tile].internal_visual_instances[vi]; + + B3_PROFILE("tile_render_viewpor"); + + glViewport( + tiles[tile].viewport_dims[0], + tiles[tile].viewport_dims[1], + tiles[tile].viewport_dims[2], + tiles[tile].viewport_dims[3]); + + glUniformMatrix4fv(ProjectionMatrix, 1, false, + &tiles[tile].projection_matrix[0]); + glUniformMatrix4fv(ModelViewMatrix, 1, false, + &tiles[tile].view_matrix[0]); + + + //if (0) { + // glDrawElementsInstancedBaseInstance( + // GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, + // indexOffset, 1, qq); + //} else + { + glVertexAttribPointer( + 1, 4, GL_FLOAT, GL_FALSE, 0, + (GLvoid*)((instanceId)*4 * sizeof(float) + + m_data->m_maxShapeCapacityInBytes)); + glVertexAttribPointer( + 2, 4, GL_FLOAT, GL_FALSE, 0, + (GLvoid*)((instanceId)*4 * sizeof(float) + + m_data->m_maxShapeCapacityInBytes + + POSITION_BUFFER_SIZE)); + glVertexAttribPointer( + 5, 4, GL_FLOAT, GL_FALSE, 0, + (GLvoid*)((instanceId)*4 * sizeof(float) + + m_data->m_maxShapeCapacityInBytes + + POSITION_BUFFER_SIZE + + ORIENTATION_BUFFER_SIZE)); + glVertexAttribPointer( + 6, 4, GL_FLOAT, GL_FALSE, 0, + (GLvoid*)((instanceId)*4 * sizeof(float) + + m_data->m_maxShapeCapacityInBytes + + POSITION_BUFFER_SIZE + + ORIENTATION_BUFFER_SIZE + + COLOR_BUFFER_SIZE)); + + glDrawElements(GL_TRIANGLES, indexCount, + GL_UNSIGNED_INT, 0); + } + } + + } + } + } + } + glViewport(dims[0], dims[1], dims[2], dims[3]); + + } else { + + B3_PROFILE("glDrawElementsInstanced"); + glDrawElementsInstanced(GL_TRIANGLES, indexCount, + GL_UNSIGNED_INT, indexOffset, + gfxObj->m_numGraphicsInstances); + } } if (gfxObj->m_flags & B3_INSTANCE_TRANSPARANCY) { @@ -2561,8 +3051,8 @@ void TinyGLInstancingRenderer::render_scene_internal(int orgRenderMode) { // gLightDir.normalize(); glUniform3f(useShadow_lightPosIn, m_data->m_lightPos[0], m_data->m_lightPos[1], m_data->m_lightPos[2]); - TinyVector3f camPos; - m_data->m_activeCamera->get_camera_position(camPos); + // TinyVector3f camPos; + // m_data->m_activeCamera->get_camera_position(camPos); glUniform3f(useShadow_cameraPositionIn, camPos[0], camPos[1], camPos[2]); glUniform1f(useShadow_materialShininessIn, @@ -2655,8 +3145,8 @@ void TinyGLInstancingRenderer::render_scene_internal(int orgRenderMode) { glUniformMatrix4fv(projectiveTexture_MVP, 1, false, &MVP[0]); glUniform3f(projectiveTexture_lightPosIn, m_data->m_lightPos[0], m_data->m_lightPos[1], m_data->m_lightPos[2]); - TinyVector3f camPos; - m_data->m_activeCamera->get_camera_position(camPos); + // TinyVector3f camPos; + // m_data->m_activeCamera->get_camera_position(camPos); glUniform3f(projectiveTexture_cameraPositionIn, camPos[0], camPos[1], camPos[2]); glUniform1f(projectiveTexture_materialShininessIn, diff --git a/src/visualizer/opengl/tiny_gl_instancing_renderer.h b/src/visualizer/opengl/tiny_gl_instancing_renderer.h index 78a6cad3..285dd9ed 100644 --- a/src/visualizer/opengl/tiny_gl_instancing_renderer.h +++ b/src/visualizer/opengl/tiny_gl_instancing_renderer.h @@ -44,6 +44,14 @@ enum { B3_SEGMENTATION_MASK_RENDERMODE, }; +struct TinyViewportTile +{ + std::vector visual_instances; + std::vector internal_visual_instances; + std::array viewport_dims; + std::array view_matrix; + std::array projection_matrix; +}; class TinyGLInstancingRenderer { std::vector m_graphicsInstances; @@ -79,7 +87,8 @@ class TinyGLInstancingRenderer { virtual void init(); virtual void render_scene(); - virtual void render_scene_internal(int orgRenderMode = B3_DEFAULT_RENDERMODE); + virtual void render_scene2(std::vector& tiles); + virtual void render_scene_internal(std::vector& tiles, int orgRenderMode = B3_DEFAULT_RENDERMODE); void init_shaders(); void cleanup_shaders(); @@ -88,18 +97,22 @@ class TinyGLInstancingRenderer { virtual void update_shape(int shapeIndex, const float* vertices); - /// vertices must be in the format x,y,z, nx,ny,nz, u,v + /// vertices must be in the format x,y,z,w, nx,ny,nz, u,v - virtual int register_shape1(std::vector& vertices, std::vector& indices, int textureIndex = -1) + virtual int register_shape1(std::vector& vertices, std::vector& indices, int textureIndex = -1, bool double_sided = false) { - return register_shape(&vertices[0], vertices.size(), &indices[0], indices.size(), textureIndex); + return register_shape(&vertices[0], vertices.size()/9, &indices[0], indices.size(), B3_GL_TRIANGLES, textureIndex, double_sided); } virtual int register_shape(const float* vertices, int numvertices, const int* indices, int numIndices, int primitiveType = B3_GL_TRIANGLES, - int textureIndex = -1); + int textureIndex = -1, + bool double_sided = false); + + std::vector get_shape_vertex_count() const; + std::vector get_shape_vertex_offsets() const; virtual int register_texture1(const std::vector& texels, int width, int height, bool flipPixelsY = true) @@ -124,6 +137,16 @@ class TinyGLInstancingRenderer { float opacity = 1.f, bool rebuild = true); + + std::vector register_graphics_instances(int shapeIndex, + const std::vector<::TINY::TinyVector3f>& positions, + const std::vector<::TINY::TinyQuaternionf>& quaternions, + const std::vector<::TINY::TinyVector3f>& colors, + const std::vector<::TINY::TinyVector3f>& scalings, + float opacity = 1.f, + bool rebuild=true); + + void write_transforms(); virtual bool read_single_instance_transform_to_cpu(float* position, @@ -187,10 +210,21 @@ class TinyGLInstancingRenderer { virtual const TinyCamera* get_active_camera() const; virtual TinyCamera* get_active_camera(); + TinyCamera get_active_camera2() const + { + const TinyCamera* cam = get_active_camera(); + return *cam; + } + void set_camera(const TinyCamera& cam); virtual void set_active_camera(TinyCamera* cam); + void get_projection_matrix(float projMatrix[16]) const; + void set_projection_matrix(const float projMatrix[16]); + void get_view_matrix(float viewMatrix[16]) const; + void set_view_matrix(const float viewMatrix[16]); + virtual void set_light_position(const float lightPos[3]); virtual void set_light_position(const double lightPos[3]); virtual void set_shadow_map_resolution(int shadowMapResolution); @@ -217,6 +251,11 @@ class TinyGLInstancingRenderer { virtual void clear_z_buffer(); virtual void set_render_frame_buffer(unsigned int renderFrameBuffer); + +private: + ::TINY::TinyVector3f get_camera_position() const; + ::TINY::TinyVector3f get_camera_target() const; + ::TINY::TinyVector3f get_camera_forward_vector() const; }; #endif // GL_INSTANCING_RENDERER_H diff --git a/src/visualizer/opengl/tiny_gl_render_to_texture.cpp b/src/visualizer/opengl/tiny_gl_render_to_texture.cpp index f398874c..7139ed64 100644 --- a/src/visualizer/opengl/tiny_gl_render_to_texture.cpp +++ b/src/visualizer/opengl/tiny_gl_render_to_texture.cpp @@ -37,26 +37,32 @@ GLRenderToTexture::GLRenderToTexture() : m_framebufferName(0) { gIntelLinuxglDrawBufferWorkaround = true; } #endif //! defined(_WIN32) && !defined(__APPLE__) + + m_width = -1; + m_height = -1; } void GLRenderToTexture::init(int width, int height, GLuint textureId, int renderTextureType) { m_renderTextureType = renderTextureType; + m_width = width; + m_height = height; glGenFramebuffers(1, &m_framebufferName); glBindFramebuffer(GL_FRAMEBUFFER, m_framebufferName); - // The depth buffer - // glGenRenderbuffers(1, &m_depthrenderbuffer); - - // glBindRenderbuffer(GL_RENDERBUFFER, m_depthrenderbuffer); - // glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, - // height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - // GL_RENDERBUFFER, m_depthrenderbuffer); switch (m_renderTextureType) { case RENDERTEXTURE_COLOR: { glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureId, 0); + + // The depth buffer + glGenRenderbuffers(1, &m_depthrenderbuffer); + + glBindRenderbuffer(GL_RENDERBUFFER, m_depthrenderbuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT,width,height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthrenderbuffer); + break; } case RENDERTEXTURE_DEPTH: { @@ -67,6 +73,8 @@ void GLRenderToTexture::init(int width, int height, GLuint textureId, }; glBindFramebuffer(GL_FRAMEBUFFER, 0); + + } bool GLRenderToTexture::enable() { @@ -79,6 +87,13 @@ bool GLRenderToTexture::enable() { // Set the list of draw buffers. GLenum drawBuffers[2] = {GL_COLOR_ATTACHMENT0, 0}; glDrawBuffers(1, drawBuffers); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | + GL_STENCIL_BUFFER_BIT); + + // glCullFace(GL_BACK); + // glFrontFace(GL_CCW); + glEnable(GL_DEPTH_TEST); + break; } case RENDERTEXTURE_DEPTH: { diff --git a/src/visualizer/opengl/tiny_gl_render_to_texture.h b/src/visualizer/opengl/tiny_gl_render_to_texture.h index 0c38ef3f..ea855c97 100644 --- a/src/visualizer/opengl/tiny_gl_render_to_texture.h +++ b/src/visualizer/opengl/tiny_gl_render_to_texture.h @@ -26,8 +26,11 @@ enum { struct GLRenderToTexture { GLuint m_framebufferName; GLuint m_depthrenderbuffer; + GLuint m_renderTextureId; bool m_initialized; int m_renderTextureType; + int m_width; + int m_height; public: GLRenderToTexture(); diff --git a/src/visualizer/opengl/tiny_mac_opengl_window.cpp b/src/visualizer/opengl/tiny_mac_opengl_window.cpp index 64909f27..0bb67c40 100644 --- a/src/visualizer/opengl/tiny_mac_opengl_window.cpp +++ b/src/visualizer/opengl/tiny_mac_opengl_window.cpp @@ -50,6 +50,11 @@ void MacOpenGLWindow::create_window(const TinyWindowConstructionInfo& ci) { void MacOpenGLWindow::run_main_loop() {} +void MacOpenGLWindow::pump_messages() +{ + Mac_pumpMessage(m_internalData); +} + void MacOpenGLWindow::start_rendering() { Mac_updateWindow(m_internalData); } void MacOpenGLWindow::end_rendering() { Mac_swapBuffer(m_internalData); } diff --git a/src/visualizer/opengl/tiny_mac_opengl_window.h b/src/visualizer/opengl/tiny_mac_opengl_window.h index 19cf6cdc..c32db1bf 100644 --- a/src/visualizer/opengl/tiny_mac_opengl_window.h +++ b/src/visualizer/opengl/tiny_mac_opengl_window.h @@ -31,6 +31,8 @@ class MacOpenGLWindow : public TinyWindowInterface { void close_window(); void start_rendering(); + + virtual void pump_messages(); void end_rendering(); // swap buffers diff --git a/src/visualizer/opengl/tiny_mac_opengl_window_objc.h b/src/visualizer/opengl/tiny_mac_opengl_window_objc.h index bc9725d7..d840a2de 100644 --- a/src/visualizer/opengl/tiny_mac_opengl_window_objc.h +++ b/src/visualizer/opengl/tiny_mac_opengl_window_objc.h @@ -44,6 +44,7 @@ int Mac_createWindow(struct MacOpenGLWindowInternalData* m_internalData, void Mac_setWindowTitle(struct MacOpenGLWindowInternalData* data, const char* windowTitle); +int Mac_pumpMessage(struct MacOpenGLWindowInternalData* m_internalData); int Mac_updateWindow(struct MacOpenGLWindowInternalData* m_internalData); void Mac_swapBuffer(struct MacOpenGLWindowInternalData* m_internalData); int Mac_requestedExit(struct MacOpenGLWindowInternalData* m_internalData); diff --git a/src/visualizer/opengl/tiny_mac_opengl_window_objc.m b/src/visualizer/opengl/tiny_mac_opengl_window_objc.m index 0d61ab74..c0f544b7 100644 --- a/src/visualizer/opengl/tiny_mac_opengl_window_objc.m +++ b/src/visualizer/opengl/tiny_mac_opengl_window_objc.m @@ -770,17 +770,12 @@ int getAsciiCodeFromVirtualKeycode(int virtualKeyCode) return keycode; } - -int Mac_updateWindow(struct MacOpenGLWindowInternalData* m_internalData) +int Mac_pumpMessage(struct MacOpenGLWindowInternalData* m_internalData) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - - GLint err = glGetError(); - assert(err==GL_NO_ERROR); - - - NSEvent *event = nil; + +NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSEvent *event = nil; bool handledEvent = false; do @@ -1023,10 +1018,20 @@ int Mac_updateWindow(struct MacOpenGLWindowInternalData* m_internalData) [m_internalData->m_myApp updateWindows]; } while (event); - - err = glGetError(); + + [pool release]; +} + +int Mac_updateWindow(struct MacOpenGLWindowInternalData* m_internalData) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + Mac_pumpMessage( m_internalData); + + GLint err = glGetError(); assert(err==GL_NO_ERROR); + [m_internalData->m_myview MakeCurrent]; err = glGetError(); assert(err==GL_NO_ERROR); @@ -1047,7 +1052,6 @@ int Mac_updateWindow(struct MacOpenGLWindowInternalData* m_internalData) err = glGetError(); assert(err==GL_NO_ERROR); - [pool release]; return 0; } diff --git a/src/visualizer/opengl/tiny_opengl3_app.cpp b/src/visualizer/opengl/tiny_opengl3_app.cpp index c9b0b2f7..0dd037d9 100644 --- a/src/visualizer/opengl/tiny_opengl3_app.cpp +++ b/src/visualizer/opengl/tiny_opengl3_app.cpp @@ -17,10 +17,10 @@ #include "tiny_opengl3_app.h" #include "tiny_shape_data.h" -#ifdef BT_USE_EGL -#include "EGLOpenGLWindow.h" +#ifdef TINY_USE_EGL +#include "tiny_egl_opengl_window.h" #else -#endif // BT_USE_EGL +#endif // TINY_USE_EGL #ifdef B3_USE_GLFW #include "GLFWOpenGLWindow.h" @@ -51,6 +51,7 @@ #include "tiny_gl_primitive_renderer.h" #include "tiny_gl_render_to_texture.h" #include "tiny_opengl_fontstashcallbacks.h" +#include "tiny_gl_instance_renderer_internal_data.h" #ifdef _WIN32 #define popen _popen @@ -72,7 +73,7 @@ struct TinyOpenGL3AppInternalData { int m_droidRegular2; int m_textureId; - const char* m_frameDumpPngFileName; + std::string m_frameDumpPngFileName; FILE* m_ffmpegFile; GLRenderToTexture* m_renderTexture; void* m_userPointer; @@ -81,6 +82,10 @@ struct TinyOpenGL3AppInternalData { int m_customViewPortHeight; int m_mp4Fps; + void* m_cudaVboPointer; + bool m_cudaVboRegistered; + + TinyOpenGL3AppInternalData() : m_fontTextureId(0), m_largeFontTextureId(0), @@ -91,14 +96,15 @@ struct TinyOpenGL3AppInternalData { m_droidRegular(0), m_droidRegular2(0), m_textureId(-1), - m_frameDumpPngFileName(0), m_ffmpegFile(0), m_renderTexture(0), m_userPointer(0), m_upAxis(1), m_customViewPortWidth(-1), m_customViewPortHeight(-1), - m_mp4Fps(60) {} + m_mp4Fps(60), + m_cudaVboPointer(0), + m_cudaVboRegistered(false){} }; static TinyOpenGL3App* gApp = 0; @@ -314,7 +320,7 @@ TinyOpenGL3App::TinyOpenGL3App(const char* title, int width, int height, m_window = new TinyDefaultOpenGLWindow(); #endif } else if (windowType == 2) { -#ifdef BT_USE_EGL +#ifdef TINY_USE_EGL m_window = new EGLOpenGLWindow(); #else printf("EGL window requires compilation with BT_USE_EGL.\n"); @@ -907,8 +913,8 @@ int TinyOpenGL3App::register_graphics_unit_sphere_shape( } -int TinyOpenGL3App::register_graphics_capsule_shape(float radius, float half_height, int up_axis, int textureId) { - int red = 0; +int TinyOpenGL3App::register_graphics_cylinder_shape(float radius, float half_height, int up_axis, int textureId, bool flat_caps) { + int red = 0; int green = 255; int blue = 0; // 0;// 128; if(textureId < 0) { @@ -970,11 +976,21 @@ int TinyOpenGL3App::register_graphics_capsule_shape(float radius, float half_hei textured_detailed_sphere_vertices[i * 9 + 2]); TinyVector3f trVer = (2 *radius * vert); - if(trVer[up_axis] > 0) - trVer[up_axis] += half_height; - else - trVer[up_axis] -= half_height; - + if (flat_caps) + { + if(trVer[up_axis] > 0) + trVer[up_axis] = half_height; + else + trVer[up_axis] = -half_height; + } else + { + if(trVer[up_axis] > 0) + trVer[up_axis] += half_height; + else + trVer[up_axis] -= half_height; + + } + transformedVertices[i * 9 + 0] = trVer[0]; transformedVertices[i * 9 + 1] = trVer[1]; transformedVertices[i * 9 + 2] = trVer[2]; @@ -996,6 +1012,13 @@ int TinyOpenGL3App::register_graphics_capsule_shape(float radius, float half_hei return graphicsShapeIndex; } +int TinyOpenGL3App::register_graphics_capsule_shape(float radius, float half_height, int up_axis, int textureId) { + + bool flat_caps = false; + return register_graphics_cylinder_shape(radius, half_height, up_axis, textureId, flat_caps); + +} + void TinyOpenGL3App::draw_grid(DrawGridData data) { int gridSize = data.gridSize; @@ -1122,10 +1145,9 @@ void TinyOpenGL3App::set_viewport(int width, int height) { } } -void TinyOpenGL3App::get_screen_pixels(unsigned char* rgbaBuffer, - int bufferSizeInBytes, - float* depthBuffer, - int depthBufferSizeInBytes) { + +void TinyOpenGL3App::get_screen_pixels(std::vector& rgbaBuffer, + std::vector& depthBuffer) { int width = m_data->m_customViewPortWidth >= 0 ? m_data->m_customViewPortWidth : (int)m_window->get_retina_scale() * @@ -1135,17 +1157,21 @@ void TinyOpenGL3App::get_screen_pixels(unsigned char* rgbaBuffer, : (int)m_window->get_retina_scale() * m_instancingRenderer->get_screen_height(); - assert((width * height * 4) == bufferSizeInBytes); - if ((width * height * 4) == bufferSizeInBytes) { - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, rgbaBuffer); + if (m_data->m_renderTexture) { + width = m_data->m_renderTexture->m_width; + height = m_data->m_renderTexture->m_height; + } + rgbaBuffer.resize(width*height*4); + { + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, &rgbaBuffer[0]); int glstat; glstat = glGetError(); assert(glstat == GL_NO_ERROR); } - assert((width * height * sizeof(float)) == depthBufferSizeInBytes); - if ((width * height * sizeof(float)) == depthBufferSizeInBytes) { + depthBuffer.resize(width * height); + { glReadPixels(0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, - depthBuffer); + &depthBuffer[0]); int glstat; glstat = glGetError(); assert(glstat == GL_NO_ERROR); @@ -1161,14 +1187,23 @@ static void writeTextureToFile(int textureWidth, int textureHeight, assert(glGetError() == GL_NO_ERROR); // glReadBuffer(GL_BACK);//COLOR_ATTACHMENT0); - - float* orgPixels = - (float*)malloc(textureWidth * textureHeight * numComponents * 4); - glReadPixels(0, 0, textureWidth, textureHeight, GL_RGBA, GL_FLOAT, orgPixels); + + //int mem_bytes = textureWidth * textureHeight * numComponents * 4; + //printf("mem_bytes=%d\n", mem_bytes); + std::vector orgPixelsArray; + orgPixelsArray.resize(textureWidth * textureHeight * numComponents); + //float* orgPixels = + // (float*)malloc(mem_bytes); + // printf("orgPixels=%x\n", orgPixels); + float* orgPixels = &orgPixelsArray[0]; + glReadPixels(0, 0, textureWidth, textureHeight, GL_RGBA, GL_FLOAT, &orgPixelsArray[0]); // it is useful to have the actual float values for debugging purposes // convert float->char - char* pixels = (char*)malloc(textureWidth * textureHeight * numComponents); + std::vector pixelArray; + pixelArray.resize(textureWidth * textureHeight * numComponents); + char* pixels = &pixelArray[0]; + assert(glGetError() == GL_NO_ERROR); for (int j = 0; j < textureHeight; j++) { @@ -1210,25 +1245,31 @@ static void writeTextureToFile(int textureWidth, int textureHeight, } } } + stbi_write_png_compression_level = 0; stbi_write_png(fileName, textureWidth, textureHeight, numComponents, pixels, textureWidth * numComponents); + } - free(pixels); - free(orgPixels); } void TinyOpenGL3App::swap_buffer() { - if (m_data->m_frameDumpPngFileName) { + if (m_data->m_frameDumpPngFileName!="") { + int width = (int)m_window->get_retina_scale() * m_instancingRenderer->get_screen_width(); int height = (int)m_window->get_retina_scale() * this->m_instancingRenderer->get_screen_height(); - writeTextureToFile(width, height, m_data->m_frameDumpPngFileName, + + if (m_data->m_renderTexture) { + width = m_data->m_renderTexture->m_width; + height = m_data->m_renderTexture->m_height; + } + writeTextureToFile(width, height, m_data->m_frameDumpPngFileName.c_str(), m_data->m_ffmpegFile); m_data->m_renderTexture->disable(); if (m_data->m_ffmpegFile == 0) { - m_data->m_frameDumpPngFileName = 0; + m_data->m_frameDumpPngFileName = ""; } } m_window->end_rendering(); @@ -1265,37 +1306,331 @@ void TinyOpenGL3App::dump_frames_to_video(const char* mp4FileName) { if (m_data->m_ffmpegFile) { fflush(m_data->m_ffmpegFile); pclose(m_data->m_ffmpegFile); - m_data->m_frameDumpPngFileName = 0; + m_data->m_frameDumpPngFileName = ""; } m_data->m_ffmpegFile = 0; } } -void TinyOpenGL3App::dump_next_frame_to_png(const char* filename) { + + +void TinyOpenGL3App::dump_next_frame_to_png(const char* filename, + bool render_to_texture, + int render_width, + int render_height) { // open pipe to ffmpeg's stdin in binary write mode m_data->m_frameDumpPngFileName = filename; // you could use m_renderTexture to allow to render at higher resolutions, // such as 4k or so + if (render_to_texture) { + enable_render_to_texture(render_width, render_height); + } +} + + + +// CUDA includes +//#define USE_SYSTEM_CUDA +#ifdef USE_SYSTEM_CUDA +#include +#include +#else +#include + +#ifdef _WIN32 +#include +#define dlsym GetProcAddress +std::string DYNAMIC_CUDA_PATH = "nvcuda.dll"; +std::string DYNAMIC_CUDART_PATH = "cudart64_110.dll"; +#else +#include +std::string DYNAMIC_CUDA_PATH = "/usr/lib/x86_64-linux-gnu/libcuda.so"; +std::string DYNAMIC_CUDART_PATH = "/usr/local/cuda/lib64/libcudart.so"; +#endif + +using namespace std; + +enum TINY_CUDA_CODES { + cudaSuccess = 0, + CU_GET_PROC_ADDRESS_DEFAULT = 0, + cudaEnableDefault = 0, + + cudaGraphicsMapFlagsNone = 0, + cudaGraphicsMapFlagsReadOnly = 1, + cudaGraphicsMapFlagsWriteDiscard = 2, + + cudaMemcpyHostToHost = 0, /**< Host -> Host */ + cudaMemcpyHostToDevice = 1, /**< Host -> Device */ + cudaMemcpyDeviceToHost = 2, /**< Device -> Host */ + cudaMemcpyDeviceToDevice = 3, /**< Device -> Device */ + cudaMemcpyDefault = 4 /**< Direction of the transfer is inferred from the pointer values. + Requires unified virtual addressing */ +}; + +// CUDA driver API functions. +typedef struct cudaGraphicsResource* cudaGraphicsResource_t; +typedef struct CUstream_st* cudaStream_t; +typedef struct cudaArray* cudaArray_t; + +// see +// https://docs.nvidia.com/cuda/cuda-runtime-api/driver-vs-runtime-api.html#driver-vs-runtime-api +// cuda driver (cuda.so) +TINY_CUDA_CODES (*cuDriverGetVersion)(int* version); +TINY_CUDA_CODES (*cuInit)(unsigned int flags); +TINY_CUDA_CODES (*cuDeviceGetCount)(int* count); +TINY_CUDA_CODES (*cuDeviceGetCount2)(int* count); +TINY_CUDA_CODES (*cuGetProcAddress) (const char* symbol, void** pfn, int cudaVersion, uint64_t flags); +TINY_CUDA_CODES (*cudaMalloc)(void** devPtr, size_t size); +TINY_CUDA_CODES (*cudaFree)(void* devPtr); + +TINY_CUDA_CODES (*cudaGLRegisterBufferObject) ( GLuint bufObj ); +TINY_CUDA_CODES (*cudaGLMapBufferObject) ( void** devPtr, GLuint bufObj ); +TINY_CUDA_CODES (*cudaGLUnmapBufferObject) ( GLuint bufObj ); + + + +TINY_CUDA_CODES (*cudaMemcpyFromArray) +(void* dst, const cudaArray_t src, size_t wOffset, size_t hOffset, size_t count, + unsigned int kind); + +#define LOAD_CUDA_FUNCTION(name, version) \ + name = reinterpret_cast(dlsym(cuda_lib, #name version)); \ + if (!name) cout << "Error:" << #name << " not found in CUDA library" << endl + +// cuda runtime (cudart.so) +TINY_CUDA_CODES (*cudaGraphicsGLRegisterImage) (cudaGraphicsResource** resource, uint64_t image, int target, + unsigned int flags); +// Returns the requested driver API function pointer. +// see also +// https://forums.developer.nvidia.com/t/some-questions-about-cugetprocaddress/191749 +TINY_CUDA_CODES (*cudaGetDriverEntryPoint) +(const char* symbol, void** funcPtr, unsigned long long flags); +TINY_CUDA_CODES (*cudaGraphicsMapResources) +(int count, cudaGraphicsResource_t* resources, cudaStream_t stream); +TINY_CUDA_CODES (*cudaGraphicsSubResourceGetMappedArray) +(cudaArray_t* array, cudaGraphicsResource_t resource, unsigned int arrayIndex, + unsigned int mipLevel); +TINY_CUDA_CODES (*cudaGraphicsUnmapResources) +(int count, cudaGraphicsResource_t* resources, cudaStream_t stream); + +#define LOAD_CUDART_FUNCTION(name, version) \ + name = reinterpret_cast(dlsym(cudart_lib, #name version)); \ + if (!name) cout << "Error:" << #name << " not found in CUDA library" << endl + + + +static bool s_cuda_initialized = false; + + +int init_cuda(bool verbose) { + + if (s_cuda_initialized) + return s_cuda_initialized; + +#ifdef _WIN32 + HMODULE cuda_lib = (HMODULE)LoadLibraryA(DYNAMIC_CUDA_PATH.c_str()); + HMODULE cudart_lib = (HMODULE)LoadLibraryA(DYNAMIC_CUDART_PATH.c_str()); +#else + void* cuda_lib = dlopen(DYNAMIC_CUDA_PATH.c_str(), RTLD_NOW); + void* cudart_lib = dlopen(DYNAMIC_CUDART_PATH.c_str(), RTLD_NOW); + // could use dlerror() on error +#endif + if (!cuda_lib) { + cout << "Unable to load library " << DYNAMIC_CUDA_PATH << endl << endl; + return false; + } + if (!cudart_lib) { + cout << "Unable to load library " << DYNAMIC_CUDART_PATH << endl << endl; + return false; + } + + LOAD_CUDA_FUNCTION(cuDriverGetVersion, ""); + LOAD_CUDA_FUNCTION(cuInit, ""); + LOAD_CUDA_FUNCTION(cuDeviceGetCount, ""); + LOAD_CUDA_FUNCTION(cuGetProcAddress, ""); + + LOAD_CUDART_FUNCTION(cudaGetDriverEntryPoint, ""); + LOAD_CUDART_FUNCTION(cudaGraphicsGLRegisterImage, ""); + LOAD_CUDART_FUNCTION(cudaGraphicsMapResources, ""); + LOAD_CUDART_FUNCTION(cudaGraphicsSubResourceGetMappedArray, ""); + LOAD_CUDART_FUNCTION(cudaMemcpyFromArray, ""); + LOAD_CUDART_FUNCTION(cudaGraphicsUnmapResources, ""); + LOAD_CUDART_FUNCTION(cudaMalloc, ""); + LOAD_CUDART_FUNCTION(cudaFree, ""); + LOAD_CUDART_FUNCTION(cudaGLRegisterBufferObject,""); + LOAD_CUDART_FUNCTION(cudaGLMapBufferObject, ""); + LOAD_CUDART_FUNCTION(cudaGLUnmapBufferObject, ""); + + + auto result = cuInit(0); + int cuda_driver_version; + result = cuDriverGetVersion(&cuda_driver_version); + if (verbose) { + cout << "CUDA driver version:" << cuda_driver_version << endl; + } + int device_count = 0; + result = cuDeviceGetCount(&device_count); + if (verbose) { + cout << "CUDA device count:" << device_count << endl; + } + result = cuGetProcAddress("cuDeviceGetCount", (void**)&cuDeviceGetCount2, + cuda_driver_version, CU_GET_PROC_ADDRESS_DEFAULT); + if (cudaSuccess != result) { + cout << "cuDeviceGetCount not found" << endl; + return false; + } + + s_cuda_initialized = true; + return s_cuda_initialized; +} + +#endif + + +struct ptr2int { + union { + void* pointer; + uint64_t intval; + }; +}; + + +TinyCudaVbo TinyOpenGL3App::cuda_map_vbo(bool verbose) +{ + + + init_cuda(verbose); + auto internal_renderer_data = m_instancingRenderer->get_internal_data(); + + if (!m_data->m_cudaVboRegistered) + { + cudaGLRegisterBufferObject(internal_renderer_data->m_vbo); + m_data->m_cudaVboRegistered = true; + } + cudaGLMapBufferObject(&m_data->m_cudaVboPointer, internal_renderer_data->m_vbo); + + int position_buffer_size_in_bytes = 4*sizeof(float)*internal_renderer_data->m_totalNumInstances; + int orientation_buffer_size_in_bytes = 4*sizeof(float)*internal_renderer_data->m_totalNumInstances; + int color_buffer_size_in_bytes = 4*sizeof(float)*internal_renderer_data->m_totalNumInstances; + + TinyCudaVbo vbo; + char* pp = (char*)m_data->m_cudaVboPointer; + vbo.vertices_ptr = pp; + vbo.num_instances = internal_renderer_data->m_totalNumInstances; + vbo.positions_ptr = pp+internal_renderer_data->m_maxShapeCapacityInBytes; + vbo.orientations_ptr = pp+internal_renderer_data->m_maxShapeCapacityInBytes+position_buffer_size_in_bytes; + vbo.colors_ptr = pp+internal_renderer_data->m_maxShapeCapacityInBytes+position_buffer_size_in_bytes+orientation_buffer_size_in_bytes; + vbo.scalings_ptr = pp+internal_renderer_data->m_maxShapeCapacityInBytes+position_buffer_size_in_bytes+orientation_buffer_size_in_bytes+color_buffer_size_in_bytes; + return vbo; +} + +void TinyOpenGL3App::cuda_unmap_vbo() +{ + if (m_data->m_cudaVboPointer!=0) + { + auto internal_renderer_data = m_instancingRenderer->get_internal_data(); + cudaGLUnmapBufferObject(internal_renderer_data->m_vbo); + m_data->m_cudaVboPointer = 0; + } +} + + + + +uint64_t TinyOpenGL3App::cuda_register_texture_image(uint64_t renderTextureId, bool verbose) { + init_cuda(verbose); + + GLuint shDrawTex; // draws a texture + cudaGraphicsResource* cuda_tex_result_resource; + // get read-write access to OpenGL texture buffer + GLuint tex_cudaResult; + tex_cudaResult = cudaGraphicsGLRegisterImage( + &cuda_tex_result_resource, renderTextureId, GL_TEXTURE_2D, + cudaGraphicsMapFlagsReadOnly); // cudaGraphicsMapFlagsNone); + + assert(tex_cudaResult == cudaSuccess); + + ptr2int caster; + caster.pointer = cuda_tex_result_resource; + return caster.intval; +} + +uint64_t TinyOpenGL3App::cuda_malloc(int num_bytes) { + ptr2int caster; + GLuint tex_cudaResult; + tex_cudaResult = cudaMalloc(&caster.pointer, num_bytes); + assert(tex_cudaResult == cudaSuccess); + return caster.intval; +} + +void TinyOpenGL3App::cuda_free(uint64_t cuda_ptr) { + ptr2int caster; + caster.intval = cuda_ptr; + cudaFree(caster.pointer); +} +uint64_t TinyOpenGL3App::cuda_copy_texture_image( uint64_t cuda_resource_int, uint64_t dest_memory_int, int num_bytes, bool gpu_device_destination, int w_offset, int h_offset) { + + ptr2int caster; + caster.intval = cuda_resource_int; + cudaGraphicsResource* cuda_tex_result_resource = (cudaGraphicsResource*)caster.pointer; + caster.intval = dest_memory_int; + void* dest_memory = caster.pointer; + + GLuint tex_cudaResult = cudaGraphicsMapResources(1, &cuda_tex_result_resource, NULL); + assert(tex_cudaResult == cudaSuccess); + + // https://stackoverflow.com/questions/9406844/cudagraphicsresourcegetmappedpointer-returns-unknown-error + // In other words, use GetMappedPointer for mapped buffer objects. Use + // GetMappedArray for mapped texture objects. + cudaArray* texture_ptr; + tex_cudaResult = cudaGraphicsSubResourceGetMappedArray( + &texture_ptr, cuda_tex_result_resource, 0, 0); + assert(tex_cudaResult == cudaSuccess); + + tex_cudaResult = cudaMemcpyFromArray( + dest_memory, texture_ptr, w_offset, h_offset, num_bytes, + gpu_device_destination ? cudaMemcpyDeviceToDevice + : cudaMemcpyDeviceToHost); + + assert(tex_cudaResult == cudaSuccess); + + tex_cudaResult = cudaGraphicsUnmapResources(1, &cuda_tex_result_resource, 0); + assert(tex_cudaResult == cudaSuccess); + return (uint64_t)cuda_tex_result_resource; +} + + + +uint64_t TinyOpenGL3App::enable_render_to_texture(int render_width, + int render_height) { if (!m_data->m_renderTexture) { m_data->m_renderTexture = new GLRenderToTexture(); - GLuint renderTextureId; - glGenTextures(1, &renderTextureId); + glGenTextures(1, &m_data->m_renderTexture->m_renderTextureId); // "Bind" the newly created texture : all future texture functions will // modify this texture - glBindTexture(GL_TEXTURE_2D, renderTextureId); + glBindTexture(GL_TEXTURE_2D, m_data->m_renderTexture->m_renderTextureId); // Give an empty image to OpenGL ( the last "0" ) // glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, g_OpenGLWidth,g_OpenGLHeight, // 0,GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexImage2D(GL_TEXTURE_2D, // 0,GL_RGBA32F, g_OpenGLWidth,g_OpenGLHeight, 0,GL_RGBA, GL_FLOAT, 0); - glTexImage2D( - GL_TEXTURE_2D, 0, GL_RGBA32F, - m_instancingRenderer->get_screen_width() * m_window->get_retina_scale(), - m_instancingRenderer->get_screen_height() * - m_window->get_retina_scale(), - 0, GL_RGBA, GL_FLOAT, 0); + + if (render_width < 0) { + render_width = m_instancingRenderer->get_screen_width() * + m_window->get_retina_scale(); + } + if (render_height < 0) { + render_height = this->m_instancingRenderer->get_screen_height() * + m_window->get_retina_scale(); + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, render_width, render_height, + 0,GL_RGBA, GL_UNSIGNED_BYTE, 0); + + //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, render_width, render_height, 0, + // GL_RGBA, GL_FLOAT, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, @@ -1303,14 +1638,15 @@ void TinyOpenGL3App::dump_next_frame_to_png(const char* filename) { // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - m_data->m_renderTexture->init( - m_instancingRenderer->get_screen_width() * m_window->get_retina_scale(), - this->m_instancingRenderer->get_screen_height() * - m_window->get_retina_scale(), - renderTextureId, RENDERTEXTURE_COLOR); + m_data->m_renderTexture->init(render_width, render_height, + m_data->m_renderTexture->m_renderTextureId, + RENDERTEXTURE_COLOR); + } m_data->m_renderTexture->enable(); + + return m_data->m_renderTexture->m_renderTextureId; } void TinyOpenGL3App::set_up_axis(int axis) { diff --git a/src/visualizer/opengl/tiny_opengl3_app.h b/src/visualizer/opengl/tiny_opengl3_app.h index 2b8586d6..d0c5cab0 100644 --- a/src/visualizer/opengl/tiny_opengl3_app.h +++ b/src/visualizer/opengl/tiny_opengl3_app.h @@ -20,6 +20,33 @@ #include "tiny_gl_primitive_renderer.h" #include "tiny_window_interface.h" +struct TinyCudaVbo +{ + int num_instances; + union { + void* positions_ptr; + uint64_t positions_int; + }; + + union { + void* orientations_ptr; + uint64_t orientations_int; + }; + union { + void* colors_ptr; + uint64_t colors_int; + }; + union { + void* scalings_ptr; + uint64_t scalings_int; + }; + + union { + void* vertices_ptr; + uint64_t vertices_int; + }; +}; + struct TinyOpenGL3App : public TinyCommonGraphicsApp { struct TinyOpenGL3AppInternalData* m_data; @@ -44,14 +71,35 @@ struct TinyOpenGL3App : public TinyCommonGraphicsApp { int textureId = -1); virtual int register_graphics_capsule_shape(float radius,float half_height,int up_axis,int textureId); + virtual int register_graphics_cylinder_shape(float radius, float half_height, int up_axis, int textureId, bool flat_caps=true); + virtual void register_grid(int xres, int yres, const ::TINY::TinyVector3f& color0, const ::TINY::TinyVector3f& color1); - void dump_next_frame_to_png(const char* pngFilename); + void dump_next_frame_to_png(const char* pngFilename, bool render_to_texture=true, + int render_width=-1, int render_height=-1); + + uint64_t enable_render_to_texture(int render_width, int render_height); + + uint64_t cuda_malloc(int num_bytes); + void cuda_free(uint64_t cuda_ptr); + + uint64_t cuda_register_texture_image( + uint64_t renderTextureId, bool verbose); + + uint64_t cuda_copy_texture_image(uint64_t cuda_resource_int, + uint64_t dest_memory_int, + int num_bytes, + bool gpu_device, + int w_offset=0, int h_offset=0); + + + TinyCudaVbo cuda_map_vbo(bool verbose); + void cuda_unmap_vbo(); + void dump_frames_to_video(const char* mp4Filename); - virtual void get_screen_pixels(unsigned char* rgbaBuffer, - int bufferSizeInBytes, float* depthBuffer, - int depthBufferSizeInBytes); + virtual void get_screen_pixels(std::vector& rgbaBuffer, + std::vector& depthBuffer); virtual void set_viewport(int width, int height); void draw_grid(DrawGridData data); diff --git a/src/visualizer/opengl/tiny_win32_window.cpp b/src/visualizer/opengl/tiny_win32_window.cpp index e49a50a8..2f4b0901 100644 --- a/src/visualizer/opengl/tiny_win32_window.cpp +++ b/src/visualizer/opengl/tiny_win32_window.cpp @@ -740,6 +740,10 @@ void TinyWin32Window::run_main_loop() {} void TinyWin32Window::start_rendering() { pumpMessage(); } +void TinyWin32Window::pump_messages() { + pumpMessage(); +} + void TinyWin32Window::renderAllObjects() {} void TinyWin32Window::end_rendering() { SwapBuffers(m_data->m_hDC); } diff --git a/src/visualizer/opengl/tiny_win32_window.h b/src/visualizer/opengl/tiny_win32_window.h index 8c13b6d7..af10d6d6 100644 --- a/src/visualizer/opengl/tiny_win32_window.h +++ b/src/visualizer/opengl/tiny_win32_window.h @@ -46,6 +46,8 @@ class TinyWin32Window : public TinyWindowInterface { virtual void start_rendering(); + virtual void pump_messages(); + virtual void renderAllObjects(); virtual void end_rendering(); diff --git a/src/visualizer/opengl/tiny_window_interface.h b/src/visualizer/opengl/tiny_window_interface.h index e70c8df4..0d156e69 100644 --- a/src/visualizer/opengl/tiny_window_interface.h +++ b/src/visualizer/opengl/tiny_window_interface.h @@ -16,6 +16,7 @@ #define TINY_WINDOW_INTERFACE_H #include "tiny_common_callbacks.h" +#include struct TinyWindowConstructionInfo { int m_width; @@ -40,7 +41,7 @@ struct TinyWindowConstructionInfo { class TinyWindowInterface { public: - virtual ~TinyWindowInterface() {} + virtual void create_default_window(int width, int height, const char* title) { TinyWindowConstructionInfo ci(width, height); @@ -57,9 +58,22 @@ class TinyWindowInterface { virtual bool requested_exit() const = 0; virtual void set_request_exit() = 0; + virtual void set_request_exit2() + { + set_request_exit(); + + set_keyboard_callback2(0); + set_wheel_callback2(0); + set_resize_callback2(0); + set_mouse_button_callback2(0); + set_mouse_move_callback2(0); + + } virtual void start_rendering() = 0; + virtual void pump_messages() = 0; + virtual void end_rendering() = 0; virtual bool is_modifier_key_pressed(int key) = 0; @@ -77,6 +91,86 @@ class TinyWindowInterface { virtual void set_wheel_callback(TinyWheelCallback wheelCallback) = 0; virtual TinyWheelCallback get_wheel_callback() = 0; + + + static inline std::function s_keyboard_callback; + + static void bridge_keyboard_callback(int a, int b) + { + if (s_keyboard_callback) + s_keyboard_callback(a,b); + } + + void set_keyboard_callback2(std::function callback) + { + s_keyboard_callback = callback; + set_keyboard_callback(bridge_keyboard_callback); + } + + static inline std::function s_mouse_move_callback; + + static void bridge_mouse_move_callback(float a, float b) + { + if (s_mouse_move_callback) + s_mouse_move_callback(a,b); + } + + + void set_mouse_move_callback2(std::function callback) + { + s_mouse_move_callback = callback; + set_mouse_move_callback( bridge_mouse_move_callback); + } + + static inline std::function s_mouse_button_callback; + + static void bridge_mouse_button_callback(int a,int b,float c, float d) + { + if (s_mouse_button_callback) + s_mouse_button_callback(a,b,c,d); + } + + void set_mouse_button_callback2(std::function callback) + { + s_mouse_button_callback = callback; + set_mouse_button_callback(bridge_mouse_button_callback); + } + + + static inline std::function s_resize_callback; + + static void bridge_resize_callback(float a, float b) + { + if (s_resize_callback) + { + s_resize_callback(a,b); + } + } + + void set_resize_callback2(std::function callback) + { + s_resize_callback = callback; + set_resize_callback( bridge_resize_callback); + } + + + static inline std::function s_wheel_callback; + + static void bridge_wheel_callback(float a, float b) + { + if (s_wheel_callback) + s_wheel_callback(a,b); + } + + void set_wheel_callback2(std::function callback) + { + s_wheel_callback = callback; + set_wheel_callback(bridge_wheel_callback); + } + + virtual ~TinyWindowInterface() { + } + virtual void set_keyboard_callback(TinyKeyboardCallback keyboardCallback) = 0; virtual TinyKeyboardCallback get_keyboard_callback() = 0; diff --git a/src/visualizer/opengl/tiny_x11_opengl_window.cpp b/src/visualizer/opengl/tiny_x11_opengl_window.cpp index 06fc958d..b0f58248 100644 --- a/src/visualizer/opengl/tiny_x11_opengl_window.cpp +++ b/src/visualizer/opengl/tiny_x11_opengl_window.cpp @@ -1041,6 +1041,11 @@ void TinyX11OpenGLWindow::pump_message() { } } +void TinyX11OpenGLWindow::pump_messages() +{ + pump_message(); +} + void TinyX11OpenGLWindow::start_rendering() { pump_message(); diff --git a/src/visualizer/opengl/tiny_x11_opengl_window.h b/src/visualizer/opengl/tiny_x11_opengl_window.h index b88b61fe..c30fe14f 100644 --- a/src/visualizer/opengl/tiny_x11_opengl_window.h +++ b/src/visualizer/opengl/tiny_x11_opengl_window.h @@ -43,6 +43,8 @@ class TinyX11OpenGLWindow : public TinyWindowInterface { virtual void close_window(); virtual void start_rendering(); + + virtual void pump_messages(); virtual void render_all_objects(); diff --git a/src/visualizer/opengl/utils/tiny_logging.cpp b/src/visualizer/opengl/utils/tiny_logging.cpp index d0bd8393..d29b511f 100644 --- a/src/visualizer/opengl/utils/tiny_logging.cpp +++ b/src/visualizer/opengl/utils/tiny_logging.cpp @@ -110,9 +110,22 @@ void TinyOutputErrorMessageVarArgsInternal(const char* str, ...) { (Tinys_errorMessageFunc)(strDebug); va_end(argList); } +#ifdef TINY_ENABLE_NVTX +#include "/usr/local/cuda-11.6/nsight-systems-2021.5.2/target-linux-x64/nvtx/include/nvtx3/nvToolsExt.h" +#endif -void TinyEnterProfileZoneDefault(const char* name) {} -void TinyLeaveProfileZoneDefault() {} +void TinyEnterProfileZoneDefault(const char* name) +{ +#ifdef TINY_ENABLE_NVTX +nvtxRangePushA(name); +#endif +} +void TinyLeaveProfileZoneDefault() +{ +#ifdef TINY_ENABLE_NVTX +nvtxRangePop(); +#endif +} static TinyEnterProfileZoneFunc* Tinys_enterFunc = TinyEnterProfileZoneDefault; static TinyLeaveProfileZoneFunc* Tinys_leaveFunc = TinyLeaveProfileZoneDefault; void TinyEnterProfileZone(const char* name) { (Tinys_enterFunc)(name); } diff --git a/third_party/crossguid/CMakeLists.txt b/third_party/crossguid/CMakeLists.txt index c22b4b0c..0178a67a 100644 --- a/third_party/crossguid/CMakeLists.txt +++ b/third_party/crossguid/CMakeLists.txt @@ -9,8 +9,9 @@ IF(APPLE) target_compile_definitions(crossguid PUBLIC GUID_CFUUID PRIVATE GGUID_CFUUID ) target_link_libraries(crossguid PUBLIC ${CoreFoundation} PRIVATE ${CoreFoundation}) ELSE() - target_compile_definitions(crossguid PUBLIC GUID_LIBUUID PRIVATE GUID_LIBUUID) - target_link_libraries(crossguid PUBLIC uuid PRIVATE uuid) + target_compile_definitions(crossguid PUBLIC GUID_CUSTOM PRIVATE GUID_CUSTOM) + #target_compile_definitions(crossguid PUBLIC GUID_LIBUUID PRIVATE GUID_LIBUUID) + #target_link_libraries(crossguid PUBLIC uuid PRIVATE uuid) ENDIF() ENDIF() diff --git a/third_party/crossguid/guid.cpp b/third_party/crossguid/guid.cpp index ea28e0a4..32c74b50 100644 --- a/third_party/crossguid/guid.cpp +++ b/third_party/crossguid/guid.cpp @@ -29,6 +29,24 @@ THE SOFTWARE. #include #endif +#ifdef GUID_CUSTOM +#include +#include + +namespace uuid { + static std::random_device rd; + static std::mt19937 gen(rd()); + static std::uniform_int_distribution<> dis(0, 15); + void generate_uuid_v4(std::array& data) { + for (int i=0;i #endif @@ -239,6 +257,19 @@ Guid newGuid() } #endif +#ifdef GUID_CUSTOM + +Guid newGuid() +{ + std::array data; + uuid::generate_uuid_v4(data); + return Guid{std::move(data)}; +} + + +#endif + + // this is the mac and ios version #ifdef GUID_CFUUID Guid newGuid() diff --git a/third_party/pybind11/include/pybind11/attr.h b/third_party/pybind11/include/pybind11/attr.h index 6962d6fc..db7cd8ef 100644 --- a/third_party/pybind11/include/pybind11/attr.h +++ b/third_party/pybind11/include/pybind11/attr.h @@ -10,65 +10,113 @@ #pragma once +#include "detail/common.h" #include "cast.h" -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +#include + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) /// \addtogroup annotations /// @{ /// Annotation for methods -struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; +struct is_method { + handle class_; + explicit is_method(const handle &c) : class_(c) {} +}; /// Annotation for operators -struct is_operator { }; +struct is_operator {}; + +/// Annotation for classes that cannot be subclassed +struct is_final {}; /// Annotation for parent scope -struct scope { handle value; scope(const handle &s) : value(s) { } }; +struct scope { + handle value; + explicit scope(const handle &s) : value(s) {} +}; /// Annotation for documentation -struct doc { const char *value; doc(const char *value) : value(value) { } }; +struct doc { + const char *value; + explicit doc(const char *value) : value(value) {} +}; /// Annotation for function names -struct name { const char *value; name(const char *value) : value(value) { } }; +struct name { + const char *value; + explicit name(const char *value) : value(value) {} +}; /// Annotation indicating that a function is an overload associated with a given "sibling" -struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } }; +struct sibling { + handle value; + explicit sibling(const handle &value) : value(value.ptr()) {} +}; /// Annotation indicating that a class derives from another given type -template struct base { - PYBIND11_DEPRECATED("base() was deprecated in favor of specifying 'T' as a template argument to class_") - base() { } +template +struct base { + + PYBIND11_DEPRECATED( + "base() was deprecated in favor of specifying 'T' as a template argument to class_") + base() = default; }; /// Keep patient alive while nurse lives -template struct keep_alive { }; +template +struct keep_alive {}; /// Annotation indicating that a class is involved in a multiple inheritance relationship -struct multiple_inheritance { }; +struct multiple_inheritance {}; /// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class -struct dynamic_attr { }; +struct dynamic_attr {}; /// Annotation which enables the buffer protocol for a type -struct buffer_protocol { }; +struct buffer_protocol {}; /// Annotation which requests that a special metaclass is created for a type struct metaclass { handle value; PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") - metaclass() {} + metaclass() = default; /// Override pybind11's default metaclass - explicit metaclass(handle value) : value(value) { } + explicit metaclass(handle value) : value(value) {} +}; + +/// Specifies a custom callback with signature `void (PyHeapTypeObject*)` that +/// may be used to customize the Python type. +/// +/// The callback is invoked immediately before `PyType_Ready`. +/// +/// Note: This is an advanced interface, and uses of it may require changes to +/// work with later versions of pybind11. You may wish to consult the +/// implementation of `make_new_python_type` in `detail/classes.h` to understand +/// the context in which the callback will be run. +struct custom_type_setup { + using callback = std::function; + + explicit custom_type_setup(callback value) : value(std::move(value)) {} + + callback value; }; /// Annotation that marks a class as local to the module: -struct module_local { const bool value; constexpr module_local(bool v = true) : value(v) { } }; +struct module_local { + const bool value; + constexpr explicit module_local(bool v = true) : value(v) {} +}; /// Annotation to mark enums as an arithmetic type -struct arithmetic { }; +struct arithmetic {}; + +/// Mark a function for addition at the beginning of the existing overload chain instead of the end +struct prepend {}; /** \rst A call policy which places one or more guard variables (``Ts...``) around the function call. @@ -88,9 +136,13 @@ struct arithmetic { }; return foo(args...); // forwarded arguments }); \endrst */ -template struct call_guard; +template +struct call_guard; -template <> struct call_guard<> { using type = detail::void_type; }; +template <> +struct call_guard<> { + using type = detail::void_type; +}; template struct call_guard { @@ -110,13 +162,14 @@ struct call_guard { /// @} annotations -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) /* Forward declarations */ enum op_id : int; enum op_type : int; struct undefined_t; -template struct op_; -inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); +template +struct op_; +void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); /// Internal data structure which holds metadata about a keyword argument struct argument_record { @@ -127,14 +180,16 @@ struct argument_record { bool none : 1; ///< True if None is allowed when loading argument_record(const char *name, const char *descr, handle value, bool convert, bool none) - : name(name), descr(descr), value(value), convert(convert), none(none) { } + : name(name), descr(descr), value(value), convert(convert), none(none) {} }; -/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.) +/// Internal data structure which holds metadata about a bound function (signature, overloads, +/// etc.) struct function_record { function_record() : is_constructor(false), is_new_style_constructor(false), is_stateless(false), - is_operator(false), has_args(false), has_kwargs(false), is_method(false) { } + is_operator(false), is_method(false), has_args(false), has_kwargs(false), + prepend(false) {} /// Function name char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ @@ -149,13 +204,13 @@ struct function_record { std::vector args; /// Pointer to lambda function which converts arguments and performs the actual call - handle (*impl) (function_call &) = nullptr; + handle (*impl)(function_call &) = nullptr; /// Storage for the wrapped function pointer and captured data, if any - void *data[3] = { }; + void *data[3] = {}; /// Pointer to custom destructor for 'data' (if needed) - void (*free_data) (function_record *ptr) = nullptr; + void (*free_data)(function_record *ptr) = nullptr; /// Return value policy associated with this function return_value_policy policy = return_value_policy::automatic; @@ -172,18 +227,28 @@ struct function_record { /// True if this is an operator (__add__), etc. bool is_operator : 1; + /// True if this is a method + bool is_method : 1; + /// True if the function has a '*args' argument bool has_args : 1; /// True if the function has a '**kwargs' argument bool has_kwargs : 1; - /// True if this is a method - bool is_method : 1; + /// True if this function is to be inserted at the beginning of the overload resolution chain + bool prepend : 1; /// Number of arguments (including py::args and/or py::kwargs, if present) std::uint16_t nargs; + /// Number of leading positional arguments, which are terminated by a py::args or py::kwargs + /// argument or by a py::kw_only annotation. + std::uint16_t nargs_pos = 0; + + /// Number of leading arguments (counted in `nargs`) that are positional-only + std::uint16_t nargs_pos_only = 0; + /// Python method object PyMethodDef *def = nullptr; @@ -201,7 +266,7 @@ struct function_record { struct type_record { PYBIND11_NOINLINE type_record() : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), - default_holder(true), module_local(false) { } + default_holder(true), module_local(false), is_final(false) {} /// Handle to the parent scope handle scope; @@ -239,6 +304,9 @@ struct type_record { /// Custom metaclass (optional) handle metaclass; + /// Custom type setup. + custom_type_setup::callback custom_type_setup_callback; + /// Multiple inheritance marker bool multiple_inheritance : 1; @@ -254,42 +322,48 @@ struct type_record { /// Is the class definition local to the module shared object? bool module_local : 1; - PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) { - auto base_info = detail::get_type_info(base, false); + /// Is the class inheritable from python classes? + bool is_final : 1; + + PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) { + auto *base_info = detail::get_type_info(base, false); if (!base_info) { std::string tname(base.name()); detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) + - "\" referenced unknown base type \"" + tname + "\""); + pybind11_fail("generic_type: type \"" + std::string(name) + + "\" referenced unknown base type \"" + tname + "\""); } if (default_holder != base_info->default_holder) { std::string tname(base.name()); detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + - (default_holder ? "does not have" : "has") + - " a non-default holder type while its base \"" + tname + "\" " + - (base_info->default_holder ? "does not" : "does")); + pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + + (default_holder ? "does not have" : "has") + + " a non-default holder type while its base \"" + tname + "\" " + + (base_info->default_holder ? "does not" : "does")); } bases.append((PyObject *) base_info->type); - if (base_info->type->tp_dictoffset != 0) - dynamic_attr = true; +#if PY_VERSION_HEX < 0x030B0000 + dynamic_attr |= base_info->type->tp_dictoffset != 0; +#else + dynamic_attr |= (base_info->type->tp_flags & Py_TPFLAGS_MANAGED_DICT) != 0; +#endif - if (caster) + if (caster) { base_info->implicit_casts.emplace_back(type, caster); + } } }; -inline function_call::function_call(const function_record &f, handle p) : - func(f), parent(p) { +inline function_call::function_call(const function_record &f, handle p) : func(f), parent(p) { args.reserve(f.nargs); args_convert.reserve(f.nargs); } /// Tag for a new-style `__init__` defined in `detail/init.h` -struct is_new_style_constructor { }; +struct is_new_style_constructor {}; /** * Partial template specializations to process custom attributes provided to @@ -297,105 +371,177 @@ struct is_new_style_constructor { }; * fields in the type_record and function_record data structures or executed at * runtime to deal with custom call policies (e.g. keep_alive). */ -template struct process_attribute; +template +struct process_attribute; -template struct process_attribute_default { +template +struct process_attribute_default { /// Default implementation: do nothing - static void init(const T &, function_record *) { } - static void init(const T &, type_record *) { } - static void precall(function_call &) { } - static void postcall(function_call &, handle) { } + static void init(const T &, function_record *) {} + static void init(const T &, type_record *) {} + static void precall(function_call &) {} + static void postcall(function_call &, handle) {} }; /// Process an attribute specifying the function's name -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } }; /// Process an attribute specifying the function's docstring -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } }; /// Process an attribute specifying the function's docstring (provided as a C-style string) -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const char *d, function_record *r) { r->doc = const_cast(d); } static void init(const char *d, type_record *r) { r->doc = const_cast(d); } }; -template <> struct process_attribute : process_attribute { }; +template <> +struct process_attribute : process_attribute {}; /// Process an attribute indicating the function's return value policy -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const return_value_policy &p, function_record *r) { r->policy = p; } }; -/// Process an attribute which indicates that this is an overloaded function associated with a given sibling -template <> struct process_attribute : process_attribute_default { +/// Process an attribute which indicates that this is an overloaded function associated with a +/// given sibling +template <> +struct process_attribute : process_attribute_default { static void init(const sibling &s, function_record *r) { r->sibling = s.value; } }; /// Process an attribute which indicates that this function is a method -template <> struct process_attribute : process_attribute_default { - static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; } +template <> +struct process_attribute : process_attribute_default { + static void init(const is_method &s, function_record *r) { + r->is_method = true; + r->scope = s.class_; + } }; /// Process an attribute which indicates the parent scope of a method -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const scope &s, function_record *r) { r->scope = s.value; } }; /// Process an attribute which indicates that this function is an operator -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const is_operator &, function_record *r) { r->is_operator = true; } }; -template <> struct process_attribute : process_attribute_default { - static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; } +template <> +struct process_attribute + : process_attribute_default { + static void init(const is_new_style_constructor &, function_record *r) { + r->is_new_style_constructor = true; + } }; +inline void check_kw_only_arg(const arg &a, function_record *r) { + if (r->args.size() > r->nargs_pos && (!a.name || a.name[0] == '\0')) { + pybind11_fail("arg(): cannot specify an unnamed argument after a kw_only() annotation or " + "args() argument"); + } +} + +inline void append_self_arg_if_needed(function_record *r) { + if (r->is_method && r->args.empty()) { + r->args.emplace_back("self", nullptr, handle(), /*convert=*/true, /*none=*/false); + } +} + /// Process a keyword argument attribute (*without* a default value) -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const arg &a, function_record *r) { - if (r->is_method && r->args.empty()) - r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/); + append_self_arg_if_needed(r); r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); + + check_kw_only_arg(a, r); } }; /// Process a keyword argument attribute (*with* a default value) -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const arg_v &a, function_record *r) { - if (r->is_method && r->args.empty()) - r->args.emplace_back("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/); + if (r->is_method && r->args.empty()) { + r->args.emplace_back( + "self", /*descr=*/nullptr, /*parent=*/handle(), /*convert=*/true, /*none=*/false); + } if (!a.value) { -#if !defined(NDEBUG) +#if defined(PYBIND11_DETAILED_ERROR_MESSAGES) std::string descr("'"); - if (a.name) descr += std::string(a.name) + ": "; + if (a.name) { + descr += std::string(a.name) + ": "; + } descr += a.type + "'"; if (r->is_method) { - if (r->name) - descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; - else + if (r->name) { + descr += " in method '" + (std::string) str(r->scope) + "." + + (std::string) r->name + "'"; + } else { descr += " in method of '" + (std::string) str(r->scope) + "'"; + } } else if (r->name) { descr += " in function '" + (std::string) r->name + "'"; } - pybind11_fail("arg(): could not convert default argument " - + descr + " into a Python object (type not registered yet?)"); + pybind11_fail("arg(): could not convert default argument " + descr + + " into a Python object (type not registered yet?)"); #else pybind11_fail("arg(): could not convert default argument " "into a Python object (type not registered yet?). " - "Compile in debug mode for more information."); + "#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for " + "more information."); #endif } r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); + + check_kw_only_arg(a, r); + } +}; + +/// Process a keyword-only-arguments-follow pseudo argument +template <> +struct process_attribute : process_attribute_default { + static void init(const kw_only &, function_record *r) { + append_self_arg_if_needed(r); + if (r->has_args && r->nargs_pos != static_cast(r->args.size())) { + pybind11_fail("Mismatched args() and kw_only(): they must occur at the same relative " + "argument location (or omit kw_only() entirely)"); + } + r->nargs_pos = static_cast(r->args.size()); + } +}; + +/// Process a positional-only-argument maker +template <> +struct process_attribute : process_attribute_default { + static void init(const pos_only &, function_record *r) { + append_self_arg_if_needed(r); + r->nargs_pos_only = static_cast(r->args.size()); + if (r->nargs_pos_only > r->nargs_pos) { + pybind11_fail("pos_only(): cannot follow a py::args() argument"); + } + // It also can't follow a kw_only, but a static_assert in pybind11.h checks that } }; -/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that) +/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees +/// that) template -struct process_attribute::value>> : process_attribute_default { +struct process_attribute::value>> + : process_attribute_default { static void init(const handle &h, type_record *r) { r->bases.append(h); } }; @@ -408,7 +554,9 @@ struct process_attribute> : process_attribute_default> { /// Process a multiple inheritance attribute template <> struct process_attribute : process_attribute_default { - static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; } + static void init(const multiple_inheritance &, type_record *r) { + r->multiple_inheritance = true; + } }; template <> @@ -416,6 +564,18 @@ struct process_attribute : process_attribute_default static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } }; +template <> +struct process_attribute { + static void init(const custom_type_setup &value, type_record *r) { + r->custom_type_setup_callback = value.value; + } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const is_final &, type_record *r) { r->is_final = true; } +}; + template <> struct process_attribute : process_attribute_default { static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; } @@ -431,46 +591,70 @@ struct process_attribute : process_attribute_default static void init(const module_local &l, type_record *r) { r->module_local = l.value; } }; +/// Process a 'prepend' attribute, putting this at the beginning of the overload chain +template <> +struct process_attribute : process_attribute_default { + static void init(const prepend &, function_record *r) { r->prepend = true; } +}; + /// Process an 'arithmetic' attribute for enums (does nothing here) template <> struct process_attribute : process_attribute_default {}; template -struct process_attribute> : process_attribute_default> { }; +struct process_attribute> : process_attribute_default> {}; /** * Process a keep_alive call policy -- invokes keep_alive_impl during the * pre-call handler if both Nurse, Patient != 0 and use the post-call handler * otherwise */ -template struct process_attribute> : public process_attribute_default> { +template +struct process_attribute> + : public process_attribute_default> { template = 0> - static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); } + static void precall(function_call &call) { + keep_alive_impl(Nurse, Patient, call, handle()); + } template = 0> - static void postcall(function_call &, handle) { } + static void postcall(function_call &, handle) {} template = 0> - static void precall(function_call &) { } + static void precall(function_call &) {} template = 0> - static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); } + static void postcall(function_call &call, handle ret) { + keep_alive_impl(Nurse, Patient, call, ret); + } }; /// Recursively iterate over variadic template arguments -template struct process_attributes { - static void init(const Args&... args, function_record *r) { - int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; - ignore_unused(unused); +template +struct process_attributes { + static void init(const Args &...args, function_record *r) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); + using expander = int[]; + (void) expander{ + 0, ((void) process_attribute::type>::init(args, r), 0)...}; } - static void init(const Args&... args, type_record *r) { - int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; - ignore_unused(unused); + static void init(const Args &...args, type_record *r) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); + using expander = int[]; + (void) expander{0, + (process_attribute::type>::init(args, r), 0)...}; } static void precall(function_call &call) { - int unused[] = { 0, (process_attribute::type>::precall(call), 0) ... }; - ignore_unused(unused); + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call); + using expander = int[]; + (void) expander{0, + (process_attribute::type>::precall(call), 0)...}; } static void postcall(function_call &call, handle fn_ret) { - int unused[] = { 0, (process_attribute::type>::postcall(call, fn_ret), 0) ... }; - ignore_unused(unused); + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call, fn_ret); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(fn_ret); + using expander = int[]; + (void) expander{ + 0, (process_attribute::type>::postcall(call, fn_ret), 0)...}; } }; @@ -484,10 +668,11 @@ using extract_guard_t = typename exactly_one_t, Extr /// Check the number of named arguments at compile time template ::value...), - size_t self = constexpr_sum(std::is_same::value...)> + size_t self = constexpr_sum(std::is_same::value...)> constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { - return named == 0 || (self + named + has_args + has_kwargs) == nargs; + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs); + return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs; } -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/third_party/pybind11/include/pybind11/buffer_info.h b/third_party/pybind11/include/pybind11/buffer_info.h index 1f4115a1..06120d55 100644 --- a/third_party/pybind11/include/pybind11/buffer_info.h +++ b/third_party/pybind11/include/pybind11/buffer_info.h @@ -11,61 +11,122 @@ #include "detail/common.h" -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +PYBIND11_NAMESPACE_BEGIN(detail) + +// Default, C-style strides +inline std::vector c_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + if (ndim > 0) { + for (size_t i = ndim - 1; i > 0; --i) { + strides[i - 1] = strides[i] * shape[i]; + } + } + return strides; +} + +// F-style strides; default when constructing an array_t with `ExtraFlags & f_style` +inline std::vector f_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + for (size_t i = 1; i < ndim; ++i) { + strides[i] = strides[i - 1] * shape[i - 1]; + } + return strides; +} + +PYBIND11_NAMESPACE_END(detail) /// Information record describing a Python buffer object struct buffer_info { void *ptr = nullptr; // Pointer to the underlying storage ssize_t itemsize = 0; // Size of individual items in bytes ssize_t size = 0; // Total number of entries - std::string format; // For homogeneous buffers, this should be set to format_descriptor::format() + std::string format; // For homogeneous buffers, this should be set to + // format_descriptor::format() ssize_t ndim = 0; // Number of dimensions std::vector shape; // Shape of the tensor (1 entry per dimension) - std::vector strides; // Number of bytes between adjacent entries (for each per dimension) + std::vector strides; // Number of bytes between adjacent entries + // (for each per dimension) bool readonly = false; // flag to indicate if the underlying storage may be written to - buffer_info() { } - - buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, - detail::any_container shape_in, detail::any_container strides_in, bool readonly=false) - : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), - shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) { - if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) + buffer_info() = default; + + buffer_info(void *ptr, + ssize_t itemsize, + const std::string &format, + ssize_t ndim, + detail::any_container shape_in, + detail::any_container strides_in, + bool readonly = false) + : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), + shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) { + if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) { pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); - for (size_t i = 0; i < (size_t) ndim; ++i) + } + for (size_t i = 0; i < (size_t) ndim; ++i) { size *= shape[i]; + } } template - buffer_info(T *ptr, detail::any_container shape_in, detail::any_container strides_in, bool readonly=false) - : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor::format(), static_cast(shape_in->size()), std::move(shape_in), std::move(strides_in), readonly) { } - - buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size, bool readonly=false) - : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) { } + buffer_info(T *ptr, + detail::any_container shape_in, + detail::any_container strides_in, + bool readonly = false) + : buffer_info(private_ctr_tag(), + ptr, + sizeof(T), + format_descriptor::format(), + static_cast(shape_in->size()), + std::move(shape_in), + std::move(strides_in), + readonly) {} + + buffer_info(void *ptr, + ssize_t itemsize, + const std::string &format, + ssize_t size, + bool readonly = false) + : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) {} template - buffer_info(T *ptr, ssize_t size, bool readonly=false) - : buffer_info(ptr, sizeof(T), format_descriptor::format(), size, readonly) { } + buffer_info(T *ptr, ssize_t size, bool readonly = false) + : buffer_info(ptr, sizeof(T), format_descriptor::format(), size, readonly) {} template - buffer_info(const T *ptr, ssize_t size, bool readonly=true) - : buffer_info(const_cast(ptr), sizeof(T), format_descriptor::format(), size, readonly) { } + buffer_info(const T *ptr, ssize_t size, bool readonly = true) + : buffer_info( + const_cast(ptr), sizeof(T), format_descriptor::format(), size, readonly) {} explicit buffer_info(Py_buffer *view, bool ownview = true) - : buffer_info(view->buf, view->itemsize, view->format, view->ndim, - {view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}, view->readonly) { - this->view = view; + : buffer_info( + view->buf, + view->itemsize, + view->format, + view->ndim, + {view->shape, view->shape + view->ndim}, + /* Though buffer::request() requests PyBUF_STRIDES, ctypes objects + * ignore this flag and return a view with NULL strides. + * When strides are NULL, build them manually. */ + view->strides + ? std::vector(view->strides, view->strides + view->ndim) + : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize), + (view->readonly != 0)) { + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + this->m_view = view; + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) this->ownview = ownview; } buffer_info(const buffer_info &) = delete; - buffer_info& operator=(const buffer_info &) = delete; + buffer_info &operator=(const buffer_info &) = delete; - buffer_info(buffer_info &&other) { - (*this) = std::move(other); - } + buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); } - buffer_info& operator=(buffer_info &&rhs) { + buffer_info &operator=(buffer_info &&rhs) noexcept { ptr = rhs.ptr; itemsize = rhs.itemsize; size = rhs.size; @@ -73,42 +134,60 @@ struct buffer_info { ndim = rhs.ndim; shape = std::move(rhs.shape); strides = std::move(rhs.strides); - std::swap(view, rhs.view); + std::swap(m_view, rhs.m_view); std::swap(ownview, rhs.ownview); readonly = rhs.readonly; return *this; } ~buffer_info() { - if (view && ownview) { PyBuffer_Release(view); delete view; } + if (m_view && ownview) { + PyBuffer_Release(m_view); + delete m_view; + } } -private: - struct private_ctr_tag { }; - - buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, - detail::any_container &&shape_in, detail::any_container &&strides_in, bool readonly) - : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) { } + Py_buffer *view() const { return m_view; } + Py_buffer *&view() { return m_view; } - Py_buffer *view = nullptr; +private: + struct private_ctr_tag {}; + + buffer_info(private_ctr_tag, + void *ptr, + ssize_t itemsize, + const std::string &format, + ssize_t ndim, + detail::any_container &&shape_in, + detail::any_container &&strides_in, + bool readonly) + : buffer_info( + ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) {} + + Py_buffer *m_view = nullptr; bool ownview = false; }; -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) -template struct compare_buffer_info { - static bool compare(const buffer_info& b) { +template +struct compare_buffer_info { + static bool compare(const buffer_info &b) { return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); } }; -template struct compare_buffer_info::value>> { - static bool compare(const buffer_info& b) { - return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor::value || - ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned::value ? "L" : "l")) || - ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned::value ? "N" : "n"))); +template +struct compare_buffer_info::value>> { + static bool compare(const buffer_info &b) { + return (size_t) b.itemsize == sizeof(T) + && (b.format == format_descriptor::value + || ((sizeof(T) == sizeof(long)) + && b.format == (std::is_unsigned::value ? "L" : "l")) + || ((sizeof(T) == sizeof(size_t)) + && b.format == (std::is_unsigned::value ? "N" : "n"))); } }; -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/third_party/pybind11/include/pybind11/cast.h b/third_party/pybind11/include/pybind11/cast.h index a0b4d1ba..3a404602 100644 --- a/third_party/pybind11/include/pybind11/cast.h +++ b/third_party/pybind11/include/pybind11/cast.h @@ -10,1042 +10,187 @@ #pragma once -#include "pytypes.h" -#include "detail/typeid.h" +#include "detail/common.h" #include "detail/descr.h" -#include "detail/internals.h" +#include "detail/type_caster_base.h" +#include "detail/typeid.h" +#include "pytypes.h" + #include -#include +#include +#include +#include +#include +#include +#include #include #include +#include +#include -#if defined(PYBIND11_CPP17) -# if defined(__has_include) -# if __has_include() -# define PYBIND11_HAS_STRING_VIEW -# endif -# elif defined(_MSC_VER) -# define PYBIND11_HAS_STRING_VIEW -# endif -#endif -#ifdef PYBIND11_HAS_STRING_VIEW -#include -#endif - -#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201811L -# define PYBIND11_HAS_U8STRING -#endif - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -/// A life support system for temporary objects created by `type_caster::load()`. -/// Adding a patient will keep it alive up until the enclosing function returns. -class loader_life_support { -public: - /// A new patient frame is created when a function is entered - loader_life_support() { - get_internals().loader_patient_stack.push_back(nullptr); - } - - /// ... and destroyed after it returns - ~loader_life_support() { - auto &stack = get_internals().loader_patient_stack; - if (stack.empty()) - pybind11_fail("loader_life_support: internal error"); - - auto ptr = stack.back(); - stack.pop_back(); - Py_CLEAR(ptr); - - // A heuristic to reduce the stack's capacity (e.g. after long recursive calls) - if (stack.capacity() > 16 && stack.size() != 0 && stack.capacity() / stack.size() > 2) - stack.shrink_to_fit(); - } - - /// This can only be used inside a pybind11-bound function, either by `argument_loader` - /// at argument preparation time or by `py::cast()` at execution time. - PYBIND11_NOINLINE static void add_patient(handle h) { - auto &stack = get_internals().loader_patient_stack; - if (stack.empty()) - throw cast_error("When called outside a bound function, py::cast() cannot " - "do Python -> C++ conversions which require the creation " - "of temporary values"); - - auto &list_ptr = stack.back(); - if (list_ptr == nullptr) { - list_ptr = PyList_New(1); - if (!list_ptr) - pybind11_fail("loader_life_support: error allocating list"); - PyList_SET_ITEM(list_ptr, 0, h.inc_ref().ptr()); - } else { - auto result = PyList_Append(list_ptr, h.ptr()); - if (result == -1) - pybind11_fail("loader_life_support: error adding patient"); - } - } -}; - -// Gets the cache entry for the given type, creating it if necessary. The return value is the pair -// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was -// just created. -inline std::pair all_type_info_get_cache(PyTypeObject *type); - -// Populates a just-created cache entry. -PYBIND11_NOINLINE inline void all_type_info_populate(PyTypeObject *t, std::vector &bases) { - std::vector check; - for (handle parent : reinterpret_borrow(t->tp_bases)) - check.push_back((PyTypeObject *) parent.ptr()); - - auto const &type_dict = get_internals().registered_types_py; - for (size_t i = 0; i < check.size(); i++) { - auto type = check[i]; - // Ignore Python2 old-style class super type: - if (!PyType_Check((PyObject *) type)) continue; - - // Check `type` in the current set of registered python types: - auto it = type_dict.find(type); - if (it != type_dict.end()) { - // We found a cache entry for it, so it's either pybind-registered or has pre-computed - // pybind bases, but we have to make sure we haven't already seen the type(s) before: we - // want to follow Python/virtual C++ rules that there should only be one instance of a - // common base. - for (auto *tinfo : it->second) { - // NB: Could use a second set here, rather than doing a linear search, but since - // having a large number of immediate pybind11-registered types seems fairly - // unlikely, that probably isn't worthwhile. - bool found = false; - for (auto *known : bases) { - if (known == tinfo) { found = true; break; } - } - if (!found) bases.push_back(tinfo); - } - } - else if (type->tp_bases) { - // It's some python type, so keep follow its bases classes to look for one or more - // registered types - if (i + 1 == check.size()) { - // When we're at the end, we can pop off the current element to avoid growing - // `check` when adding just one base (which is typical--i.e. when there is no - // multiple inheritance) - check.pop_back(); - i--; - } - for (handle parent : reinterpret_borrow(type->tp_bases)) - check.push_back((PyTypeObject *) parent.ptr()); - } - } -} - -/** - * Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will - * be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side - * derived class that uses single inheritance. Will contain as many types as required for a Python - * class that uses multiple inheritance to inherit (directly or indirectly) from multiple - * pybind-registered classes. Will be empty if neither the type nor any base classes are - * pybind-registered. - * - * The value is cached for the lifetime of the Python type. - */ -inline const std::vector &all_type_info(PyTypeObject *type) { - auto ins = all_type_info_get_cache(type); - if (ins.second) - // New cache entry: populate it - all_type_info_populate(type, ins.first->second); - - return ins.first->second; -} - -/** - * Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any - * ancestors are pybind11-registered. Throws an exception if there are multiple bases--use - * `all_type_info` instead if you want to support multiple bases. - */ -PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) { - auto &bases = all_type_info(type); - if (bases.size() == 0) - return nullptr; - if (bases.size() > 1) - pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases"); - return bases.front(); -} - -inline detail::type_info *get_local_type_info(const std::type_index &tp) { - auto &locals = registered_local_types_cpp(); - auto it = locals.find(tp); - if (it != locals.end()) - return it->second; - return nullptr; -} - -inline detail::type_info *get_global_type_info(const std::type_index &tp) { - auto &types = get_internals().registered_types_cpp; - auto it = types.find(tp); - if (it != types.end()) - return it->second; - return nullptr; -} - -/// Return the type info for a given C++ type; on lookup failure can either throw or return nullptr. -PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_index &tp, - bool throw_if_missing = false) { - if (auto ltype = get_local_type_info(tp)) - return ltype; - if (auto gtype = get_global_type_info(tp)) - return gtype; - - if (throw_if_missing) { - std::string tname = tp.name(); - detail::clean_type_id(tname); - pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\""); - } - return nullptr; -} - -PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool throw_if_missing) { - detail::type_info *type_info = get_type_info(tp, throw_if_missing); - return handle(type_info ? ((PyObject *) type_info->type) : nullptr); -} - -struct value_and_holder { - instance *inst = nullptr; - size_t index = 0u; - const detail::type_info *type = nullptr; - void **vh = nullptr; - - // Main constructor for a found value/holder: - value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) : - inst{i}, index{index}, type{type}, - vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]} - {} - - // Default constructor (used to signal a value-and-holder not found by get_value_and_holder()) - value_and_holder() {} - - // Used for past-the-end iterator - value_and_holder(size_t index) : index{index} {} - - template V *&value_ptr() const { - return reinterpret_cast(vh[0]); - } - // True if this `value_and_holder` has a non-null value pointer - explicit operator bool() const { return value_ptr(); } - - template H &holder() const { - return reinterpret_cast(vh[1]); - } - bool holder_constructed() const { - return inst->simple_layout - ? inst->simple_holder_constructed - : inst->nonsimple.status[index] & instance::status_holder_constructed; - } - void set_holder_constructed(bool v = true) { - if (inst->simple_layout) - inst->simple_holder_constructed = v; - else if (v) - inst->nonsimple.status[index] |= instance::status_holder_constructed; - else - inst->nonsimple.status[index] &= (uint8_t) ~instance::status_holder_constructed; - } - bool instance_registered() const { - return inst->simple_layout - ? inst->simple_instance_registered - : inst->nonsimple.status[index] & instance::status_instance_registered; - } - void set_instance_registered(bool v = true) { - if (inst->simple_layout) - inst->simple_instance_registered = v; - else if (v) - inst->nonsimple.status[index] |= instance::status_instance_registered; - else - inst->nonsimple.status[index] &= (uint8_t) ~instance::status_instance_registered; - } -}; - -// Container for accessing and iterating over an instance's values/holders -struct values_and_holders { -private: - instance *inst; - using type_vec = std::vector; - const type_vec &tinfo; - -public: - values_and_holders(instance *inst) : inst{inst}, tinfo(all_type_info(Py_TYPE(inst))) {} - - struct iterator { - private: - instance *inst = nullptr; - const type_vec *types = nullptr; - value_and_holder curr; - friend struct values_and_holders; - iterator(instance *inst, const type_vec *tinfo) - : inst{inst}, types{tinfo}, - curr(inst /* instance */, - types->empty() ? nullptr : (*types)[0] /* type info */, - 0, /* vpos: (non-simple types only): the first vptr comes first */ - 0 /* index */) - {} - // Past-the-end iterator: - iterator(size_t end) : curr(end) {} - public: - bool operator==(const iterator &other) { return curr.index == other.curr.index; } - bool operator!=(const iterator &other) { return curr.index != other.curr.index; } - iterator &operator++() { - if (!inst->simple_layout) - curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs; - ++curr.index; - curr.type = curr.index < types->size() ? (*types)[curr.index] : nullptr; - return *this; - } - value_and_holder &operator*() { return curr; } - value_and_holder *operator->() { return &curr; } - }; - - iterator begin() { return iterator(inst, &tinfo); } - iterator end() { return iterator(tinfo.size()); } - - iterator find(const type_info *find_type) { - auto it = begin(), endit = end(); - while (it != endit && it->type != find_type) ++it; - return it; - } - - size_t size() { return tinfo.size(); } -}; - -/** - * Extracts C++ value and holder pointer references from an instance (which may contain multiple - * values/holders for python-side multiple inheritance) that match the given type. Throws an error - * if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If - * `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned, - * regardless of type (and the resulting .type will be nullptr). - * - * The returned object should be short-lived: in particular, it must not outlive the called-upon - * instance. - */ -PYBIND11_NOINLINE inline value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/, bool throw_if_missing /*= true in common.h*/) { - // Optimize common case: - if (!find_type || Py_TYPE(this) == find_type->type) - return value_and_holder(this, find_type, 0, 0); - - detail::values_and_holders vhs(this); - auto it = vhs.find(find_type); - if (it != vhs.end()) - return *it; - - if (!throw_if_missing) - return value_and_holder(); - -#if defined(NDEBUG) - pybind11_fail("pybind11::detail::instance::get_value_and_holder: " - "type is not a pybind11 base of the given instance " - "(compile in debug mode for type details)"); -#else - pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" + - std::string(find_type->type->tp_name) + "' is not a pybind11 base of the given `" + - std::string(Py_TYPE(this)->tp_name) + "' instance"); -#endif -} - -PYBIND11_NOINLINE inline void instance::allocate_layout() { - auto &tinfo = all_type_info(Py_TYPE(this)); - - const size_t n_types = tinfo.size(); - - if (n_types == 0) - pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types"); - - simple_layout = - n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs(); - - // Simple path: no python-side multiple inheritance, and a small-enough holder - if (simple_layout) { - simple_value_holder[0] = nullptr; - simple_holder_constructed = false; - simple_instance_registered = false; - } - else { // multiple base types or a too-large holder - // Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer, - // [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool - // values that tracks whether each associated holder has been initialized. Each [block] is - // padded, if necessary, to an integer multiple of sizeof(void *). - size_t space = 0; - for (auto t : tinfo) { - space += 1; // value pointer - space += t->holder_size_in_ptrs; // holder instance - } - size_t flags_at = space; - space += size_in_ptrs(n_types); // status bytes (holder_constructed and instance_registered) - - // Allocate space for flags, values, and holders, and initialize it to 0 (flags and values, - // in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6 - // they default to using pymalloc, which is designed to be efficient for small allocations - // like the one we're doing here; in earlier versions (and for larger allocations) they are - // just wrappers around malloc. -#if PY_VERSION_HEX >= 0x03050000 - nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); - if (!nonsimple.values_and_holders) throw std::bad_alloc(); -#else - nonsimple.values_and_holders = (void **) PyMem_New(void *, space); - if (!nonsimple.values_and_holders) throw std::bad_alloc(); - std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *)); -#endif - nonsimple.status = reinterpret_cast(&nonsimple.values_and_holders[flags_at]); - } - owned = true; -} - -PYBIND11_NOINLINE inline void instance::deallocate_layout() { - if (!simple_layout) - PyMem_Free(nonsimple.values_and_holders); -} - -PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) { - handle type = detail::get_type_handle(tp, false); - if (!type) - return false; - return isinstance(obj, type); -} - -PYBIND11_NOINLINE inline std::string error_string() { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred"); - return "Unknown internal error occurred"; - } - - error_scope scope; // Preserve error state - - std::string errorString; - if (scope.type) { - errorString += handle(scope.type).attr("__name__").cast(); - errorString += ": "; - } - if (scope.value) - errorString += (std::string) str(scope.value); - - PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); - -#if PY_MAJOR_VERSION >= 3 - if (scope.trace != nullptr) - PyException_SetTraceback(scope.value, scope.trace); -#endif - -#if !defined(PYPY_VERSION) - if (scope.trace) { - PyTracebackObject *trace = (PyTracebackObject *) scope.trace; - - /* Get the deepest trace possible */ - while (trace->tb_next) - trace = trace->tb_next; - - PyFrameObject *frame = trace->tb_frame; - errorString += "\n\nAt:\n"; - while (frame) { - int lineno = PyFrame_GetLineNumber(frame); - errorString += - " " + handle(frame->f_code->co_filename).cast() + - "(" + std::to_string(lineno) + "): " + - handle(frame->f_code->co_name).cast() + "\n"; - frame = frame->f_back; - } - } -#endif - - return errorString; -} - -PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail::type_info *type ) { - auto &instances = get_internals().registered_instances; - auto range = instances.equal_range(ptr); - for (auto it = range.first; it != range.second; ++it) { - for (auto vh : values_and_holders(it->second)) { - if (vh.type == type) - return handle((PyObject *) it->second); - } - } - return handle(); -} +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -inline PyThreadState *get_thread_state_unchecked() { -#if defined(PYPY_VERSION) - return PyThreadState_GET(); -#elif PY_VERSION_HEX < 0x03000000 - return _PyThreadState_Current; -#elif PY_VERSION_HEX < 0x03050000 - return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current); -#elif PY_VERSION_HEX < 0x03050200 - return (PyThreadState*) _PyThreadState_Current.value; -#else - return _PyThreadState_UncheckedGet(); -#endif -} +PYBIND11_WARNING_DISABLE_MSVC(4127) -// Forward declarations -inline void keep_alive_impl(handle nurse, handle patient); -inline PyObject *make_new_instance(PyTypeObject *type); +PYBIND11_NAMESPACE_BEGIN(detail) -class type_caster_generic { -public: - PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info) - : typeinfo(get_type_info(type_info)), cpptype(&type_info) { } - - type_caster_generic(const type_info *typeinfo) - : typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) { } - - bool load(handle src, bool convert) { - return load_impl(src, convert); - } - - PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent, - const detail::type_info *tinfo, - void *(*copy_constructor)(const void *), - void *(*move_constructor)(const void *), - const void *existing_holder = nullptr) { - if (!tinfo) // no type info: error will be set already - return handle(); - - void *src = const_cast(_src); - if (src == nullptr) - return none().release(); - - auto it_instances = get_internals().registered_instances.equal_range(src); - for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) { - for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) { - if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) - return handle((PyObject *) it_i->second).inc_ref(); - } - } - - auto inst = reinterpret_steal(make_new_instance(tinfo->type)); - auto wrapper = reinterpret_cast(inst.ptr()); - wrapper->owned = false; - void *&valueptr = values_and_holders(wrapper).begin()->value_ptr(); - - switch (policy) { - case return_value_policy::automatic: - case return_value_policy::take_ownership: - valueptr = src; - wrapper->owned = true; - break; - - case return_value_policy::automatic_reference: - case return_value_policy::reference: - valueptr = src; - wrapper->owned = false; - break; - - case return_value_policy::copy: - if (copy_constructor) - valueptr = copy_constructor(src); - else { -#if defined(NDEBUG) - throw cast_error("return_value_policy = copy, but type is " - "non-copyable! (compile in debug mode for details)"); -#else - std::string type_name(tinfo->cpptype->name()); - detail::clean_type_id(type_name); - throw cast_error("return_value_policy = copy, but type " + - type_name + " is non-copyable!"); -#endif - } - wrapper->owned = true; - break; - - case return_value_policy::move: - if (move_constructor) - valueptr = move_constructor(src); - else if (copy_constructor) - valueptr = copy_constructor(src); - else { -#if defined(NDEBUG) - throw cast_error("return_value_policy = move, but type is neither " - "movable nor copyable! " - "(compile in debug mode for details)"); -#else - std::string type_name(tinfo->cpptype->name()); - detail::clean_type_id(type_name); - throw cast_error("return_value_policy = move, but type " + - type_name + " is neither movable nor copyable!"); -#endif - } - wrapper->owned = true; - break; - - case return_value_policy::reference_internal: - valueptr = src; - wrapper->owned = false; - keep_alive_impl(inst, parent); - break; - - default: - throw cast_error("unhandled return_value_policy: should not happen!"); - } - - tinfo->init_instance(wrapper, existing_holder); - - return inst.release(); - } - - // Base methods for generic caster; there are overridden in copyable_holder_caster - void load_value(value_and_holder &&v_h) { - auto *&vptr = v_h.value_ptr(); - // Lazy allocation for unallocated values: - if (vptr == nullptr) { - auto *type = v_h.type ? v_h.type : typeinfo; - if (type->operator_new) { - vptr = type->operator_new(type->type_size); - } else { - #if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912) - if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) - vptr = ::operator new(type->type_size, - std::align_val_t(type->type_align)); - else - #endif - vptr = ::operator new(type->type_size); - } - } - value = vptr; - } - bool try_implicit_casts(handle src, bool convert) { - for (auto &cast : typeinfo->implicit_casts) { - type_caster_generic sub_caster(*cast.first); - if (sub_caster.load(src, convert)) { - value = cast.second(sub_caster.value); - return true; - } - } - return false; - } - bool try_direct_conversions(handle src) { - for (auto &converter : *typeinfo->direct_conversions) { - if (converter(src.ptr(), value)) - return true; - } - return false; - } - void check_holder_compat() {} - - PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) { - auto caster = type_caster_generic(ti); - if (caster.load(src, false)) - return caster.value; - return nullptr; - } - - /// Try to load with foreign typeinfo, if available. Used when there is no - /// native typeinfo, or when the native one wasn't able to produce a value. - PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) { - constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID; - const auto pytype = src.get_type(); - if (!hasattr(pytype, local_key)) - return false; - - type_info *foreign_typeinfo = reinterpret_borrow(getattr(pytype, local_key)); - // Only consider this foreign loader if actually foreign and is a loader of the correct cpp type - if (foreign_typeinfo->module_local_load == &local_load - || (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) - return false; - - if (auto result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) { - value = result; - return true; - } - return false; - } - - // Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant - // bits of code between here and copyable_holder_caster where the two classes need different - // logic (without having to resort to virtual inheritance). - template - PYBIND11_NOINLINE bool load_impl(handle src, bool convert) { - if (!src) return false; - if (!typeinfo) return try_load_foreign_module_local(src); - if (src.is_none()) { - // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; - value = nullptr; - return true; - } - - auto &this_ = static_cast(*this); - this_.check_holder_compat(); - - PyTypeObject *srctype = Py_TYPE(src.ptr()); - - // Case 1: If src is an exact type match for the target type then we can reinterpret_cast - // the instance's value pointer to the target type: - if (srctype == typeinfo->type) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); - return true; - } - // Case 2: We have a derived class - else if (PyType_IsSubtype(srctype, typeinfo->type)) { - auto &bases = all_type_info(srctype); - bool no_cpp_mi = typeinfo->simple_type; - - // Case 2a: the python type is a Python-inherited derived class that inherits from just - // one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of - // the right type and we can use reinterpret_cast. - // (This is essentially the same as case 2b, but because not using multiple inheritance - // is extremely common, we handle it specially to avoid the loop iterator and type - // pointer lookup overhead) - if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); - return true; - } - // Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if - // we can find an exact match (or, for a simple C++ type, an inherited match); if so, we - // can safely reinterpret_cast to the relevant pointer. - else if (bases.size() > 1) { - for (auto base : bases) { - if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder(base)); - return true; - } - } - } - - // Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match - // in the registered bases, above, so try implicit casting (needed for proper C++ casting - // when MI is involved). - if (this_.try_implicit_casts(src, convert)) - return true; - } - - // Perform an implicit conversion - if (convert) { - for (auto &converter : typeinfo->implicit_conversions) { - auto temp = reinterpret_steal(converter(src.ptr(), typeinfo->type)); - if (load_impl(temp, false)) { - loader_life_support::add_patient(temp); - return true; - } - } - if (this_.try_direct_conversions(src)) - return true; - } - - // Failed to match local typeinfo. Try again with global. - if (typeinfo->module_local) { - if (auto gtype = get_global_type_info(*typeinfo->cpptype)) { - typeinfo = gtype; - return load(src, false); - } - } - - // Global typeinfo has precedence over foreign module_local - return try_load_foreign_module_local(src); - } - - - // Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast - // isn't needed or can't be used. If the type is unknown, sets the error and returns a pair - // with .second = nullptr. (p.first = nullptr is not an error: it becomes None). - PYBIND11_NOINLINE static std::pair src_and_type( - const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) { - if (auto *tpi = get_type_info(cast_type)) - return {src, const_cast(tpi)}; - - // Not found, set error: - std::string tname = rtti_type ? rtti_type->name() : cast_type.name(); - detail::clean_type_id(tname); - std::string msg = "Unregistered type : " + tname; - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return {nullptr, nullptr}; - } - - const type_info *typeinfo = nullptr; - const std::type_info *cpptype = nullptr; - void *value = nullptr; -}; - -/** - * Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster - * needs to provide `operator T*()` and `operator T&()` operators. - * - * If the type supports moving the value away via an `operator T&&() &&` method, it should use - * `movable_cast_op_type` instead. - */ -template -using cast_op_type = - conditional_t>::value, - typename std::add_pointer>::type, - typename std::add_lvalue_reference>::type>; - -/** - * Determine suitable casting operator for a type caster with a movable value. Such a type caster - * needs to provide `operator T*()`, `operator T&()`, and `operator T&&() &&`. The latter will be - * called in appropriate contexts where the value can be moved rather than copied. - * - * These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro. - */ -template -using movable_cast_op_type = - conditional_t::type>::value, - typename std::add_pointer>::type, - conditional_t::value, - typename std::add_rvalue_reference>::type, - typename std::add_lvalue_reference>::type>>; - -// std::is_copy_constructible isn't quite enough: it lets std::vector (and similar) through when -// T is non-copyable, but code containing such a copy constructor fails to actually compile. -template struct is_copy_constructible : std::is_copy_constructible {}; - -// Specialization for types that appear to be copy constructible but also look like stl containers -// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if -// so, copy constructability depends on whether the value_type is copy constructible. -template struct is_copy_constructible, - std::is_same, - // Avoid infinite recursion - negation> - >::value>> : is_copy_constructible {}; - -// Likewise for std::pair -// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't themselves -// copy constructible, but this can not be relied upon when T1 or T2 are themselves containers). -template struct is_copy_constructible> - : all_of, is_copy_constructible> {}; - -// The same problems arise with std::is_copy_assignable, so we use the same workaround. -template struct is_copy_assignable : std::is_copy_assignable {}; -template struct is_copy_assignable, - std::is_same - >::value>> : is_copy_assignable {}; -template struct is_copy_assignable> - : all_of, is_copy_assignable> {}; - -NAMESPACE_END(detail) - -// polymorphic_type_hook::get(src, tinfo) determines whether the object pointed -// to by `src` actually is an instance of some class derived from `itype`. -// If so, it sets `tinfo` to point to the std::type_info representing that derived -// type, and returns a pointer to the start of the most-derived object of that type -// (in which `src` is a subobject; this will be the same address as `src` in most -// single inheritance cases). If not, or if `src` is nullptr, it simply returns `src` -// and leaves `tinfo` at its default value of nullptr. -// -// The default polymorphic_type_hook just returns src. A specialization for polymorphic -// types determines the runtime type of the passed object and adjusts the this-pointer -// appropriately via dynamic_cast. This is what enables a C++ Animal* to appear -// to Python as a Dog (if Dog inherits from Animal, Animal is polymorphic, Dog is -// registered with pybind11, and this Animal is in fact a Dog). -// -// You may specialize polymorphic_type_hook yourself for types that want to appear -// polymorphic to Python but do not use C++ RTTI. (This is a not uncommon pattern -// in performance-sensitive applications, used most notably in LLVM.) -template -struct polymorphic_type_hook -{ - static const void *get(const itype *src, const std::type_info*&) { return src; } -}; -template -struct polymorphic_type_hook::value>> -{ - static const void *get(const itype *src, const std::type_info*& type) { - type = src ? &typeid(*src) : nullptr; - return dynamic_cast(src); - } -}; - -NAMESPACE_BEGIN(detail) - -/// Generic type caster for objects stored on the heap -template class type_caster_base : public type_caster_generic { - using itype = intrinsic_t; - -public: - static constexpr auto name = _(); - - type_caster_base() : type_caster_base(typeid(type)) { } - explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { } - - static handle cast(const itype &src, return_value_policy policy, handle parent) { - if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) - policy = return_value_policy::copy; - return cast(&src, policy, parent); - } - - static handle cast(itype &&src, return_value_policy, handle parent) { - return cast(&src, return_value_policy::move, parent); - } - - // Returns a (pointer, type_info) pair taking care of necessary type lookup for a - // polymorphic type (using RTTI by default, but can be overridden by specializing - // polymorphic_type_hook). If the instance isn't derived, returns the base version. - static std::pair src_and_type(const itype *src) { - auto &cast_type = typeid(itype); - const std::type_info *instance_type = nullptr; - const void *vsrc = polymorphic_type_hook::get(src, instance_type); - if (instance_type && !same_type(cast_type, *instance_type)) { - // This is a base pointer to a derived type. If the derived type is registered - // with pybind11, we want to make the full derived object available. - // In the typical case where itype is polymorphic, we get the correct - // derived pointer (which may be != base pointer) by a dynamic_cast to - // most derived type. If itype is not polymorphic, we won't get here - // except via a user-provided specialization of polymorphic_type_hook, - // and the user has promised that no this-pointer adjustment is - // required in that case, so it's OK to use static_cast. - if (const auto *tpi = get_type_info(*instance_type)) - return {vsrc, tpi}; - } - // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so - // don't do a cast - return type_caster_generic::src_and_type(src, cast_type, instance_type); - } - - static handle cast(const itype *src, return_value_policy policy, handle parent) { - auto st = src_and_type(src); - return type_caster_generic::cast( - st.first, policy, parent, st.second, - make_copy_constructor(src), make_move_constructor(src)); - } - - static handle cast_holder(const itype *src, const void *holder) { - auto st = src_and_type(src); - return type_caster_generic::cast( - st.first, return_value_policy::take_ownership, {}, st.second, - nullptr, nullptr, holder); - } - - template using cast_op_type = detail::cast_op_type; - - operator itype*() { return (type *) value; } - operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); } - -protected: - using Constructor = void *(*)(const void *); - - /* Only enabled when the types are {copy,move}-constructible *and* when the type - does not have a private operator new implementation. */ - template ::value>> - static auto make_copy_constructor(const T *x) -> decltype(new T(*x), Constructor{}) { - return [](const void *arg) -> void * { - return new T(*reinterpret_cast(arg)); - }; - } - - template ::value>> - static auto make_move_constructor(const T *x) -> decltype(new T(std::move(*const_cast(x))), Constructor{}) { - return [](const void *arg) -> void * { - return new T(std::move(*const_cast(reinterpret_cast(arg)))); - }; - } - - static Constructor make_copy_constructor(...) { return nullptr; } - static Constructor make_move_constructor(...) { return nullptr; } -}; - -template class type_caster : public type_caster_base { }; -template using make_caster = type_caster>; +template +class type_caster : public type_caster_base {}; +template +using make_caster = type_caster>; // Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T -template typename make_caster::template cast_op_type cast_op(make_caster &caster) { +template +typename make_caster::template cast_op_type cast_op(make_caster &caster) { return caster.operator typename make_caster::template cast_op_type(); } -template typename make_caster::template cast_op_type::type> +template +typename make_caster::template cast_op_type::type> cast_op(make_caster &&caster) { - return std::move(caster).operator - typename make_caster::template cast_op_type::type>(); + return std::move(caster).operator typename make_caster:: + template cast_op_type::type>(); } -template class type_caster> { +template +class type_caster> { private: using caster_t = make_caster; caster_t subcaster; - using subcaster_cast_op_type = typename caster_t::template cast_op_type; - static_assert(std::is_same::type &, subcaster_cast_op_type>::value, - "std::reference_wrapper caster requires T to have a caster with an `T &` operator"); + using reference_t = type &; + using subcaster_cast_op_type = typename caster_t::template cast_op_type; + + static_assert( + std::is_same::type &, subcaster_cast_op_type>::value + || std::is_same::value, + "std::reference_wrapper caster requires T to have a caster with an " + "`operator T &()` or `operator const T &()`"); + public: bool load(handle src, bool convert) { return subcaster.load(src, convert); } static constexpr auto name = caster_t::name; - static handle cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { + static handle + cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { // It is definitely wrong to take ownership of this pointer, so mask that rvp - if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic) + if (policy == return_value_policy::take_ownership + || policy == return_value_policy::automatic) { policy = return_value_policy::automatic_reference; + } return caster_t::cast(&src.get(), policy, parent); } - template using cast_op_type = std::reference_wrapper; - operator std::reference_wrapper() { return subcaster.operator subcaster_cast_op_type&(); } + template + using cast_op_type = std::reference_wrapper; + explicit operator std::reference_wrapper() { return cast_op(subcaster); } }; -#define PYBIND11_TYPE_CASTER(type, py_name) \ - protected: \ - type value; \ - public: \ - static constexpr auto name = py_name; \ - template >::value, int> = 0> \ - static handle cast(T_ *src, return_value_policy policy, handle parent) { \ - if (!src) return none().release(); \ - if (policy == return_value_policy::take_ownership) { \ - auto h = cast(std::move(*src), policy, parent); delete src; return h; \ - } else { \ - return cast(*src, policy, parent); \ - } \ - } \ - operator type*() { return &value; } \ - operator type&() { return value; } \ - operator type&&() && { return std::move(value); } \ - template using cast_op_type = pybind11::detail::movable_cast_op_type - - -template using is_std_char_type = any_of< - std::is_same, /* std::string */ +#define PYBIND11_TYPE_CASTER(type, py_name) \ +protected: \ + type value; \ + \ +public: \ + static constexpr auto name = py_name; \ + template >::value, \ + int> \ + = 0> \ + static ::pybind11::handle cast( \ + T_ *src, ::pybind11::return_value_policy policy, ::pybind11::handle parent) { \ + if (!src) \ + return ::pybind11::none().release(); \ + if (policy == ::pybind11::return_value_policy::take_ownership) { \ + auto h = cast(std::move(*src), policy, parent); \ + delete src; \ + return h; \ + } \ + return cast(*src, policy, parent); \ + } \ + operator type *() { return &value; } /* NOLINT(bugprone-macro-parentheses) */ \ + operator type &() { return value; } /* NOLINT(bugprone-macro-parentheses) */ \ + operator type &&() && { return std::move(value); } /* NOLINT(bugprone-macro-parentheses) */ \ + template \ + using cast_op_type = ::pybind11::detail::movable_cast_op_type + +template +using is_std_char_type = any_of, /* std::string */ #if defined(PYBIND11_HAS_U8STRING) - std::is_same, /* std::u8string */ + std::is_same, /* std::u8string */ #endif - std::is_same, /* std::u16string */ - std::is_same, /* std::u32string */ - std::is_same /* std::wstring */ ->; + std::is_same, /* std::u16string */ + std::is_same, /* std::u32string */ + std::is_same /* std::wstring */ + >; template struct type_caster::value && !is_std_char_type::value>> { using _py_type_0 = conditional_t; - using _py_type_1 = conditional_t::value, _py_type_0, typename std::make_unsigned<_py_type_0>::type>; + using _py_type_1 = conditional_t::value, + _py_type_0, + typename std::make_unsigned<_py_type_0>::type>; using py_type = conditional_t::value, double, _py_type_1>; -public: +public: bool load(handle src, bool convert) { py_type py_value; - if (!src) + if (!src) { return false; + } + +#if !defined(PYPY_VERSION) + auto index_check = [](PyObject *o) { return PyIndex_Check(o); }; +#else + // In PyPy 7.3.3, `PyIndex_Check` is implemented by calling `__index__`, + // while CPython only considers the existence of `nb_index`/`__index__`. + auto index_check = [](PyObject *o) { return hasattr(o, "__index__"); }; +#endif if (std::is_floating_point::value) { - if (convert || PyFloat_Check(src.ptr())) + if (convert || PyFloat_Check(src.ptr())) { py_value = (py_type) PyFloat_AsDouble(src.ptr()); - else + } else { return false; - } else if (PyFloat_Check(src.ptr())) { + } + } else if (PyFloat_Check(src.ptr()) + || (!convert && !PYBIND11_LONG_CHECK(src.ptr()) && !index_check(src.ptr()))) { return false; - } else if (std::is_unsigned::value) { - py_value = as_unsigned(src.ptr()); - } else { // signed integer: - py_value = sizeof(T) <= sizeof(long) - ? (py_type) PyLong_AsLong(src.ptr()) - : (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr()); + } else { + handle src_or_index = src; + // PyPy: 7.3.7's 3.8 does not implement PyLong_*'s __index__ calls. +#if PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION) + object index; + if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: index_check(src.ptr()) + index = reinterpret_steal(PyNumber_Index(src.ptr())); + if (!index) { + PyErr_Clear(); + if (!convert) + return false; + } else { + src_or_index = index; + } + } +#endif + if (std::is_unsigned::value) { + py_value = as_unsigned(src_or_index.ptr()); + } else { // signed integer: + py_value = sizeof(T) <= sizeof(long) + ? (py_type) PyLong_AsLong(src_or_index.ptr()) + : (py_type) PYBIND11_LONG_AS_LONGLONG(src_or_index.ptr()); + } } + // Python API reported an error bool py_err = py_value == (py_type) -1 && PyErr_Occurred(); - // Protect std::numeric_limits::min/max with parentheses - if (py_err || (std::is_integral::value && sizeof(py_type) != sizeof(T) && - (py_value < (py_type) (std::numeric_limits::min)() || - py_value > (py_type) (std::numeric_limits::max)()))) { - bool type_error = py_err && PyErr_ExceptionMatches( -#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION) - PyExc_SystemError -#else - PyExc_TypeError -#endif - ); + // Check to see if the conversion is valid (integers should match exactly) + // Signed/unsigned checks happen elsewhere + if (py_err + || (std::is_integral::value && sizeof(py_type) != sizeof(T) + && py_value != (py_type) (T) py_value)) { PyErr_Clear(); - if (type_error && convert && PyNumber_Check(src.ptr())) { + if (py_err && convert && (PyNumber_Check(src.ptr()) != 0)) { auto tmp = reinterpret_steal(std::is_floating_point::value - ? PyNumber_Float(src.ptr()) - : PyNumber_Long(src.ptr())); + ? PyNumber_Float(src.ptr()) + : PyNumber_Long(src.ptr())); PyErr_Clear(); return load(tmp, false); } @@ -1056,62 +201,75 @@ struct type_caster::value && !is_std_char_t return true; } - template + template static typename std::enable_if::value, handle>::type cast(U src, return_value_policy /* policy */, handle /* parent */) { return PyFloat_FromDouble((double) src); } - template - static typename std::enable_if::value && std::is_signed::value && (sizeof(U) <= sizeof(long)), handle>::type + template + static typename std::enable_if::value && std::is_signed::value + && (sizeof(U) <= sizeof(long)), + handle>::type cast(U src, return_value_policy /* policy */, handle /* parent */) { return PYBIND11_LONG_FROM_SIGNED((long) src); } - template - static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) <= sizeof(unsigned long)), handle>::type + template + static typename std::enable_if::value && std::is_unsigned::value + && (sizeof(U) <= sizeof(unsigned long)), + handle>::type cast(U src, return_value_policy /* policy */, handle /* parent */) { return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src); } - template - static typename std::enable_if::value && std::is_signed::value && (sizeof(U) > sizeof(long)), handle>::type + template + static typename std::enable_if::value && std::is_signed::value + && (sizeof(U) > sizeof(long)), + handle>::type cast(U src, return_value_policy /* policy */, handle /* parent */) { return PyLong_FromLongLong((long long) src); } - template - static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) > sizeof(unsigned long)), handle>::type + template + static typename std::enable_if::value && std::is_unsigned::value + && (sizeof(U) > sizeof(unsigned long)), + handle>::type cast(U src, return_value_policy /* policy */, handle /* parent */) { return PyLong_FromUnsignedLongLong((unsigned long long) src); } - PYBIND11_TYPE_CASTER(T, _::value>("int", "float")); + PYBIND11_TYPE_CASTER(T, const_name::value>("int", "float")); }; -template struct void_caster { +template +struct void_caster { public: bool load(handle src, bool) { - if (src && src.is_none()) + if (src && src.is_none()) { return true; + } return false; } static handle cast(T, return_value_policy /* policy */, handle /* parent */) { - return none().inc_ref(); + return none().release(); } - PYBIND11_TYPE_CASTER(T, _("None")); + PYBIND11_TYPE_CASTER(T, const_name("None")); }; -template <> class type_caster : public void_caster {}; +template <> +class type_caster : public void_caster {}; -template <> class type_caster : public type_caster { +template <> +class type_caster : public type_caster { public: using type_caster::cast; bool load(handle h, bool) { if (!h) { return false; - } else if (h.is_none()) { + } + if (h.is_none()) { value = nullptr; return true; } @@ -1123,7 +281,7 @@ template <> class type_caster : public type_caster { } /* Check if this is a C++ type */ - auto &bases = all_type_info((PyTypeObject *) h.get_type().ptr()); + const auto &bases = all_type_info((PyTypeObject *) type::handle_of(h).ptr()); if (bases.size() == 1) { // Only allowing loading from a single-value type value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); return true; @@ -1134,193 +292,247 @@ template <> class type_caster : public type_caster { } static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { - if (ptr) + if (ptr) { return capsule(ptr).release(); - else - return none().inc_ref(); + } + return none().release(); } - template using cast_op_type = void*&; - operator void *&() { return value; } - static constexpr auto name = _("capsule"); + template + using cast_op_type = void *&; + explicit operator void *&() { return value; } + static constexpr auto name = const_name("capsule"); + private: void *value = nullptr; }; -template <> class type_caster : public void_caster { }; +template <> +class type_caster : public void_caster {}; -template <> class type_caster { +template <> +class type_caster { public: bool load(handle src, bool convert) { - if (!src) return false; - else if (src.ptr() == Py_True) { value = true; return true; } - else if (src.ptr() == Py_False) { value = false; return true; } - else if (convert || !strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name)) { + if (!src) { + return false; + } + if (src.ptr() == Py_True) { + value = true; + return true; + } + if (src.ptr() == Py_False) { + value = false; + return true; + } + if (convert || (std::strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name) == 0)) { // (allow non-implicit conversion for numpy booleans) Py_ssize_t res = -1; if (src.is_none()) { - res = 0; // None is implicitly converted to False + res = 0; // None is implicitly converted to False } - #if defined(PYPY_VERSION) - // On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists +#if defined(PYPY_VERSION) + // On PyPy, check that "__bool__" attr exists else if (hasattr(src, PYBIND11_BOOL_ATTR)) { res = PyObject_IsTrue(src.ptr()); } - #else +#else // Alternate approach for CPython: this does the same as the above, but optimized // using the CPython API so as to avoid an unneeded attribute lookup. - else if (auto tp_as_number = src.ptr()->ob_type->tp_as_number) { + else if (auto *tp_as_number = src.ptr()->ob_type->tp_as_number) { if (PYBIND11_NB_BOOL(tp_as_number)) { res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); } } - #endif +#endif if (res == 0 || res == 1) { - value = (bool) res; + value = (res != 0); return true; - } else { - PyErr_Clear(); } + PyErr_Clear(); } return false; } static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { return handle(src ? Py_True : Py_False).inc_ref(); } - PYBIND11_TYPE_CASTER(bool, _("bool")); + PYBIND11_TYPE_CASTER(bool, const_name("bool")); }; // Helper class for UTF-{8,16,32} C++ stl strings: -template struct string_caster { +template +struct string_caster { using CharT = typename StringType::value_type; // Simplify life by being able to assume standard char sizes (the standard only guarantees // minimums, but Python requires exact sizes) - static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char size != 1"); + static_assert(!std::is_same::value || sizeof(CharT) == 1, + "Unsupported char size != 1"); #if defined(PYBIND11_HAS_U8STRING) - static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char8_t size != 1"); + static_assert(!std::is_same::value || sizeof(CharT) == 1, + "Unsupported char8_t size != 1"); #endif - static_assert(!std::is_same::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2"); - static_assert(!std::is_same::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4"); + static_assert(!std::is_same::value || sizeof(CharT) == 2, + "Unsupported char16_t size != 2"); + static_assert(!std::is_same::value || sizeof(CharT) == 4, + "Unsupported char32_t size != 4"); // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) static_assert(!std::is_same::value || sizeof(CharT) == 2 || sizeof(CharT) == 4, - "Unsupported wchar_t size != 2/4"); + "Unsupported wchar_t size != 2/4"); static constexpr size_t UTF_N = 8 * sizeof(CharT); bool load(handle src, bool) { -#if PY_MAJOR_VERSION < 3 - object temp; -#endif handle load_src = src; if (!src) { return false; - } else if (!PyUnicode_Check(load_src.ptr())) { -#if PY_MAJOR_VERSION >= 3 - return load_bytes(load_src); -#else - if (std::is_same::value) { - return load_bytes(load_src); - } + } + if (!PyUnicode_Check(load_src.ptr())) { + return load_raw(load_src); + } - // The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false - if (!PYBIND11_BYTES_CHECK(load_src.ptr())) + // For UTF-8 we avoid the need for a temporary `bytes` object by using + // `PyUnicode_AsUTF8AndSize`. + if (UTF_N == 8) { + Py_ssize_t size = -1; + const auto *buffer + = reinterpret_cast(PyUnicode_AsUTF8AndSize(load_src.ptr(), &size)); + if (!buffer) { + PyErr_Clear(); return false; - - temp = reinterpret_steal(PyUnicode_FromObject(load_src.ptr())); - if (!temp) { PyErr_Clear(); return false; } - load_src = temp; -#endif + } + value = StringType(buffer, static_cast(size)); + return true; } - object utfNbytes = reinterpret_steal(PyUnicode_AsEncodedString( - load_src.ptr(), UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr)); - if (!utfNbytes) { PyErr_Clear(); return false; } + auto utfNbytes + = reinterpret_steal(PyUnicode_AsEncodedString(load_src.ptr(), + UTF_N == 8 ? "utf-8" + : UTF_N == 16 ? "utf-16" + : "utf-32", + nullptr)); + if (!utfNbytes) { + PyErr_Clear(); + return false; + } - const CharT *buffer = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); + const auto *buffer + = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); - if (UTF_N > 8) { buffer++; length--; } // Skip BOM for UTF-16/32 + // Skip BOM for UTF-16/32 + if (UTF_N > 8) { + buffer++; + length--; + } value = StringType(buffer, length); // If we're loading a string_view we need to keep the encoded Python object alive: - if (IsView) + if (IsView) { loader_life_support::add_patient(utfNbytes); + } return true; } - static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { + static handle + cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { const char *buffer = reinterpret_cast(src.data()); - ssize_t nbytes = ssize_t(src.size() * sizeof(CharT)); + auto nbytes = ssize_t(src.size() * sizeof(CharT)); handle s = decode_utfN(buffer, nbytes); - if (!s) throw error_already_set(); + if (!s) { + throw error_already_set(); + } return s; } - PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME)); + PYBIND11_TYPE_CASTER(StringType, const_name(PYBIND11_STRING_NAME)); private: static handle decode_utfN(const char *buffer, ssize_t nbytes) { #if !defined(PYPY_VERSION) - return - UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) : - UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) : - PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); + return UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) + : UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) + : PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); #else - // PyPy seems to have multiple problems related to PyUnicode_UTF*: the UTF8 version - // sometimes segfaults for unknown reasons, while the UTF16 and 32 versions require a - // non-const char * arguments, which is also a nuisance, so bypass the whole thing by just - // passing the encoding as a string value, which works properly: - return PyUnicode_Decode(buffer, nbytes, UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr); + // PyPy segfaults when on PyUnicode_DecodeUTF16 (and possibly on PyUnicode_DecodeUTF32 as + // well), so bypass the whole thing by just passing the encoding as a string value, which + // works properly: + return PyUnicode_Decode(buffer, + nbytes, + UTF_N == 8 ? "utf-8" + : UTF_N == 16 ? "utf-16" + : "utf-32", + nullptr); #endif } - // When loading into a std::string or char*, accept a bytes object as-is (i.e. + // When loading into a std::string or char*, accept a bytes/bytearray object as-is (i.e. // without any encoding/decoding attempt). For other C++ char sizes this is a no-op. // which supports loading a unicode from a str, doesn't take this path. template - bool load_bytes(enable_if_t::value, handle> src) { + bool load_raw(enable_if_t::value, handle> src) { if (PYBIND11_BYTES_CHECK(src.ptr())) { - // We were passed a Python 3 raw bytes; accept it into a std::string or char* + // We were passed raw bytes; accept it into a std::string or char* // without any encoding attempt. const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); - if (bytes) { - value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); - return true; + if (!bytes) { + pybind11_fail("Unexpected PYBIND11_BYTES_AS_STRING() failure."); + } + value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); + return true; + } + if (PyByteArray_Check(src.ptr())) { + // We were passed a bytearray; accept it into a std::string or char* + // without any encoding attempt. + const char *bytearray = PyByteArray_AsString(src.ptr()); + if (!bytearray) { + pybind11_fail("Unexpected PyByteArray_AsString() failure."); } + value = StringType(bytearray, (size_t) PyByteArray_Size(src.ptr())); + return true; } return false; } template - bool load_bytes(enable_if_t::value, handle>) { return false; } + bool load_raw(enable_if_t::value, handle>) { + return false; + } }; template -struct type_caster, enable_if_t::value>> +struct type_caster, + enable_if_t::value>> : string_caster> {}; #ifdef PYBIND11_HAS_STRING_VIEW template -struct type_caster, enable_if_t::value>> +struct type_caster, + enable_if_t::value>> : string_caster, true> {}; #endif // Type caster for C-style strings. We basically use a std::string type caster, but also add the // ability to use None as a nullptr char* (which the string caster doesn't allow). -template struct type_caster::value>> { +template +struct type_caster::value>> { using StringType = std::basic_string; - using StringCaster = type_caster; + using StringCaster = make_caster; StringCaster str_caster; bool none = false; CharT one_char = 0; + public: bool load(handle src, bool convert) { - if (!src) return false; + if (!src) { + return false; + } if (src.is_none()) { // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; + if (!convert) { + return false; + } none = true; return true; } @@ -1328,45 +540,58 @@ template struct type_caster::value) { handle s = PyUnicode_DecodeLatin1((const char *) &src, 1, nullptr); - if (!s) throw error_already_set(); + if (!s) { + throw error_already_set(); + } return s; } return StringCaster::cast(StringType(1, src), policy, parent); } - operator CharT*() { return none ? nullptr : const_cast(static_cast(str_caster).c_str()); } - operator CharT&() { - if (none) + explicit operator CharT *() { + return none ? nullptr : const_cast(static_cast(str_caster).c_str()); + } + explicit operator CharT &() { + if (none) { throw value_error("Cannot convert None to a character"); + } auto &value = static_cast(str_caster); size_t str_len = value.size(); - if (str_len == 0) + if (str_len == 0) { throw value_error("Cannot convert empty string to a character"); + } // If we're in UTF-8 mode, we have two possible failures: one for a unicode character that - // is too high, and one for multiple unicode characters (caught later), so we need to figure - // out how long the first encoded character is in bytes to distinguish between these two - // errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those - // can fit into a single char value. + // is too high, and one for multiple unicode characters (caught later), so we need to + // figure out how long the first encoded character is in bytes to distinguish between these + // two errors. We also allow want to allow unicode characters U+0080 through U+00FF, as + // those can fit into a single char value. if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { - unsigned char v0 = static_cast(value[0]); - size_t char0_bytes = !(v0 & 0x80) ? 1 : // low bits only: 0-127 - (v0 & 0xE0) == 0xC0 ? 2 : // 0b110xxxxx - start of 2-byte sequence - (v0 & 0xF0) == 0xE0 ? 3 : // 0b1110xxxx - start of 3-byte sequence - 4; // 0b11110xxx - start of 4-byte sequence + auto v0 = static_cast(value[0]); + // low bits only: 0-127 + // 0b110xxxxx - start of 2-byte sequence + // 0b1110xxxx - start of 3-byte sequence + // 0b11110xxx - start of 4-byte sequence + size_t char0_bytes = (v0 & 0x80) == 0 ? 1 + : (v0 & 0xE0) == 0xC0 ? 2 + : (v0 & 0xF0) == 0xE0 ? 3 + : 4; if (char0_bytes == str_len) { // If we have a 128-255 value, we can decode it into a single char: if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) { // 0x110000xx 0x10xxxxxx - one_char = static_cast(((v0 & 3) << 6) + (static_cast(value[1]) & 0x3F)); + one_char = static_cast(((v0 & 3) << 6) + + (static_cast(value[1]) & 0x3F)); return one_char; } // Otherwise we have a single character, but it's > U+00FF @@ -1379,34 +604,40 @@ template struct type_caster(value[0]); - if (one_char >= 0xD800 && one_char < 0xE000) + if (one_char >= 0xD800 && one_char < 0xE000) { throw value_error("Character code point not in range(0x10000)"); + } } - if (str_len != 1) + if (str_len != 1) { throw value_error("Expected a character, but multi-character string found"); + } one_char = value[0]; return one_char; } - static constexpr auto name = _(PYBIND11_STRING_NAME); - template using cast_op_type = pybind11::detail::cast_op_type<_T>; + static constexpr auto name = const_name(PYBIND11_STRING_NAME); + template + using cast_op_type = pybind11::detail::cast_op_type<_T>; }; // Base implementation for std::tuple and std::pair -template class Tuple, typename... Ts> class tuple_caster { +template