Files
tinyusdz/web/tests/test-memory-view.js
Syoyo Fujita 9e1c785ec5 Add zero-copy asset loading and memory view support to EMAssetResolver
This commit enhances the WebAssembly bindings with two major improvements:

1. **Zero-Copy Asset Loading** (`setAssetFromRawPointer`):
   - Direct Uint8Array access using raw pointers
   - Eliminates intermediate copying during JS↔C++ transfer
   - 67% reduction in memory copies (from 3 to 1)
   - Optimal performance for large binary assets (textures, meshes, USD files)

2. **Memory View Access** (`getAssetCacheDataAsMemoryView`):
   - Direct typed memory view of cached asset data
   - Returns Uint8Array for existing assets, undefined otherwise
   - Consistent with existing getAsset method

**Technical Details:**
- Added AssetCacheEntry struct with SHA-256 hash validation
- Implemented raw pointer method with emscripten::allow_raw_pointers()
- Enhanced error handling and data integrity checks
- Backward compatible with existing setAsset/getAsset methods

**JavaScript Usage:**
```javascript
// Zero-copy loading
const dataPtr = Module.HEAPU8.subarray(uint8Array.byteOffset,
                  uint8Array.byteOffset + uint8Array.byteLength).byteOffset;
loader.setAssetFromRawPointer('texture.jpg', dataPtr, uint8Array.length);

// Direct memory view
const memView = loader.getAssetCacheDataAsMemoryView('texture.jpg');
```

**Testing:**
- Comprehensive Node.js test suite with mock implementations
- Performance benchmarking utilities
- Data integrity validation
- Zero-copy helper functions for real-world usage

Ideal for USD workflows with large textures, geometry data, and binary scene files.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-21 02:17:24 +09:00

145 lines
5.4 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Test for getAssetCacheDataAsMemoryView method
*
* This test verifies that the new method returns cache data as a memory view
* and that it behaves correctly for both existing and non-existing assets.
*/
const fs = require('fs');
const path = require('path');
// Load TinyUSDZ module
const TinyUSDZModule = require('../js/src/tinyusdz/tinyusdz.js');
async function runTest() {
console.log('Loading TinyUSDZ module...');
try {
const tinyusdz = await TinyUSDZModule();
console.log('✓ TinyUSDZ module loaded successfully');
// Create a loader instance
const loader = new tinyusdz.TinyUSDZLoaderNative();
console.log('✓ Loader created');
// Test data - simple string content
const testAssetName = 'test-asset.txt';
const testContent = 'Hello, World! This is test content for memory view.';
const expectedSize = testContent.length;
// Set the asset in cache
console.log('Setting test asset in cache...');
loader.setAsset(testAssetName, testContent);
console.log(`✓ Asset '${testAssetName}' set with content: "${testContent}"`);
// Verify the asset exists
const hasAsset = loader.hasAsset(testAssetName);
console.log(`✓ Asset exists check: ${hasAsset}`);
if (!hasAsset) {
throw new Error('Asset should exist after being set');
}
// Test the new getAssetCacheDataAsMemoryView method
console.log('Testing getAssetCacheDataAsMemoryView...');
const memoryView = loader.getAssetCacheDataAsMemoryView(testAssetName);
if (memoryView === undefined) {
throw new Error('Memory view should not be undefined for existing asset');
}
console.log('✓ Memory view is not undefined');
// Check if it's a typed array
if (!(memoryView instanceof Uint8Array)) {
throw new Error('Memory view should be a Uint8Array');
}
console.log('✓ Memory view is a Uint8Array');
// Check size
if (memoryView.length !== expectedSize) {
throw new Error(`Expected size ${expectedSize}, got ${memoryView.length}`);
}
console.log(`✓ Memory view has correct size: ${memoryView.length} bytes`);
// Check content by converting back to string
const decoder = new TextDecoder();
const retrievedContent = decoder.decode(memoryView);
if (retrievedContent !== testContent) {
throw new Error(`Content mismatch. Expected: "${testContent}", Got: "${retrievedContent}"`);
}
console.log(`✓ Content matches: "${retrievedContent}"`);
// Test with non-existing asset
console.log('Testing with non-existing asset...');
const nonExistingMemoryView = loader.getAssetCacheDataAsMemoryView('non-existing-asset');
if (nonExistingMemoryView !== undefined) {
throw new Error('Memory view should be undefined for non-existing asset');
}
console.log('✓ Non-existing asset returns undefined as expected');
// Test with binary data
console.log('Testing with binary data...');
const binaryAssetName = 'binary-test.bin';
const binaryData = new Uint8Array([0x00, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD, 0xFC]);
const binaryString = String.fromCharCode(...binaryData);
loader.setAsset(binaryAssetName, binaryString);
const binaryMemoryView = loader.getAssetCacheDataAsMemoryView(binaryAssetName);
if (binaryMemoryView.length !== binaryData.length) {
throw new Error(`Binary data size mismatch. Expected: ${binaryData.length}, Got: ${binaryMemoryView.length}`);
}
for (let i = 0; i < binaryData.length; i++) {
if (binaryMemoryView[i] !== binaryData[i]) {
throw new Error(`Binary data mismatch at index ${i}. Expected: ${binaryData[i]}, Got: ${binaryMemoryView[i]}`);
}
}
console.log('✓ Binary data memory view works correctly');
// Test comparison with existing getAsset method
console.log('Comparing with existing getAsset method...');
const assetObj = loader.getAsset(testAssetName);
if (!assetObj || !assetObj.data) {
throw new Error('getAsset should return object with data');
}
// Both should have the same content
if (assetObj.data.length !== memoryView.length) {
throw new Error('getAsset and getAssetCacheDataAsMemoryView should return same size data');
}
for (let i = 0; i < memoryView.length; i++) {
if (assetObj.data[i] !== memoryView[i]) {
throw new Error(`Data mismatch at index ${i} between getAsset and getAssetCacheDataAsMemoryView`);
}
}
console.log('✓ getAssetCacheDataAsMemoryView returns same data as getAsset');
console.log('\n🎉 All tests passed!');
console.log('✓ getAssetCacheDataAsMemoryView method works correctly');
console.log('✓ Returns Uint8Array memory view for existing assets');
console.log('✓ Returns undefined for non-existing assets');
console.log('✓ Handles both text and binary data correctly');
console.log('✓ Consistent with existing getAsset method');
} catch (error) {
console.error('\n❌ Test failed:', error.message);
console.error('Stack trace:', error.stack);
process.exit(1);
}
}
// Run the test
if (require.main === module) {
console.log('='.repeat(60));
console.log('Testing getAssetCacheDataAsMemoryView method');
console.log('='.repeat(60));
runTest().catch((error) => {
console.error('\n❌ Unexpected error:', error);
process.exit(1);
});
}
module.exports = { runTest };