Skip to content

Commit e0d4a48

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix-reloading-crash
2 parents fc50f4d + aa2696e commit e0d4a48

24 files changed

+538
-27
lines changed

.github/workflows/build.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ jobs:
114114
run: |
115115
cd d:\cesium\CesiumForUnityBuildProject\Packages\com.cesium.unity
116116
dotnet publish Reinterop~ -o .
117+
- name: Print disk space 1
118+
if: success() || failure() # run this step even if previous step failed
119+
run: |
120+
get-psdrive
117121
- name: Build Package
118122
run: |
119123
cd d:\cesium\CesiumForUnityBuildProject\Packages\com.cesium.unity
@@ -128,6 +132,10 @@ jobs:
128132
$ENV:EZVCPKG_BASEDIR="D:/.ezvcpkg"
129133
# Run the build
130134
dotnet run --project Build~
135+
- name: Print disk space 2
136+
if: success() || failure() # run this step even if previous step failed
137+
run: |
138+
get-psdrive
131139
- name: Publish Logs
132140
if: success() || failure() # run this step even if previous step failed
133141
uses: actions/upload-artifact@v4

CHANGES.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,35 @@
11
# Change Log
22

3+
## v1.15.4 - 2025-03-03
4+
5+
##### Fixes :wrench:
6+
7+
- "External Tilesets" are now unloaded when they are no longer used. This will significantly reduce the growth of memory usage over time when using Google Photorealistic 3D Tiles and similar tilesets.
8+
9+
This release updates [cesium-native](https://github.com/CesiumGS/cesium-native) from v0.44.3 to v0.45.0. See the [changelog](https://github.com/CesiumGS/cesium-native/blob/main/CHANGES.md) for a complete list of changes in cesium-native.
10+
11+
## v1.15.3 - 2025-02-12
12+
13+
This release updates [cesium-native](https://github.com/CesiumGS/cesium-native) from v0.44.2 to v0.44.3. See the [changelog](https://github.com/CesiumGS/cesium-native/blob/main/CHANGES.md) for a complete list of changes in cesium-native.
14+
15+
## v1.15.2 - 2025-02-10
16+
17+
This release updates [cesium-native](https://github.com/CesiumGS/cesium-native) from v0.44.1 to v0.44.2. See the [changelog](https://github.com/CesiumGS/cesium-native/blob/main/CHANGES.md) for a complete list of changes in cesium-native.
18+
19+
## v1.15.1 - 2025-02-03
20+
21+
This release updates [cesium-native](https://github.com/CesiumGS/cesium-native) from v0.44.0 to v0.44.1. See the [changelog](https://github.com/CesiumGS/cesium-native/blob/main/CHANGES.md) for a complete list of changes in cesium-native.
22+
23+
## v1.15.0 - 2025-02-03
24+
25+
Cesium for Unity now requires Unity 2022 LTS (2022.3), Unity 6, or later.
26+
27+
##### Fixes :wrench:
28+
29+
- Exceptions in either C# or C++ code are now handled much more reliably.
30+
31+
In addition to the above, this release updates [cesium-native](https://github.com/CesiumGS/cesium-native) from v0.43.0 to v0.44.0. See the [changelog](https://github.com/CesiumGS/cesium-native/blob/main/CHANGES.md) for a complete list of changes in cesium-native.
32+
333
## v1.14.1 - 2025-01-02
434

535
This is the last release of Cesium for Unity that will support Unity 2021 LTS (2021.3). Future versions will require Unity 2022 LTS (2022.3) or Unity 6.

Editor/ConfigureReinterop.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,9 @@ public void ExposeToCPP()
247247
EditorApplication.ExecuteMenuItem("Window/General/Hierarchy");
248248

249249
EditorUtility.SetDirty(null);
250+
251+
System.Exception exception = null;
252+
var exceptionMessage = exception.Message;
250253
}
251254
}
252255
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using Microsoft.CodeAnalysis;
2+
3+
namespace Reinterop
4+
{
5+
/// <summary>
6+
/// Inserts the "ReinteropException" class into an assembly as it is compiled.
7+
/// This is intended to be used from a RegisterPostInitializationOutput
8+
/// callback on an IIncrementalGenerator.
9+
/// </summary>
10+
internal class CSharpReinteropException
11+
{
12+
public static void Generate(GeneratorExecutionContext context)
13+
{
14+
context.AddSource("ReinteropException", Source);
15+
}
16+
17+
public static CppType GetCppWrapperType(CppGenerationContext context)
18+
{
19+
List<string> ns = new List<string>();
20+
if (context.BaseNamespace.Length > 0)
21+
ns.Add(context.BaseNamespace);
22+
ns.Add("Reinterop");
23+
24+
// If the first two namespaces are identical, remove the duplication.
25+
// This is to avoid `Reinterop::Reinterop`.
26+
if (ns.Count >= 2 && ns[0] == ns[1])
27+
ns.RemoveAt(0);
28+
29+
return new CppType(InteropTypeKind.ClassWrapper, ns, "ReinteropException", null, 0);
30+
}
31+
32+
public const string Source =
33+
"""
34+
namespace Reinterop
35+
{
36+
[Reinterop]
37+
internal class ReinteropException : System.Exception
38+
{
39+
public ReinteropException(string message) : base(message) {}
40+
41+
internal static void ExposeToCPP()
42+
{
43+
ReinteropException e = new ReinteropException("message");
44+
string s = e.Message;
45+
}
46+
}
47+
}
48+
""";
49+
}
50+
}

Reinterop~/CodeGenerator.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ public IEnumerable<CppSourceFile> DistributeToSourceFiles(IEnumerable<GeneratedR
105105

106106
// Create source files for the standard types.
107107
CppObjectHandle.Generate(this.Options, sourceFiles);
108+
CppReinteropException.Generate(this.Options, sourceFiles);
108109

109110
// Create source files for the generated types.
110111
foreach (GeneratedResult? generated in generatedResults)

Reinterop~/Constructors.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ private static void GenerateSingleNonStatic(CppGenerationContext context, TypeTo
4646

4747
bool hasStructRewrite = Interop.RewriteStructReturn(ref interopParameters, ref returnType, ref interopReturnType);
4848

49+
// Add a parameter in which to return the exception, if there is one.
50+
interopParameters = interopParameters.Concat(new[] { (ParameterName: "reinteropException", CallSiteName: "&reinteropException", Type: CppType.VoidPointerPointer, InteropType: CppType.VoidPointerPointer) });
51+
4952
var interopParameterStrings = interopParameters.Select(parameter => $"{parameter.InteropType.GetFullyQualifiedName()} {parameter.ParameterName}");
5053

5154
string interopFunctionName = $"Construct_{Interop.HashParameters(constructor.Parameters)}";
@@ -96,16 +99,20 @@ private static void GenerateSingleNonStatic(CppGenerationContext context, TypeTo
9699
$$"""
97100
{{definition.Type.Name}} {{definition.Type.Name}}{{templateSpecialization}}::Construct({{string.Join(", ", parameterStrings)}})
98101
{
102+
void* reinteropException = nullptr;
99103
{{definition.Type.Name}} result;
100104
{{interopFunctionName}}({{string.Join(", ", parameterPassStrings)}});
105+
if (reinteropException != nullptr)
106+
throw Reinterop::ReinteropNativeException(::DotNet::System::Exception(::DotNet::Reinterop::ObjectHandle(reinteropException)));
101107
return result;
102108
}
103109
""",
104110
TypeDefinitionsReferenced: new[]
105111
{
106112
definition.Type,
107113
interopReturnType,
108-
CppObjectHandle.GetCppType(context)
114+
CppObjectHandle.GetCppType(context),
115+
CppReinteropException.GetCppType(context)
109116
}.Concat(parameters.Select(parameter => parameter.Type))
110117
));
111118
}
@@ -124,15 +131,22 @@ private static void GenerateSingleNonStatic(CppGenerationContext context, TypeTo
124131
Content:
125132
$$"""
126133
{{definition.Type.Name}}{{templateSpecialization}}::{{definition.Type.Name}}({{string.Join(", ", parameterStrings)}})
127-
: _handle({{interopFunctionName}}({{string.Join(", ", parameterPassStrings)}}))
134+
: _handle([&]() mutable {
135+
void* reinteropException = nullptr;
136+
void* handle = {{interopFunctionName}}({{string.Join(", ", parameterPassStrings)}});
137+
if (reinteropException != nullptr)
138+
throw Reinterop::ReinteropNativeException(::DotNet::System::Exception(::DotNet::Reinterop::ObjectHandle(reinteropException)));
139+
return handle;
140+
}())
128141
{
129142
}
130143
""",
131144
TypeDefinitionsReferenced: new[]
132145
{
133146
definition.Type,
134147
interopReturnType,
135-
CppObjectHandle.GetCppType(context)
148+
CppObjectHandle.GetCppType(context),
149+
CppReinteropException.GetCppType(context)
136150
}.Concat(parameters.Select(parameter => parameter.Type))
137151
));
138152
}

Reinterop~/CppReinteropException.cs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
namespace Reinterop
2+
{
3+
internal class CppReinteropException
4+
{
5+
public static CppType GetCppType(CppGenerationContext context)
6+
{
7+
List<string> ns = new List<string>();
8+
if (context.BaseNamespace.Length > 0)
9+
ns.Add(context.BaseNamespace);
10+
ns.Add("Reinterop");
11+
12+
// If the first two namespaces are identical, remove the duplication.
13+
// This is to avoid `Reinterop::Reinterop`.
14+
if (ns.Count >= 2 && ns[0] == ns[1])
15+
ns.RemoveAt(0);
16+
17+
return new CppType(InteropTypeKind.ClassWrapper, ns, "ReinteropNativeException", null, 0);
18+
}
19+
20+
public static void Generate(CppGenerationContext context, IDictionary<string, CppSourceFile> sourceFiles)
21+
{
22+
CppType type = GetCppType(context);
23+
24+
string headerPath = Path.Combine(new[] { "include" }.Concat(type.Namespaces).Concat(new[] { type.Name + ".h" }).ToArray());
25+
26+
CppSourceFile? headerFile = null;
27+
if (!sourceFiles.TryGetValue(headerPath, out headerFile))
28+
{
29+
headerFile = new CppSourceFile();
30+
headerFile.IsHeaderFile = true;
31+
headerFile.Filename = headerPath;
32+
sourceFiles.Add(headerPath, headerFile);
33+
}
34+
35+
var headerNamespace = headerFile.GetNamespace(type.GetFullyQualifiedNamespace(false));
36+
headerNamespace.Members.Add(
37+
$$"""
38+
class ReinteropNativeException : public std::runtime_error {
39+
public:
40+
ReinteropNativeException(const DotNet::System::Exception& exception);
41+
const ::DotNet::System::Exception& GetDotNetException() const;
42+
43+
private:
44+
::DotNet::System::Exception _exception;
45+
};
46+
""");
47+
48+
headerFile.Includes.Add("<stdexcept>");
49+
50+
CppType exceptionType = new CppType(InteropTypeKind.ClassWrapper, new[] { "DotNet", "System" }, "Exception", null, 0);
51+
exceptionType.AddSourceIncludesToSet(headerFile.Includes);
52+
53+
string sourcePath = Path.Combine("src", type.Name + ".cpp");
54+
55+
CppSourceFile? sourceFile = null;
56+
if (!sourceFiles.TryGetValue(sourcePath, out sourceFile))
57+
{
58+
sourceFile = new CppSourceFile();
59+
sourceFile.IsHeaderFile = false;
60+
sourceFile.Filename = sourcePath;
61+
sourceFiles.Add(sourcePath, sourceFile);
62+
}
63+
64+
type.AddSourceIncludesToSet(sourceFile.Includes);
65+
66+
CppType stringType = new CppType(InteropTypeKind.ClassWrapper, new[] { "DotNet", "System" }, "String", null, 0);
67+
stringType.AddSourceIncludesToSet(sourceFile.Includes);
68+
69+
var sourceNamespace = sourceFile.GetNamespace(type.GetFullyQualifiedNamespace(false));
70+
sourceNamespace.Members.Add(
71+
$$"""
72+
ReinteropNativeException::ReinteropNativeException(const DotNet::System::Exception& exception)
73+
: std::runtime_error(exception.Message().ToStlString()),
74+
_exception(exception) {}
75+
76+
const ::DotNet::System::Exception& ReinteropNativeException::GetDotNetException() const {
77+
return this->_exception;
78+
}
79+
""");
80+
}
81+
}
82+
}

Reinterop~/CppType.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ internal enum CppTypeFlags
77
{
88
Pointer = 1,
99
Reference = 2,
10-
Const = 4
10+
Const = 4,
11+
DoublePointer = 9 // A double pointer is also a pointer
1112
}
1213

1314
/// <summary>
@@ -42,6 +43,7 @@ internal class CppType
4243
public static readonly CppType Single = CreatePrimitiveType(NoNamespace, "float");
4344
public static readonly CppType Double = CreatePrimitiveType(NoNamespace, "double");
4445
public static readonly CppType VoidPointer = CreatePrimitiveType(NoNamespace, "void", CppTypeFlags.Pointer);
46+
public static readonly CppType VoidPointerPointer = CreatePrimitiveType(NoNamespace, "void", CppTypeFlags.DoublePointer);
4547
public static readonly CppType Void = CreatePrimitiveType(NoNamespace, "void");
4648
public static readonly CppType NullPointer = CreatePrimitiveType(StandardNamespace, "nullptr_t", 0, IncludeCStdDef);
4749

@@ -201,11 +203,13 @@ public string GetFullyQualifiedName(bool startWithGlobal = true)
201203
}
202204

203205
string modifier = Flags.HasFlag(CppTypeFlags.Const) ? "const " : "";
204-
string suffix = Flags.HasFlag(CppTypeFlags.Pointer)
205-
? "*"
206-
: Flags.HasFlag(CppTypeFlags.Reference)
207-
? "&"
208-
: "";
206+
string suffix = "";
207+
if (Flags.HasFlag(CppTypeFlags.DoublePointer))
208+
suffix = "**";
209+
else if (Flags.HasFlag(CppTypeFlags.Pointer))
210+
suffix = "*";
211+
else if (Flags.HasFlag(CppTypeFlags.Reference))
212+
suffix = "&";
209213
string ns = GetFullyQualifiedNamespace(startWithGlobal);
210214
if (ns.Length > 0)
211215
return $"{modifier}{ns}::{Name}{template}{suffix}";

Reinterop~/Fields.cs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ private static void GenerateSingleFieldAccessors(CppGenerationContext context, T
9090

9191
bool hasStructRewrite = Interop.RewriteStructReturn(ref getParameters, ref getType, ref getInteropType);
9292

93+
// Add a parameter in which to return the exception, if there is one.
94+
getParameters = getParameters.Concat(new[] { (ParameterName: "reinteropException", CallSiteName: "&reinteropException", Type: CppType.VoidPointerPointer, InteropType: CppType.VoidPointerPointer) });
95+
interopSetParameters = interopSetParameters + ", void** reinteropException";
96+
9397
var interopGetParameters = getParameters.Select(parameter => $"{parameter.InteropType.GetFullyQualifiedName()} {parameter.ParameterName}");
9498
var interopGetParametersCall = getParameters.Select(parameter => parameter.Type.GetConversionToInteropType(context, parameter.CallSiteName));
9599

@@ -145,7 +149,11 @@ private static void GenerateSingleFieldAccessors(CppGenerationContext context, T
145149

146150
string[] invocation = new[]
147151
{
152+
$"void* reinteropException = nullptr;",
148153
$"auto result = Field_get_{field.Name}({string.Join(", ", interopGetParametersCall)});",
154+
$"if (reinteropException != nullptr) {{",
155+
$" throw Reinterop::ReinteropNativeException(::DotNet::System::Exception(::DotNet::Reinterop::ObjectHandle(reinteropException)));",
156+
$"}}",
149157
$"return {getType.GetConversionFromInteropType(context, "result")};"
150158
};
151159
if (hasStructRewrite)
@@ -154,17 +162,25 @@ private static void GenerateSingleFieldAccessors(CppGenerationContext context, T
154162
{
155163
invocation = new[]
156164
{
165+
$"void* reinteropException = nullptr;",
157166
$"{getType.GenericArguments.FirstOrDefault().GetFullyQualifiedName()} result;",
158167
$"std::uint8_t resultIsValid = Field_get_{field.Name}({string.Join(", ", interopGetParametersCall)});",
168+
$"if (reinteropException != nullptr) {{",
169+
$" throw Reinterop::ReinteropNativeException(::DotNet::System::Exception(::DotNet::Reinterop::ObjectHandle(reinteropException)));",
170+
$"}}",
159171
$"return resultIsValid ? std::make_optional(std::move({getType.GetConversionFromInteropType(context, "result")})) : std::nullopt;"
160172
};
161173
}
162174
else
163175
{
164176
invocation = new[]
165177
{
178+
$"void* reinteropException = nullptr;",
166179
$"{getType.GetFullyQualifiedName()} result;",
167180
$"Field_get_{field.Name}({string.Join(", ", interopGetParametersCall)});",
181+
$"if (reinteropException != nullptr) {{",
182+
$" throw Reinterop::ReinteropNativeException(::DotNet::System::Exception(::DotNet::Reinterop::ObjectHandle(reinteropException)));",
183+
$"}}",
168184
$"return {getType.GetConversionFromInteropType(context, "result")};"
169185
};
170186
}
@@ -181,22 +197,28 @@ private static void GenerateSingleFieldAccessors(CppGenerationContext context, T
181197
{
182198
definition.Type,
183199
getType,
184-
CppObjectHandle.GetCppType(context)
200+
CppObjectHandle.GetCppType(context),
201+
CppReinteropException.GetCppType(context)
185202
}
186203
));
187204

188205
definition.Elements.Add(new(
189206
Content:
190207
$$"""
191208
void {{definition.Type.Name}}::{{field.Name}}({{setType.GetFullyQualifiedName()}} value){{(field.IsStatic ? "" : " const")}} {
192-
Field_set_{{field.Name}}({{interopSetParametersCall}});
209+
void* reinteropException = nullptr;
210+
Field_set_{{field.Name}}({{interopSetParametersCall}}, &reinteropException);
211+
if (reinteropException != nullptr) {
212+
throw Reinterop::ReinteropNativeException(::DotNet::System::Exception(::DotNet::Reinterop::ObjectHandle(reinteropException)));
213+
}
193214
}
194215
""",
195216
TypeDefinitionsReferenced: new[]
196217
{
197218
definition.Type,
198219
setType,
199-
CppObjectHandle.GetCppType(context)
220+
CppObjectHandle.GetCppType(context),
221+
CppReinteropException.GetCppType(context)
200222
}
201223
));
202224
}

0 commit comments

Comments
 (0)