refactoring

This commit is contained in:
Nick Fisher
2025-03-19 17:54:43 +08:00
parent 124f923720
commit 6744c02019
23 changed files with 1074 additions and 1278 deletions

View File

@@ -5,7 +5,7 @@ import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_dart.g.dart';
import 'package:thermion_dart/src/filament/src/layers.dart';
import 'package:thermion_dart/thermion_dart.dart';
import 'package:vector_math/vector_math_64.dart';
import '../../../../utils/src/matrix.dart';
class FFICamera extends Camera {

View File

@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:thermion_dart/src/filament/src/engine.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_material.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
@@ -14,12 +15,12 @@ typedef RenderCallback = Pointer<NativeFunction<Void Function(Pointer<Void>)>>;
class FFIFilamentConfig extends FilamentConfig<RenderCallback, Pointer<Void>> {
FFIFilamentConfig(
{required super.backend,
required super.resourceLoader,
required super.driver,
required super.platform,
required super.sharedContext,
required super.uberArchivePath});
{required super.resourceLoader,
super.backend = Backend.DEFAULT,
super.driver = null,
super.platform = null,
super.sharedContext = null,
super.uberArchivePath = null});
}
class FFIFilamentApp extends FilamentApp<Pointer> {
@@ -55,7 +56,8 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
renderableManager: renderableManager,
ubershaderMaterialProvider: ubershaderMaterialProvider) {}
static Future create(FFIFilamentConfig config) async {
static Future create({FFIFilamentConfig? config}) async {
config ??= FFIFilamentConfig(resourceLoader: nullptr);
if (FilamentApp.instance != null) {
await FilamentApp.instance!.destroy();
}
@@ -65,7 +67,7 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
final engine = await withPointerCallback<TEngine>((cb) =>
Engine_createRenderThread(
TBackend.values[config.backend.index].index,
TBackend.values[config!.backend.index].index,
config.platform ?? nullptr,
config.sharedContext ?? nullptr,
config.stereoscopicEyeCount,
@@ -79,15 +81,13 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
final renderer = await withPointerCallback<TRenderer>(
(cb) => Engine_createRendererRenderThread(engine, cb));
final ubershaderMaterialProvider =
await withPointerCallback<TMaterialProvider>(
(cb) => GltfAssetLoader_getMaterialProvider(gltfAssetLoader));
GltfAssetLoader_getMaterialProvider(gltfAssetLoader);
final transformManager = Engine_getTransformManager(engine);
final lightManager = Engine_getLightManager(engine);
final renderableManager = Engine_getRenderableManager(engine);
final renderTicker = await withPointerCallback<TRenderTicker>(
(cb) => RenderTicker_create(renderer));
final renderTicker = RenderTicker_create(renderer);
final nameComponentManager = NameComponentManager_create();
@@ -210,7 +210,8 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
TextureSamplerType textureSamplerType = TextureSamplerType.SAMPLER_2D,
TextureFormat textureFormat = TextureFormat.RGBA16F,
int? importedTextureHandle}) async {
var bitmask = flags.fold(0, (a, b) => a | b.index);
var bitmask = flags.fold(0, (a, b) => a | b.value);
print("bitmask $bitmask");
final texturePtr = await withPointerCallback<TTexture>((cb) {
Texture_buildRenderThread(
engine,
@@ -218,8 +219,8 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
height,
depth,
levels,
importedTextureHandle ?? 0,
bitmask,
importedTextureHandle ?? 0,
TTextureSamplerType.values[textureSamplerType.index],
TTextureFormat.values[textureFormat.index],
cb);
@@ -404,7 +405,7 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
FFIMaterial? _gridMaterial;
Future<FFIMaterial> get gridMaterial async {
_gridMaterial ??= FFIMaterial(Material_createGridMaterial(), this);
_gridMaterial ??= FFIMaterial(Material_createGridMaterial(engine), this);
return _gridMaterial!;
}
@@ -425,8 +426,8 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
///
@override
Future render() async {
await withVoidCallback((cb) =>
RenderTicker_renderRenderThread(renderTicker, 0, cb));
await withVoidCallback(
(cb) => RenderTicker_renderRenderThread(renderTicker, 0, cb));
}
///
@@ -436,6 +437,10 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
Future register(
covariant FFISwapChain swapChain, covariant FFIView view) async {
_viewMappings[view] = swapChain;
if (!_views.containsKey(swapChain)) {
_views[swapChain] = [];
}
_views[swapChain]!.add(view);
}
final _hooks = <Future Function()>[];
@@ -531,9 +536,12 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
Material? _imageMaterial;
///
///
///
@override
Future<MaterialInstance> createImageMaterialInstance() async {
_imageMaterial ??= FFIMaterial(Material_createImageMaterial(),
_imageMaterial ??= FFIMaterial(Material_createImageMaterial(engine),
FilamentApp.instance! as FFIFilamentApp);
var instance =
await _imageMaterial!.createInstance() as FFIMaterialInstance;

View File

@@ -4,7 +4,6 @@ import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_texture.dart';
import 'package:thermion_dart/thermion_dart.dart';
import 'package:vector_math/vector_math_64.dart';
class FFIMaterial extends Material {
final FFIFilamentApp app;

View File

@@ -1,5 +1,3 @@
import 'dart:ffi';
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
import 'package:thermion_dart/thermion_dart.dart';

View File

@@ -107,7 +107,7 @@ class FFIView extends View {
}
Future setScene(covariant FFIScene scene) async {
await withVoidCallback((cb) => View_setScene(view, scene.scene));
View_setScene(view, scene.scene);
}
@override

View File

@@ -25,11 +25,15 @@ external ffi.Pointer<TMaterialInstance> Material_createInstance(
ffi.Pointer<TMaterial> tMaterial,
);
@ffi.Native<ffi.Pointer<TMaterial> Function()>(isLeaf: true)
external ffi.Pointer<TMaterial> Material_createImageMaterial();
@ffi.Native<ffi.Pointer<TMaterial> Function(ffi.Pointer<TEngine>)>(isLeaf: true)
external ffi.Pointer<TMaterial> Material_createImageMaterial(
ffi.Pointer<TEngine> tEngine,
);
@ffi.Native<ffi.Pointer<TMaterial> Function()>(isLeaf: true)
external ffi.Pointer<TMaterial> Material_createGridMaterial();
@ffi.Native<ffi.Pointer<TMaterial> Function(ffi.Pointer<TEngine>)>(isLeaf: true)
external ffi.Pointer<TMaterial> Material_createGridMaterial(
ffi.Pointer<TEngine> tEngine,
);
@ffi.Native<ffi.Bool Function(ffi.Pointer<TMaterial>, ffi.Pointer<ffi.Char>)>(
isLeaf: true)
@@ -1491,9 +1495,11 @@ external void RenderLoop_create();
@ffi.Native<ffi.Void Function()>(isLeaf: true)
external void RenderLoop_destroy();
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>)>(isLeaf: true)
@ffi.Native<
ffi.Void Function(
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
external void RenderLoop_requestAnimationFrame(
ffi.Pointer<ffi.Void> onComplete,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
);
@ffi.Native<
@@ -3847,7 +3853,7 @@ enum TGizmoType {
};
}
sealed class TPrimitiveType {
abstract class TPrimitiveType {
/// !< points
static const PRIMITIVETYPE_POINTS = 0;
@@ -4265,7 +4271,7 @@ enum TTextureFormat {
}
/// ! Pixel Data Format
sealed class TPixelDataFormat {
abstract class TPixelDataFormat {
/// !< One Red channel, float
static const PIXELDATAFORMAT_R = 0;
@@ -4299,7 +4305,7 @@ sealed class TPixelDataFormat {
static const PIXELDATAFORMAT_ALPHA = 11;
}
sealed class TPixelDataType {
abstract class TPixelDataType {
/// !< unsigned byte
static const PIXELDATATYPE_UBYTE = 0;

View File

@@ -95,6 +95,7 @@ class ThermionViewerFFI extends ThermionViewer {
_cameras.add(camera);
await view.setCamera(camera);
if (renderTarget != null) {
await view.setRenderTarget(renderTarget);
}
@@ -149,14 +150,12 @@ class ThermionViewerFFI extends ThermionViewer {
}
final _onDispose = <Future Function()>[];
bool _disposing = false;
///
///
///
@override
Future dispose() async {
_disposing = true;
await setRendering(false);
await destroyAssets();
await destroyLights();
@@ -166,7 +165,6 @@ class ThermionViewerFFI extends ThermionViewer {
}
_onDispose.clear();
_disposing = false;
}
///

View File

@@ -25,10 +25,6 @@
namespace thermion
{
typedef std::chrono::time_point time_point_t;
using namespace std::chrono;
class RenderTicker
{

View File

@@ -68,8 +68,8 @@ extern "C"
};
EMSCRIPTEN_KEEPALIVE TMaterialInstance *Material_createInstance(TMaterial *tMaterial);
EMSCRIPTEN_KEEPALIVE TMaterial *Material_createImageMaterial();
EMSCRIPTEN_KEEPALIVE TMaterial *Material_createGridMaterial();
EMSCRIPTEN_KEEPALIVE TMaterial *Material_createImageMaterial(TEngine *tEngine);
EMSCRIPTEN_KEEPALIVE TMaterial *Material_createGridMaterial(TEngine *tEngine);
EMSCRIPTEN_KEEPALIVE bool Material_hasParameter(TMaterial *tMaterial, const char *propertyName);
EMSCRIPTEN_KEEPALIVE bool MaterialInstance_isStencilWriteEnabled(TMaterialInstance *materialInstance);
EMSCRIPTEN_KEEPALIVE void MaterialInstance_setStencilWrite(TMaterialInstance *materialInstance, bool enabled);

View File

@@ -16,7 +16,7 @@ namespace thermion
EMSCRIPTEN_KEEPALIVE void RenderLoop_create();
EMSCRIPTEN_KEEPALIVE void RenderLoop_destroy();
EMSCRIPTEN_KEEPALIVE void RenderLoop_requestAnimationFrame(void (*onComplete));
EMSCRIPTEN_KEEPALIVE void RenderLoop_requestAnimationFrame(void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void RenderTicker_renderRenderThread(TRenderTicker *tRenderTicker, uint64_t frameTimeInNanos, void (*onComplete)());
// EMSCRIPTEN_KEEPALIVE void RenderLoop_addTask(TRenderLoop* tRenderLoop, void (*task)());
@@ -52,6 +52,7 @@ namespace thermion
TTextureFormat format,
void (*onComplete)(TTexture*)
);
EMSCRIPTEN_KEEPALIVE void Engine_destroyTextureRenderThread(TEngine *engine, TTexture* tTexture, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Engine_createFenceRenderThread(TEngine *tEngine, void (*onComplete)(TFence*));
EMSCRIPTEN_KEEPALIVE void Engine_destroyFenceRenderThread(TEngine *tEngine, TFence *tFence, void (*onComplete)());
@@ -175,18 +176,6 @@ namespace thermion
EMSCRIPTEN_KEEPALIVE void Image_getHeightRenderThread(TLinearImage *tLinearImage, void (*onComplete)(uint32_t));
EMSCRIPTEN_KEEPALIVE void Image_getChannelsRenderThread(TLinearImage *tLinearImage, void (*onComplete)(uint32_t));
// Texture methods
EMSCRIPTEN_KEEPALIVE void Texture_buildRenderThread(TEngine *engine,
uint32_t width,
uint32_t height,
uint32_t depth,
uint8_t levels,
uint16_t tUsage,
intptr_t import,
TTextureSamplerType sampler,
TTextureFormat format,
void (*onComplete)(TTexture *)
);
EMSCRIPTEN_KEEPALIVE void Texture_loadImageRenderThread(
TEngine *tEngine,
@@ -292,8 +281,6 @@ namespace thermion
void (*onComplete)()
);
EMSCRIPTEN_KEEPALIVE void AnimationManager_updateBoneMatricesRenderThread(TSceneManager *sceneManager,
EntityId asset, void (*callback)(bool));
EMSCRIPTEN_KEEPALIVE void AnimationManager_setBoneTransformRenderThread(
TAnimationManager *tAnimationManager,
EntityId asset,

View File

@@ -1,5 +1,7 @@
#pragma once
#include <vector>
#include <utils/Entity.h>
#include <filament/Box.h>
#include <filament/Engine.h>

View File

@@ -37,10 +37,6 @@ public:
void addAllEntities(Scene* scene) override;
void removeAllEntities(Scene* scene) override;
void setPriority(RenderableManager& rm, int priority) override;
void setLayer(RenderableManager& rm, int layer) override;
size_t getInstanceCount() override { return _instances.size(); }
SceneAsset* getInstanceByEntity(utils::Entity entity) override;
SceneAsset* getInstanceAt(size_t index) override;

View File

@@ -227,23 +227,7 @@ namespace thermion
scene->remove(_gridEntity);
// scene->remove(_sphereEntity);
}
void GridOverlay::setPriority(RenderableManager &rm, int priority)
{
auto gridInstance = rm.getInstance(_gridEntity);
rm.setPriority(gridInstance, priority);
// auto sphereInstance = rm.getInstance(_sphereEntity);
// rm.setPriority(sphereInstance, priority);
}
void GridOverlay::setLayer(RenderableManager &rm, int layer)
{
auto gridInstance = rm.getInstance(_gridEntity);
rm.setLayerMask(gridInstance, 0xFF, 1u << (uint8_t)layer);
// auto sphereInstance = rm.getInstance(_sphereEntity);
// rm.setLayerMask(sphereInstance, 0xFF, 1u << (uint8_t)layer);
}
SceneAsset *GridOverlay::getInstanceByEntity(utils::Entity entity)
{
for (auto &instance : _instances)

View File

@@ -42,7 +42,6 @@ namespace thermion
using namespace filament;
using namespace filament::math;
using namespace utils;
using namespace std::chrono;
using std::string;

View File

@@ -1,12 +1,13 @@
#include <filament/Camera.h>
#include <filament/ColorGrading.h>
#include <filament/Engine.h>
#include <filament/Frustum.h>
#include <filament/ToneMapper.h>
#include <filament/View.h>
#include <filament/Viewport.h>
#include <filament/Engine.h>
#include <filament/ToneMapper.h>
#include <filament/ColorGrading.h>
#include <filament/Camera.h>
#include <utils/Entity.h>
#include "c_api/ThermionDartApi.h"
#include "c_api/TCamera.h"
#include "Log.hpp"
#include "MathUtils.hpp"
@@ -32,8 +33,8 @@ namespace thermion
EMSCRIPTEN_KEEPALIVE void Camera_getFrustum(TCamera *tCamera, double *out) {
auto *camera = reinterpret_cast<Camera *>(tCamera);
auto &frustum = camera->getFrustum();
auto planes = frustum.getPlanes();
auto frustum = camera->getFrustum();
auto planes = frustum.getNormalizedPlanes();
for(int i = 0; i < 6; i++) {
for(int j = 0; j < 4; j++) {
out[(i*4) + j] = planes[i][j];
@@ -41,18 +42,30 @@ namespace thermion
}
}
EMSCRIPTEN_KEEPALIVE void Camera_setLensProjection(TCamera *tCamera, double near, double far, double aspect, double focalLength) {
auto *camera = reinterpret_cast<Camera *>(tCamera);
camera->setLensProjection(near, far, aspect, focalLength);
}
EMSCRIPTEN_KEEPALIVE void Camera_setModelMatrix(TCamera *tCamera, double4x4 tModelMatrix) {
auto *camera = reinterpret_cast<Camera *>(tCamera);
auto modelMatrix = convert_double4x4_to_mat4(tModelMatrix);
camera->setModelMatrix(modelMatrix);
}
EMSCRIPTEN_KEEPALIVE void Camera_setCustomProjectionWithCulling(TCamera *tCamera, double4x4 projectionMatrix, double near, double far)
{
auto *camera = reinterpret_cast<Camera *>(tCamera);
camera->setCustomProjection(convert_double4x4_to_mat4(projectionMatrix), near, far);
}
EMSCRIPTEN_KEEPALIVE double Camera_getFocusDistance(TCamera *camera) {
EMSCRIPTEN_KEEPALIVE double Camera_getFocusDistance(TCamera *tCamera) {
auto *camera = reinterpret_cast<Camera *>(tCamera);
return camera->getFocusDistance();
}
EMSCRIPTEN_KEEPALIVE void Camera_setFocusDistance(TCamera *camera, float distance) {
EMSCRIPTEN_KEEPALIVE void Camera_setFocusDistance(TCamera *tCamera, float distance) {
auto *camera = reinterpret_cast<Camera *>(tCamera);
return camera->setFocusDistance(distance);
}
@@ -93,16 +106,16 @@ namespace thermion
return camera->getCullingFar();
}
EMSCRIPTEN_KEEPALIVE void Camera_setProjection(TCamera *const tCamera, Projection projection, double left, double right,
EMSCRIPTEN_KEEPALIVE void Camera_setProjection(TCamera *const tCamera, TProjection projection, double left, double right,
double bottom, double top,
double near, double far)
{
auto *camera = reinterpret_cast<Camera *>(tCamera);
filament::Camera::Projection filamentProjection;
switch(projection) {
case Projection::Orthographic:
case TProjection::Orthographic:
filamentProjection = filament::Camera::Projection::ORTHO;
case Projection::Perspective:
case TProjection::Perspective:
filamentProjection = filament::Camera::Projection::PERSPECTIVE;
}
camera->setProjection(filamentProjection, left, right, bottom, top, near, far);

View File

@@ -47,15 +47,21 @@ namespace thermion
EMSCRIPTEN_KEEPALIVE TEngine *Engine_create(
TBackend backend,
void* platform,
void* sharedContext,
void* tPlatform,
void* tSharedContext,
uint8_t stereoscopicEyeCount,
bool disableHandleUseAfterFreeCheck)
{
filament::Engine::Config config;
config.stereoscopicEyeCount = stereoscopicEyeCount;
config.disableHandleUseAfterFreeCheck = disableHandleUseAfterFreeCheck;
auto *engine = filament::Engine::create(static_cast<filament::Engine::Backend>(backend), platform, sharedContext, &config);
auto *platform = reinterpret_cast<filament::backend::Platform *>(tPlatform);
auto *engine = filament::Engine::create(
static_cast<filament::Engine::Backend>(backend),
platform,
tSharedContext,
&config
);
return reinterpret_cast<TEngine *>(engine);
}

View File

@@ -46,12 +46,13 @@ EMSCRIPTEN_KEEPALIVE int LightManager_createLight(TEngine *tEngine, TLightManage
}
filament::LightManager::Builder builder(lightType);
auto entity = utils::EntityManager::create();
auto result = builder.build(*engine, utils::Entity::import(entity));
auto &em = utils::EntityManager::get();
auto entity = em.create();
auto result = builder.build(*engine, entity);
if(result != filament::LightManager::Builder::Result::Success) {
Log("Failed to create light");
}
return entity;
return utils::Entity::smuggle(entity);
}
EMSCRIPTEN_KEEPALIVE void LightManager_destroyLight(TLightManager *tLightManager, EntityId entity) {

View File

@@ -2,12 +2,16 @@
#include <filament/MaterialInstance.h>
#include <filament/Material.h>
#include <math/mat4.h>
#include <math/vec4.h>
#include <math/vec2.h>
#include "Log.hpp"
#include "materials/image.h"
#include "material/image.h"
#include "material/grid.h"
#include "c_api/TMaterialInstance.h"
#ifdef __cplusplus
@@ -26,7 +30,7 @@ namespace thermion
EMSCRIPTEN_KEEPALIVE TMaterial *Material_createImageMaterial(TEngine *tEngine) {
auto *engine = reinterpret_cast<filament::Engine *>(tEngine);
auto *material = Material::Builder()
auto *material = filament::Material::Builder()
.package(IMAGE_IMAGE_DATA, IMAGE_IMAGE_SIZE)
.build(*engine);
@@ -35,10 +39,9 @@ namespace thermion
EMSCRIPTEN_KEEPALIVE TMaterial *Material_createGridMaterial(TEngine *tEngine) {
auto *engine = reinterpret_cast<filament::Engine *>(tEngine);
auto *material = Material::Builder()
.package(GRID_GRID_DATA, GRID_GRID_SIZE)
.build(*engine);
auto *material = filament::Material::Builder()
.package(GRID_GRID_DATA, GRID_GRID_SIZE)
.build(*engine);
return reinterpret_cast<TMaterial *>(material);
}

View File

@@ -121,7 +121,7 @@ namespace thermion
if (!renderableInstance.isValid()) {
return Aabb3 { };
}
auto box = renderableManager.getAxisAlignedBoundingBox(renderableInstance);
auto box = renderableManager->getAxisAlignedBoundingBox(renderableInstance);
return Aabb3{box.center.x, box.center.y, box.center.z, box.halfExtent.x, box.halfExtent.y, box.halfExtent.z};
}

View File

@@ -1,7 +1,6 @@
#include <sstream>
#include <vector>
#include "c_api/TTexture.h"
#include <filament/Engine.h>
#include <filament/Material.h>
#include <filament/RenderTarget.h>
@@ -15,6 +14,8 @@
#include <filament/imageio/ImageDecoder.h>
#include <filament/backend/DriverEnums.h>
#include "c_api/TTexture.h"
#include "Log.hpp"
#ifdef __cplusplus
@@ -227,7 +228,7 @@ namespace thermion
auto *engine = reinterpret_cast<::filament::Engine *>(tEngine);
auto format = convertToFilamentFormat(tFormat);
auto samplerType = static_cast<::filament::Texture::Sampler>(static_cast<int>(tSamplerType));
auto usage = static_cast<::filament::Texture::Usage>(tUsage);
auto usage = static_cast<TextureUsage>(tUsage);
if ((usage & TextureUsage::UPLOADABLE) == TextureUsage::UPLOADABLE) {
TRACE("UPLOADABLE");

View File

@@ -1,4 +1,4 @@
#include "RenderLoop.hpp"
#include "rendering/RenderLoop.hpp"
#include <functional>
#include <stdlib.h>

File diff suppressed because it is too large Load Diff

View File

@@ -1,201 +0,0 @@
import Foundation
import GLKit
@objc public class ThermionTextureSwift : NSObject {
public var pixelBuffer: CVPixelBuffer?
var pixelBufferAttrs = [
kCVPixelBufferPixelFormatTypeKey: NSNumber(value: kCVPixelFormatType_32ABGR ),
kCVPixelBufferIOSurfacePropertiesKey: [:] as CFDictionary
] as [CFString : Any] as CFDictionary
@objc public var cvMetalTextureCache:CVMetalTextureCache?
@objc public var metalDevice:MTLDevice?
@objc public var cvMetalTexture:CVMetalTexture?
@objc public var metalTexture:MTLTexture?
@objc public var metalTextureAddress:Int = -1
@objc override public init() {
}
@objc public init(width:Int64, height:Int64, isDepth:Bool) {
if(self.metalDevice == nil) {
self.metalDevice = MTLCreateSystemDefaultDevice()!
}
if isDepth {
// Create a proper depth texture without IOSurface backing
let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(
pixelFormat: .depth32Float,
width: Int(width),
height: Int(height),
mipmapped: false)
textureDescriptor.usage = [.renderTarget, .shaderRead]
textureDescriptor.storageMode = .private // Best performance for GPU-only access
metalTexture = metalDevice?.makeTexture(descriptor: textureDescriptor)
let metalTexturePtr = Unmanaged.passRetained(metalTexture!).toOpaque()
metalTextureAddress = Int(bitPattern: metalTexturePtr)
return
}
let pixelFormat: MTLPixelFormat = isDepth ? .depth32Float : .bgra8Unorm
let cvPixelFormat = isDepth ? kCVPixelFormatType_DepthFloat32 : kCVPixelFormatType_32BGRA
if(CVPixelBufferCreate(kCFAllocatorDefault, Int(width), Int(height),
kCVPixelFormatType_32BGRA, pixelBufferAttrs, &pixelBuffer) != kCVReturnSuccess) {
print("Error allocating pixel buffer")
metalTextureAddress = -1;
return
}
if self.cvMetalTextureCache == nil {
let cacheCreationResult = CVMetalTextureCacheCreate(
kCFAllocatorDefault,
nil,
self.metalDevice!,
nil,
&self.cvMetalTextureCache)
if(cacheCreationResult != kCVReturnSuccess) {
print("Error creating Metal texture cache")
metalTextureAddress = -1
return
}
}
let cvret = CVMetalTextureCacheCreateTextureFromImage(
kCFAllocatorDefault,
self.cvMetalTextureCache!,
pixelBuffer!, nil,
MTLPixelFormat.bgra8Unorm,
Int(width), Int(height),
0,
&cvMetalTexture)
if(cvret != kCVReturnSuccess) {
print("Error creating texture from image")
metalTextureAddress = -1
return
}
metalTexture = CVMetalTextureGetTexture(cvMetalTexture!)
let metalTexturePtr = Unmanaged.passRetained(metalTexture!).toOpaque()
metalTextureAddress = Int(bitPattern:metalTexturePtr)
}
@objc public func destroyTexture() {
CVMetalTextureCacheFlush(self.cvMetalTextureCache!, 0)
self.metalTexture = nil
self.cvMetalTexture = nil
self.pixelBuffer = nil
self.metalDevice = nil
self.cvMetalTextureCache = nil
}
@objc public func fillColor() {
CVPixelBufferLockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))
let bufferWidth = Int(CVPixelBufferGetWidth(pixelBuffer!))
let bufferHeight = Int(CVPixelBufferGetHeight(pixelBuffer!))
let bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer!)
guard let baseAddress = CVPixelBufferGetBaseAddress(pixelBuffer!) else {
return
}
for row in 0..<bufferHeight {
var pixel = baseAddress + row * bytesPerRow
for _ in 0..<bufferWidth {
let blue = pixel
blue.storeBytes(of: 255, as: UInt8.self)
let red = pixel + 1
red.storeBytes(of: 0, as: UInt8.self)
let green = pixel + 2
green.storeBytes(of: 0, as: UInt8.self)
let alpha = pixel + 3
alpha.storeBytes(of: 255, as: UInt8.self)
pixel += 4;
}
}
CVPixelBufferUnlockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))
}
@objc public func getTextureBytes() -> NSData? {
guard let texture = self.metalTexture else {
print("Metal texture is not available")
return nil
}
let width = texture.width
let height = texture.height
// Check what type of texture we're dealing with
let isDepthTexture = texture.pixelFormat == .depth32Float ||
texture.pixelFormat == .depth16Unorm
print("Using texture pixel format : \(texture.pixelFormat) isDepthTexture \(isDepthTexture) (depth32Float \(MTLPixelFormat.depth32Float)) (depth16Unorm \(MTLPixelFormat.depth16Unorm))")
// Determine bytes per pixel based on format
let bytesPerPixel = isDepthTexture ?
(texture.pixelFormat == .depth32Float ? 4 : 2) : 4
let bytesPerRow = width * bytesPerPixel
let byteCount = bytesPerRow * height
// Create a staging buffer that is CPU-accessible
guard let stagingBuffer = self.metalDevice?.makeBuffer(
length: byteCount,
options: .storageModeShared) else {
print("Failed to create staging buffer")
return nil
}
// Create command buffer and encoder for copying
guard let cmdQueue = self.metalDevice?.makeCommandQueue(),
let cmdBuffer = cmdQueue.makeCommandBuffer(),
let blitEncoder = cmdBuffer.makeBlitCommandEncoder() else {
print("Failed to create command objects")
return nil
}
// Copy from texture to buffer
blitEncoder.copy(
from: texture,
sourceSlice: 0,
sourceLevel: 0,
sourceOrigin: MTLOrigin(x: 0, y: 0, z: 0),
sourceSize: MTLSize(width: width, height: height, depth: 1),
to: stagingBuffer,
destinationOffset: 0,
destinationBytesPerRow: bytesPerRow,
destinationBytesPerImage: byteCount
)
blitEncoder.endEncoding()
cmdBuffer.commit()
cmdBuffer.waitUntilCompleted()
// Now the data is in the staging buffer, accessible to CPU
if isDepthTexture {
// For depth textures, just return the raw data
return NSData(bytes: stagingBuffer.contents(), length: byteCount)
} else {
// For color textures, do the BGRA to RGBA swizzling
let bytes = stagingBuffer.contents().bindMemory(to: UInt8.self, capacity: byteCount)
let data = NSMutableData(bytes: bytes, length: byteCount)
let mutableBytes = data.mutableBytes.bindMemory(to: UInt8.self, capacity: byteCount)
for i in stride(from: 0, to: byteCount, by: 4) {
let blue = mutableBytes[i]
let red = mutableBytes[i+2]
mutableBytes[i] = red
mutableBytes[i+2] = blue
}
return data
}
}
}