Skip to content

Commit

Permalink
End-to-end tagging: C++ (#8316)
Browse files Browse the repository at this point in the history
Implemented with the help of @Wumpf.

This semantically mimics very closely the way things are done in Rust,
minus all technical differences due to the differences between both the
languages and the SDKs.
For that reason, everything stated in
#8304 (comment) basically
applies as-is.

Pretty happy about it, I must say.

* DNM: requires #8304 
* Part of #7948

---------

Co-authored-by: Andreas Reich <[email protected]>
  • Loading branch information
teh-cmc and Wumpf authored Dec 9, 2024
1 parent 67a3cac commit 962f3e0
Show file tree
Hide file tree
Showing 272 changed files with 3,154 additions and 572 deletions.
23 changes: 18 additions & 5 deletions crates/build/re_types_builder/src/codegen/cpp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ impl QuotedObject {
.map(|m| m.to_cpp_tokens(&quote!(#type_ident)));

let indicator_comment = quote_doc_comment("Indicator component, used to identify the archetype when converting to a list of components.");
let indicator_fqname =
let indicator_component_fqname =
format!("{}Indicator", obj.fqname).replace("archetypes", "components");
let doc_hide_comment = quote_hide_from_docs();
let deprecation_notice = quote_deprecation_notice(obj);
Expand All @@ -586,7 +586,7 @@ impl QuotedObject {
#(#field_declarations;)*

public:
static constexpr const char IndicatorComponentName[] = #indicator_fqname;
static constexpr const char IndicatorComponentName[] = #indicator_component_fqname;
#NEWLINE_TOKEN
#NEWLINE_TOKEN
#indicator_comment
Expand Down Expand Up @@ -1586,28 +1586,39 @@ fn archetype_serialize(type_ident: &Ident, obj: &Object, hpp_includes: &mut Incl
quote!(archetypes)
};

let archetype_name = &obj.fqname;

let num_fields = quote_integer(obj.fields.len() + 1); // Plus one for the indicator.
let push_batches = obj.fields.iter().map(|field| {
let field_name = field_name_identifier(field);
let field_fqname = &field.typ.fqname();
let field_accessor = quote!(archetype.#field_name);

let push_back = quote! {
RR_RETURN_NOT_OK(result.error);
cells.push_back(std::move(result.value));
};

let archetype_field_name = field.snake_case_name();

// TODO(andreas): Introducing MonoCollection will remove the need for distinguishing these two cases.
if field.is_nullable && !obj.attrs.has(ATTR_RERUN_LOG_MISSING_AS_EMPTY) {
quote! {
if (#field_accessor.has_value()) {
auto result = ComponentBatch::from_loggable(#field_accessor.value());
auto result = ComponentBatch::from_loggable(
#field_accessor.value(),
ComponentDescriptor(#archetype_name, #archetype_field_name, #field_fqname)
);
#push_back
}
}
} else {
quote! {
{
auto result = ComponentBatch::from_loggable(#field_accessor);
auto result = ComponentBatch::from_loggable(
#field_accessor,
ComponentDescriptor(#archetype_name, #archetype_field_name, #field_fqname)
);
#push_back
}
}
Expand Down Expand Up @@ -2560,14 +2571,16 @@ fn quote_loggable_hpp_and_cpp(
let methods_cpp = methods.iter().map(|m| m.to_cpp_tokens(&loggable_type_name));
let hide_from_docs_comment = quote_hide_from_docs();

hpp_includes.insert_rerun("component_descriptor.hpp");

let hpp = quote! {
namespace rerun {
#predeclarations_and_static_assertions

#hide_from_docs_comment
template<>
struct #loggable_type_name {
static constexpr const char Name[] = #fqname;
static constexpr ComponentDescriptor Descriptor = #fqname;
#NEWLINE_TOKEN
#NEWLINE_TOKEN
#(#methods_hpp)*
Expand Down
2 changes: 1 addition & 1 deletion crates/top/rerun_c/src/arrow_utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{CError, CErrorCode};

/// Converts a C-FFI arrow array into a Rust component batch, taking ownership of the underlying arrow data. ///
/// Converts a C-FFI arrow array into a Rust component batch, taking ownership of the underlying arrow data.
///
/// Safety:
/// This must only be ever called once for a given ffi array.
Expand Down
15 changes: 9 additions & 6 deletions crates/top/rerun_c/src/component_type_registry.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use once_cell::sync::Lazy;
use parking_lot::RwLock;
use re_sdk::ComponentName;
use re_sdk::ComponentDescriptor;

use crate::{CComponentTypeHandle, CError, CErrorCode};

pub struct ComponentType {
pub name: ComponentName,
pub descriptor: ComponentDescriptor,
pub datatype: arrow2::datatypes::DataType,
}

Expand All @@ -18,22 +18,25 @@ pub struct ComponentTypeRegistry {
impl ComponentTypeRegistry {
pub fn register(
&mut self,
name: ComponentName,
descriptor: ComponentDescriptor,
datatype: arrow2::datatypes::DataType,
) -> CComponentTypeHandle {
#[cfg(debug_assertions)]
{
for ty in &self.types {
assert_ne!(
ty.name, name,
"Component type with the same name already registered"
ty.descriptor, descriptor,
"Component type with the same descriptor already registered"
);
}
}

let id = self.next_id;
self.next_id += 1;
self.types.push(ComponentType { name, datatype });
self.types.push(ComponentType {
descriptor,
datatype,
});
id
}

Expand Down
69 changes: 51 additions & 18 deletions crates/top/rerun_c/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ use once_cell::sync::Lazy;
use arrow_utils::arrow_array_from_c_ffi;
use re_sdk::{
external::nohash_hasher::IntMap,
log::{Chunk, ChunkComponents, ChunkId, PendingRow, TimeColumn},
log::{Chunk, ChunkId, PendingRow, TimeColumn},
time::TimeType,
ComponentDescriptor, ComponentName, EntityPath, RecordingStream, RecordingStreamBuilder,
StoreKind, TimePoint, Timeline,
ComponentDescriptor, EntityPath, RecordingStream, RecordingStreamBuilder, StoreKind, TimePoint,
Timeline,
};
use recording_streams::{recording_stream, RECORDING_STREAMS};

Expand Down Expand Up @@ -161,10 +161,18 @@ pub struct CStoreInfo {
pub store_kind: CStoreKind,
}

/// See `rr_component_descriptor` in the C header.
#[repr(C)]
pub struct CComponentDescriptor {
pub archetype_name: CStringView,
pub archetype_field_name: CStringView,
pub component_name: CStringView,
}

/// See `rr_component_type` in the C header.
#[repr(C)]
pub struct CComponentType {
pub name: CStringView,
pub descriptor: CComponentDescriptor,
pub schema: arrow2::ffi::ArrowSchema,
}

Expand Down Expand Up @@ -341,8 +349,30 @@ pub extern "C" fn rr_spawn(spawn_opts: *const CSpawnOptions, error: *mut CError)
fn rr_register_component_type_impl(
component_type: &CComponentType,
) -> Result<CComponentTypeHandle, CError> {
let component_name = component_type.name.as_str("component_type.name")?;
let component_name = ComponentName::from(component_name);
let CComponentDescriptor {
archetype_name,
archetype_field_name,
component_name,
} = &component_type.descriptor;

let archetype_name = if !archetype_name.is_null() {
Some(archetype_name.as_str("component_type.descriptor.archetype_name")?)
} else {
None
};
let archetype_field_name = if !archetype_field_name.is_null() {
Some(archetype_field_name.as_str("component_type.descriptor.archetype_field_name")?)
} else {
None
};
let component_name = component_name.as_str("component_type.descriptor.component_name")?;

let component_descr = ComponentDescriptor {
archetype_name: archetype_name.map(Into::into),
archetype_field_name: archetype_field_name.map(Into::into),
component_name: component_name.into(),
};

let schema =
unsafe { arrow2::ffi::import_field_from_c(&component_type.schema) }.map_err(|err| {
CError::new(
Expand All @@ -353,7 +383,7 @@ fn rr_register_component_type_impl(

Ok(COMPONENT_TYPES
.write()
.register(component_name, schema.data_type))
.register(component_descr, schema.data_type))
}

#[allow(unsafe_code)]
Expand Down Expand Up @@ -790,7 +820,7 @@ fn rr_recording_stream_log_impl(
let component_type = component_type_registry.get(*component_type)?;
let datatype = component_type.datatype.clone();
let values = unsafe { arrow_array_from_c_ffi(array, datatype) }?;
components.insert(ComponentDescriptor::new(component_type.name), values);
components.insert(component_type.descriptor.clone(), values);
}
}

Expand Down Expand Up @@ -978,20 +1008,23 @@ fn rr_recording_stream_send_columns_impl(
)
})?;

Ok((ComponentDescriptor::new(component_type.name), component_values.clone()))
Ok((component_type.descriptor.clone(), component_values.clone()))
})
.collect::<Result<_, CError>>()?
};

let components: ChunkComponents = components.into_iter().collect();

let chunk = Chunk::from_auto_row_ids(id, entity_path.into(), time_columns, components)
.map_err(|err| {
CError::new(
CErrorCode::RecordingStreamChunkValidationFailure,
&format!("Failed to create chunk: {err}"),
)
})?;
let chunk = Chunk::from_auto_row_ids(
id,
entity_path.into(),
time_columns,
components.into_iter().collect(),
)
.map_err(|err| {
CError::new(
CErrorCode::RecordingStreamChunkValidationFailure,
&format!("Failed to create chunk: {err}"),
)
})?;

stream.send_chunk(chunk);

Expand Down
10 changes: 10 additions & 0 deletions docs/snippets/all/descriptors/descr_builtin_archetype.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <rerun.hpp>

int main() {
const auto rec = rerun::RecordingStream("rerun_example_descriptors_builtin_archetype");
rec.spawn().exit_on_failure();

rec.log_static("data", rerun::Points3D({{1.0f, 2.0f, 3.0f}}).with_radii({0.3f, 0.2f, 0.1f}));

// The tags are indirectly checked by the Rust version (have a look over there for more info).
}
11 changes: 11 additions & 0 deletions docs/snippets/all/descriptors/descr_builtin_component.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <rerun.hpp>

int main() {
const auto rec = rerun::RecordingStream("rerun_example_descriptors_builtin_component");
rec.spawn().exit_on_failure();

rerun::Position3D positions[1] = {{1.0f, 2.0f, 3.0f}};
rec.log_static("data", positions);

// The tags are indirectly checked by the Rust version (have a look over there for more info).
}
74 changes: 74 additions & 0 deletions docs/snippets/all/descriptors/descr_custom_archetype.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include <rerun.hpp>
#include <vector>

struct CustomPosition3D {
rerun::components::Position3D position;
};

template <>
struct rerun::Loggable<CustomPosition3D> {
static constexpr ComponentDescriptor Descriptor = "user.CustomPosition3D";

static const std::shared_ptr<arrow::DataType>& arrow_datatype() {
return rerun::Loggable<rerun::components::Position3D>::arrow_datatype();
}

// TODO(#4257) should take a rerun::Collection instead of pointer and size.
static rerun::Result<std::shared_ptr<arrow::Array>> to_arrow(
const CustomPosition3D* instances, size_t num_instances
) {
return rerun::Loggable<rerun::components::Position3D>::to_arrow(
reinterpret_cast<const rerun::components::Position3D*>(instances),
num_instances
);
}
};

/// A custom archetype that extends Rerun's builtin `rerun::Points3D` archetype with a custom component.
struct CustomPoints3D {
static constexpr const char IndicatorComponentName[] = "user.CustomPoints3DIndicator";
using IndicatorComponent = rerun::components::IndicatorComponent<IndicatorComponentName>;

rerun::Collection<CustomPosition3D> positions;
std::optional<rerun::Collection<rerun::Color>> colors;
};

template <>
struct rerun::AsComponents<CustomPoints3D> {
static Result<std::vector<ComponentBatch>> serialize(const CustomPoints3D& archetype) {
std::vector<rerun::ComponentBatch> batches;

CustomPoints3D::IndicatorComponent indicator;
batches.push_back(ComponentBatch::from_loggable(indicator).value_or_throw());

auto positions_descr = rerun::Loggable<CustomPosition3D>::Descriptor
.or_with_archetype_name("user.CustomPoints3D")
.or_with_archetype_field_name("custom_positions");
batches.push_back(
ComponentBatch::from_loggable(archetype.positions, positions_descr).value_or_throw()
);

if (archetype.colors) {
auto colors_descr = rerun::Loggable<rerun::Color>::Descriptor
.or_with_archetype_name("user.CustomPoints3D")
.or_with_archetype_field_name("colors");
batches.push_back(
ComponentBatch::from_loggable(archetype.colors, colors_descr).value_or_throw()
);
}

return batches;
}
};

int main() {
const auto rec = rerun::RecordingStream("rerun_example_descriptors_custom_archetype");
rec.spawn().exit_on_failure();

CustomPosition3D positions[1] = {{rerun::components::Position3D{1.0f, 2.0f, 3.0f}}};
rerun::Color colors[1] = {rerun::Color(0xFF00FFFF)};

rec.log_static("data", CustomPoints3D{positions, colors});

// The tags are indirectly checked by the Rust version (have a look over there for more info).
}
35 changes: 35 additions & 0 deletions docs/snippets/all/descriptors/descr_custom_component.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include <rerun.hpp>

struct CustomPosition3D {
rerun::components::Position3D position;
};

template <>
struct rerun::Loggable<CustomPosition3D> {
static constexpr const ComponentDescriptor Descriptor =
ComponentDescriptor("user.CustomArchetype", "custom_positions", "user.CustomPosition3D");

static const std::shared_ptr<arrow::DataType>& arrow_datatype() {
return rerun::Loggable<rerun::components::Position3D>::arrow_datatype();
}

// TODO(#4257) should take a rerun::Collection instead of pointer and size.
static rerun::Result<std::shared_ptr<arrow::Array>> to_arrow(
const CustomPosition3D* instances, size_t num_instances
) {
return rerun::Loggable<rerun::components::Position3D>::to_arrow(
reinterpret_cast<const rerun::components::Position3D*>(instances),
num_instances
);
}
};

int main() {
const auto rec = rerun::RecordingStream("rerun_example_descriptors_custom_component");
rec.spawn().exit_on_failure();

CustomPosition3D positions[1] = {{rerun::components::Position3D{1.0f, 2.0f, 3.0f}}};
rec.log_static("data", positions);

// The tags are indirectly checked by the Rust version (have a look over there for more info).
}
Loading

0 comments on commit 962f3e0

Please sign in to comment.