fix: store reference to material instances in ThermionViewer so they can be cleaned up on dispose

This commit is contained in:
Nick Fisher
2024-11-27 14:19:18 +08:00
parent bedd50ec38
commit c2077cb6b1
7 changed files with 101 additions and 51 deletions

View File

@@ -122,7 +122,7 @@ class FFIAsset extends ThermionAsset {
}
var sourceMaterialInstance = ThermionFFIMaterialInstance(
RenderableManager_getMaterialInstanceAt(
renderableManager, targetEntity, 0));
renderableManager, targetEntity, 0), sceneManager);
await sourceMaterialInstance.setStencilWriteEnabled(true);
await sourceMaterialInstance.setDepthWriteEnabled(true);
@@ -138,7 +138,7 @@ class FFIAsset extends ThermionAsset {
_unlitMaterialProvider, key.address, cb);
});
final highlightMaterialInstance =
ThermionFFIMaterialInstance(materialInstancePtr);
ThermionFFIMaterialInstance(materialInstancePtr, sceneManager);
await highlightMaterialInstance
.setStencilCompareFunction(SamplerCompareFunction.NE);
await highlightMaterialInstance.setStencilReferenceValue(1);

View File

@@ -6,8 +6,9 @@ import 'package:thermion_dart/thermion_dart.dart';
class ThermionFFIMaterialInstance extends MaterialInstance {
final Pointer<TMaterialInstance> pointer;
final Pointer<TSceneManager> sceneManager;
ThermionFFIMaterialInstance(this.pointer) {
ThermionFFIMaterialInstance(this.pointer, this.sceneManager) {
if (pointer == nullptr) {
throw Exception("MaterialInstance not found");
}
@@ -50,14 +51,17 @@ class ThermionFFIMaterialInstance extends MaterialInstance {
@override
Future setDepthFunc(SamplerCompareFunction depthFunc) async {
MaterialInstance_setDepthFunc(pointer, TSamplerCompareFunc.values[depthFunc.index]);
MaterialInstance_setDepthFunc(
pointer, TSamplerCompareFunc.values[depthFunc.index]);
}
@override
Future setStencilCompareFunction(SamplerCompareFunction func,
[StencilFace face = StencilFace.FRONT_AND_BACK]) async {
MaterialInstance_setStencilCompareFunction(
pointer, TSamplerCompareFunc.values[func.index], TStencilFace.values[face.index]);
pointer,
TSamplerCompareFunc.values[func.index],
TStencilFace.values[face.index]);
}
@override
@@ -113,4 +117,8 @@ class ThermionFFIMaterialInstance extends MaterialInstance {
Future setStencilWriteMask(int mask) async {
MaterialInstance_setStencilWriteMask(pointer, mask);
}
Future dispose() async {
SceneManager_destroyMaterialInstance(sceneManager, pointer);
}
}

View File

@@ -282,6 +282,8 @@ class ThermionViewerFFI extends ThermionViewer {
final _onDispose = <Future Function()>[];
bool _disposing = false;
final _materialInstances = <ThermionFFIMaterialInstance>[];
///
///
///
@@ -291,12 +293,13 @@ class ThermionViewerFFI extends ThermionViewer {
throw Exception("Viewer has already been disposed.");
}
_disposing = true;
await setRendering(false);
await clearEntities();
for (final mInstance in _materialInstances) {
await mInstance.dispose();
}
await clearLights();
// await _sceneUpdateEventController.close();
Viewer_destroyOnRenderThread(_viewer!);
_sceneManager = null;
_viewer = null;
@@ -984,9 +987,6 @@ class ThermionViewerFFI extends ThermionViewer {
_sceneManager!, asset.pointer, callback));
}
///
///
///
///
///
///
@@ -1880,7 +1880,10 @@ class ThermionViewerFFI extends ThermionViewer {
throw Exception("Failed to create material instance");
}
return ThermionFFIMaterialInstance(materialInstance);
var instance =
ThermionFFIMaterialInstance(materialInstance, _sceneManager!);
_materialInstances.add(instance);
return instance;
}
///
@@ -1888,21 +1891,20 @@ class ThermionViewerFFI extends ThermionViewer {
///
Future destroyMaterialInstance(
ThermionFFIMaterialInstance materialInstance) async {
SceneManager_destroyMaterialInstance(
_sceneManager!, materialInstance.pointer);
await materialInstance.dispose();
_materialInstances.remove(materialInstance);
}
///
///
///
Future<ThermionFFIMaterialInstance> createUnlitMaterialInstance() async {
var instance = await withPointerCallback<TMaterialInstance>((cb) {
var instancePtr = await withPointerCallback<TMaterialInstance>((cb) {
SceneManager_createUnlitMaterialInstanceRenderThread(_sceneManager!, cb);
});
if (instance == nullptr) {
throw Exception("Failed to create material instance");
}
return ThermionFFIMaterialInstance(instance);
final instance = ThermionFFIMaterialInstance(instancePtr, _sceneManager!);
_materialInstances.add(instance);
return instance;
}
///
@@ -1910,14 +1912,13 @@ class ThermionViewerFFI extends ThermionViewer {
///
Future<ThermionFFIMaterialInstance>
createUnlitFixedSizeMaterialInstance() async {
var instance = await withPointerCallback<TMaterialInstance>((cb) {
var instancePtr = await withPointerCallback<TMaterialInstance>((cb) {
SceneManager_createUnlitFixedSizeMaterialInstanceRenderThread(
_sceneManager!, cb);
});
if (instance == nullptr) {
throw Exception("Failed to create material instance");
}
return ThermionFFIMaterialInstance(instance);
final instance = ThermionFFIMaterialInstance(instancePtr, _sceneManager!);
_materialInstances.add(instance);
return instance;
}
///
@@ -1925,12 +1926,11 @@ class ThermionViewerFFI extends ThermionViewer {
///
Future<MaterialInstance> getMaterialInstanceAt(
ThermionEntity entity, int index) async {
final instance = RenderableManager_getMaterialInstanceAt(
final instancePtr = RenderableManager_getMaterialInstanceAt(
_renderableManager!, entity, index);
if (instance == nullptr) {
throw Exception("Failed to get material instance");
}
return ThermionFFIMaterialInstance(instance);
final instance = ThermionFFIMaterialInstance(instancePtr, _sceneManager!);
return instance;
}
@override
@@ -2036,8 +2036,8 @@ class ThermionViewerFFI extends ThermionViewer {
SceneManager_getOverlayEntityCount(_sceneManager!);
final overlayEntities = List<ThermionEntity>.generate(overlayEntityCount,
(i) => SceneManager_getOverlayEntityAt(_sceneManager!, i)).toSet();
return FFIGizmo(
view, gizmo.cast<TSceneAsset>(), _sceneManager!, _engine!, nullptr, overlayEntities);
return FFIGizmo(view, gizmo.cast<TSceneAsset>(), _sceneManager!, _engine!,
nullptr, overlayEntities);
}
}

View File

@@ -67,7 +67,7 @@ enum StencilFace {
BACK,
/// Both front and back faces
FRONT_AND_BACK
FRONT_AND_BACK
}
enum AlphaMode { OPAQUE, MASK, BLEND }
@@ -109,4 +109,5 @@ abstract class MaterialInstance {
Future setStencilReadMask(int mask);
Future setStencilWriteMask(int mask);
}

View File

@@ -792,28 +792,28 @@ abstract class ThermionViewer {
AlphaMode alphaMode = AlphaMode.OPAQUE,
bool enableDiagnostics = false,
bool hasMetallicRoughnessTexture = false,
int metallicRoughnessUV = 0,
int baseColorUV = 0,
int metallicRoughnessUV = -1,
int baseColorUV = -1,
bool hasClearCoatTexture = false,
int clearCoatUV = 0,
int clearCoatUV = -1,
bool hasClearCoatRoughnessTexture = false,
int clearCoatRoughnessUV = 0,
int clearCoatRoughnessUV = -1,
bool hasClearCoatNormalTexture = false,
int clearCoatNormalUV = 0,
int clearCoatNormalUV = -1,
bool hasClearCoat = false,
bool hasTransmission = false,
bool hasTextureTransforms = false,
int emissiveUV = 0,
int aoUV = 0,
int normalUV = 0,
int emissiveUV = -1,
int aoUV = -1,
int normalUV = -1,
bool hasTransmissionTexture = false,
int transmissionUV = 0,
int transmissionUV = -1,
bool hasSheenColorTexture = false,
int sheenColorUV = 0,
int sheenColorUV = -1,
bool hasSheenRoughnessTexture = false,
int sheenRoughnessUV = 0,
int sheenRoughnessUV = -1,
bool hasVolumeThicknessTexture = false,
int volumeThicknessUV = 0,
int volumeThicknessUV = -1,
bool hasSheen = false,
bool hasIOR = false,
bool hasVolume = false,

View File

@@ -41,9 +41,6 @@
#include "scene/GeometrySceneAssetBuilder.hpp"
#include "UnprojectTexture.hpp"
extern "C"
{
#include "material/image.h"
@@ -130,11 +127,12 @@ namespace thermion
_engine->getEntityManager().destroy(entity);
}
destroyAll();
_engine->destroy(_unlitFixedSizeMaterial);
_cameras.clear();
_gridOverlay->destroy();
destroyAll();
_gltfResourceLoader->asyncCancelLoad();
_ubershaderProvider->destroyMaterials();
@@ -153,7 +151,7 @@ namespace thermion
Gizmo *SceneManager::createGizmo(View *view, Scene *scene)
{
auto gizmo = std::make_unique<Gizmo>(_engine, view, scene, _unlitFixedSizeMaterial);
auto *raw =gizmo.get();
auto *raw = gizmo.get();
_sceneAssets.push_back(std::move(gizmo));
return raw;
}
@@ -682,7 +680,6 @@ namespace thermion
math::float4 entityPlaneInClipSpace = Camera::inverseProjection(camera.getProjectionMatrix()) * ndcEntityPlanePos;
auto entityPlaneInCameraSpace = entityPlaneInClipSpace / entityPlaneInClipSpace.w;
auto entityPlaneInWorldSpace = camera.getModelMatrix() * entityPlaneInCameraSpace;
}
void SceneManager::queueTransformUpdates(EntityId *entities, math::mat4 *transforms, int numEntities)

View File

@@ -13,16 +13,30 @@ import 'helpers.dart';
void main() async {
final testHelper = TestHelper("geometry");
group("custom geometry", () {
test('create cube', () async {
test('create cube (no normals/uvs)', () async {
await testHelper.withViewer((viewer) async {
final cube = await viewer
.createGeometry(GeometryHelper.cube(normals: false, uvs: false));
await testHelper.capture(viewer, "geometry_add_cube");
await testHelper.capture(viewer, "geometry_cube_no_normals_uvs");
await viewer.removeEntity(cube);
await testHelper.capture(viewer, "geometry_remove_cube");
});
});
test('create cube with unlit ubershader material (no normals/uvs)',
() async {
await testHelper.withViewer((viewer) async {
final materialInstance =
await viewer.createUbershaderMaterialInstance(unlit: true);
await materialInstance.setParameterFloat4(
"baseColorFactor", 1.0, 0.0, 0.0, 1.0);
final cube = await viewer.createGeometry(
GeometryHelper.cube(normals: false, uvs: false),
materialInstances: [materialInstance]);
await testHelper.capture(viewer, "geometry_cube_ubershader");
});
});
test('create cube (with normals)', () async {
var viewer = await testHelper.createViewer();
await viewer
@@ -30,6 +44,36 @@ void main() async {
await testHelper.capture(viewer, "geometry_cube_with_normals");
});
test('create cube with lit ubershader material (normals/ no uvs)',
() async {
await testHelper.withViewer((viewer) async {
final materialInstance =
await viewer.createUbershaderMaterialInstance(
unlit: false,
alphaMode: AlphaMode.BLEND,
hasVertexColors:false);
await materialInstance.setParameterFloat4(
"baseColorFactor", 1.0, 0.0, 0.0, 1.0);
final cube = await viewer.createGeometry(
GeometryHelper.cube(normals: true, uvs: false),
materialInstances: [materialInstance]
);
await viewer.addDirectLight(DirectLight.sun(
intensity: 100000,
castShadows: false,
direction: Vector3(0,-0.5,-1)));
// await viewer.addDirectLight(DirectLight.spot(
// intensity: 1000000,
// position: Vector3(0,3,3),
// direction: Vector3(0,-1.5,-1),
// falloffRadius: 10));
await materialInstance.setParameterFloat4(
"baseColorFactor", 1.0, 0.0, 0.0, 1.0);
await testHelper.capture(viewer, "geometry_cube_lit_ubershader");
});
});
test('create instance', () async {
await testHelper.withViewer((viewer) async {
final cube = await viewer