Swift Package with Metal-native volume rendering, SwiftUI overlays, and a thin DICOM dataset bridge. Metal is the only official clinical renderer. Interactive clinical frames are MTLTexture outputs presented through MTKView or CAMetalLayer; CGImage is allowed only for explicit export, snapshot, debug, and test readback use cases behind SnapshotExporting/TextureSnapshotExporter.
| 3D Volume Rendering | MPR View |
MTKCore— Domain types (VolumeDataset, orientation/spacing models), Metal helpers (MetalRaycaster,VolumeTextureFactory,ShaderLibraryLoader), serializable clinical transfer function models (TransferFunction,ClinicalTransferFunctionPreset,AdvancedToneCurveModel,VolumeTransferFunctionLibrary), and runtime availability guards. MTKCore does not parse DICOM.MTKUI— SwiftUI-friendly public viewport contracts (StackViewport,VolumeViewport,VolumeViewport3D,ClinicalViewportSession) plus containers, overlays, and compatibility controllers. MTKUI is the UI layer over MTKCore Metal volume/MPR adapters.MetalViewportSurfaceis the official clinicalMTKViewpresentation surface.MTKDicomBridge— Optional bridge product that depends on the remoteDICOM-Decoderpackage and convertsDicomCore.DicomDecodedSeriesintoVolumeDataset.MTKFixtures— Optional synthetic datasets generated by code. The public package does not ship.raw.zipfixture presets.
The public app-facing API contract is documented in Architecture/PublicAPI.md. The accepted clinical rendering architecture is documented in Architecture/ClinicalRenderingADR.md. Multi-volume registration and resampling follow the staged plan in Architecture/MultiVolumeRegistration.md.
DICOM / VolumeDataset
|
v
VolumeResourceManager
|
v
GPU volume texture / transfer texture / auxiliary textures
|
v
MTKRenderingEngine
|
v
ViewportRenderGraph
|
v
VolumeRaycastPass / MPRReslicePass / MIPPass / OverlayPass
|
v
PresentationPass
|
v
MTKView / CAMetalLayer drawable
Metal-native rendering is the only official clinical backend. The target interactive presentation surface is MTKView/CAMetalLayer, with MTLTexture as the frame result handed to the presentation pass. Viewports are expected to share GPU resources through handles owned by a resource manager, so synchronized volume, MPR, projection, and overlay views consume the same volume textures, transfer textures, and auxiliary textures instead of duplicating them per surface.
Bounding geometry can be a valid internal implementation detail for ray entry and ray exit inside a specialized pass. It does not change the public clinical rendering architecture.
MetalViewportSurface is the official MTKUI surface for drawable-backed clinical presentation. It presents completed 2D MTLTexture frames through PresentationPass into an MTKView drawable without CGImage conversion. The primary flow is compute/render pass -> persistent outputTexture -> PresentationPass -> drawable -> present; compute directly into the drawable is not the main path because drawables are ephemeral presentation targets, acquisition can be display-paced, and presented drawables cannot be reused for scheduling, inspection, export, or overlay composition.
For application code, prefer the stable public wrappers: VolumeDataset/ImageData3D, StackViewport, VolumeViewport, VolumeViewport3D, ClinicalViewportSession, clinical transfer functions, VolumeLayer, SurfaceMeshLayer, and MetalViewportSurface/MetalViewportView. MTKRenderingEngine, ViewportRenderGraph, VolumeResourceManager, RenderPassNode, and output texture pools are implementation details behind those wrappers, not the recommended product API.
MTKCore exposes a minimal SurfaceMesh contract with vertices, normals, indices, coordinate space, bounds, and segment metadata. MarchingCubesExtractor provides deterministic CPU extraction from LabelmapVolume labels or scalar VolumeDataset thresholds. Extracted labelmap meshes carry the same label/segment id that MPR labelmap overlays use. Extracted meshes default to .worldMillimeters coordinates through the source volume affine; .textureNormalized is available for callers that need texture-space geometry.
MTKDicomBridge can convert parsed DICOM SEG objects into VolumeLayer labelmaps aligned to a base VolumeDataset, preserving segment labels for MPR and 3D overlay review.
The v1 render path is labelmap 3D -> SurfaceMesh -> SurfaceMeshLayer -> volume3D viewport. MetalSurfaceMeshRenderer draws indexed triangles into the existing Metal output texture after volume raycasting. Opaque surfaces write mesh-local depth first, semi-transparent surfaces render afterward with stable layer-level back-to-front ordering, and volume crop/clip settings are applied to the surface fragments. True raycast-volume depth occlusion, smoothing, decimation, topology repair, advanced materials, and GPU extraction remain out of scope for this version.
MTKCore and MTKUI support v1 scalar volume fusion for registered layer stacks in a 3D viewport. VolumeLayer can carry scalar volume content with its own VolumeDataset, VolumeTransferFunction, opacity, visibility, and blend mode. VolumeLayerBlendMode.sourceOver is the default alpha-over mode, and .additive is available for PET-like heat or dose overlays.
The existing single-volume VolumeRenderRequest(dataset:transferFunction:...), VolumeViewport3D.applyDataset, and ClinicalViewportSession setup paths remain source-compatible. Multi-layer rendering keeps the one-layer fast path; additional visible scalar layers are raycast separately and composited through a Metal pass, so cost scales with the number of visible scalar layers. VolumeResourceManager shares layer resources by handle across viewports and reports layer memory in GPU resource metrics.
V1 does not implement registration. Scalar layers must already be registered and resampled into the base volume texture space; non-identity scalar transforms are rejected with a clear error. Labelmap/MPR affine overlay support remains unchanged. The v2 plan for explicit LayerTransform, affine consumption, CPU/GPU resampling, PET/CT, MR multimodal, dose-map, and prior/current workflows is documented in Architecture/MultiVolumeRegistration.md. Until that plan is implemented, alignment and resampling are external preprocessing requirements.
- Swift 5.10, Xcode 16
- iOS 17+ / macOS 14+
- Metal-capable device required for rendering and GPU test coverage. Metal is the runtime contract for rendering; no alternate rendering runtime is provided. GPU-dependent tests require Metal and skip when unavailable.
- Metal Performance Shaders behavior is feature-specific and should be treated as an explicit capability/result contract:
- Volume rendering (
MetalVolumeRenderingAdapter,MetalRaycaster): Pure Metal ray marching is the required rendering path on Metal-capable devices and has no MPS dependency. - Empty-space acceleration (
MPSEmptySpaceAccelerator): Optional MPS accelerator for supported devices. Shared helpers return.success,.unavailable(reason:), or.failed(error)instead ofnil. - Histogram calculation (
VolumeHistogramCalculator): Pure Metal compute. No MPS dependency. - Statistics calculation (
VolumeStatisticsCalculator): Metal compute with explicit GPU setup and execution errors. CPU reference implementations exist only in tests.
- Volume rendering (
MTK is a rendering and UI toolkit for research, education, and prototype applications involving volumetric medical-image data on Apple platforms. It is not a medical device, has not been validated for clinical decision-making, and should not be the sole basis for diagnosis, treatment, or patient triage.
If you load real DICOM studies, keep PHI handling, local security, and institutional review requirements in mind. The repository demonstrates rendering infrastructure and loading patterns; it does not claim regulatory clearance, dataset-wide clinical validation, or diagnostic performance.
Point Xcode or SwiftPM at the MTK Git repository and depend on the library products you need:
.package(url: "https://github.com/ThalesMMS/MTK.git", from: "1.0.8"),
.target(
name: "YourApp",
dependencies: [
.product(name: "MTKCore", package: "MTK"),
.product(name: "MTKUI", package: "MTK")
]
)Add the optional bridge only when the app wants MTK's default Swift DICOM parser:
.target(
name: "YourApp",
dependencies: [
.product(name: "MTKCore", package: "MTK"),
.product(name: "MTKDicomBridge", package: "MTK")
]
)SceneKit examples may be extracted to a separate experimental package in the future, but they are not part of the main package contract.
- Replace
MTKSceneKitvolume presentation (VolumeCubeMaterial) withMTKCorerendering throughMetalVolumeRenderingAdapterand present the resulting frames withMTKUIcontainers such asVolumeViewportContainerandMetalViewportSurface. - Replace
MTKSceneKitMPR presentation (MPRPlaneMaterial) withMetalMPRAdapterplusMTKUIlayouts such asMPRGridComposerorTriplanarMPRComposer. - Replace
VolumeCameraControllerandCameraPose-driven interaction withStackViewport,VolumeViewport,VolumeViewport3D, orClinicalViewportSession. - Replace node/plane helper usage (
SCNNode+Volumetric) with geometry and display helpers that stay in the Metal-native path, such asMPRPlaneGeometryFactoryandMPRDisplayTransformFactory. - If you still need a standalone 3D wrapper for non-clinical experiments, keep that code outside the main package. There is no maintained
SceneKitExamplespackage in this repository today.
Recommended vs Legacy:
- Recommended:
MetalVolumeRenderingAdapter+VolumeViewportContainer/ Legacy: deprecatedVolumeCubeMaterial - Recommended:
MetalMPRAdapter+MPRGridComposerorTriplanarMPRComposer/ Legacy: deprecatedMPRPlaneMaterial - Recommended:
StackViewport,VolumeViewport,VolumeViewport3D, orClinicalViewportSession/ Legacy: deprecatedVolumeCameraController,CameraPose,SCNNode+Volumetric
Compatibility note:
- The current MTK package requires iOS 17+ and macOS 14+. Downstream apps that still need older platform support or a custom 3D wrapper should keep that compatibility layer outside this package.
- Example code for the supported migration path lives in
Examples/BasicVolumeRendering.swift,Examples/MPRViewer.swift,Examples/TriplanarMPRViewer.swift, andExamples/DicomLoader.swift.
ShaderLibraryLoaderrequiresMTK.metallibto be bundled inBundle.module. Missing or invalid artifacts are reported as structuredShaderLibraryLoader.LoaderErrorcases, such asmetallibNotBundledormetallibLoadFailed(underlying:).- Precompiled Metal libraries are versioned under
Sources/MTKCore/Resourcesso downstream Xcode projects build without shader-generation trust prompts. - Manual shader rebuild is only needed after changing
.metalsources:MTK_METAL_SDK=all METALLIB_STRICT=1 bash Tooling/Shaders/build_metallib.sh Sources/MTKCore/Resources/Shaders Sources/MTKCore/Resources/MTK.metallib - Troubleshooting: if shader loading fails, verify that
MTK.metallib,MTK-iphonesimulator.metallib, andMTK-iphoneos.metallibare present in theMTKCoreresource bundle. - Public
.raw.zippresets are not bundled. UseClinicalSyntheticFixturesinMTKFixturesfor sample volumes;VolumeTextureFactory(preset:)is deprecated and kept only for compatibility. - Preset loading now reports
noDataAvailable; it does not silently return a stub volume. - Use
VolumeTextureFactory.debugPlaceholderDataset()only for tests or explicit debug tooling that needs a minimal 1x1x1 volume.
Minimal SwiftUI viewer that applies a volume through the public 3D viewport contract:
import MTKCore
import MTKUI
import SwiftUI
struct VolumePreview: View {
@StateObject private var viewport = try! VolumeViewport3D()
var body: some View {
MetalViewportView(surface: viewport.surface)
.task {
let voxelCount = 256 * 256 * 128
let voxels = Data(repeating: 0, count: voxelCount * VolumePixelFormat.int16Signed.bytesPerVoxel)
let dataset = VolumeDataset(
data: voxels,
dimensions: VolumeDimensions(width: 256, height: 256, depth: 128),
spacing: VolumeSpacing(x: 1.0, y: 1.0, z: 1.5),
pixelFormat: .int16Signed,
intensityRange: (-1024)...3071
)
await viewport.applyDataset(dataset)
await viewport.setWindowLevel(window: 1700, level: 350)
try? await viewport.applyClinicalTransferFunctionPreset(.ctSoftTissue)
}
}
}Use StackViewport for volume-backed slice scrolling, VolumeViewport for MPR/projection, and VolumeViewport3D for true 3D volume rendering. Use ClinicalViewportSession with ClinicalViewportGrid(session:) for the reference axial/coronal/sagittal plus 3D clinical layout.
TransferFunction is the public JSON contract for clinical presets and user edits. Version 1 keeps existing .tf keys (name, min, max, colourPoints, alphaPoints, shift, colorSpace) and adds optional metadata, renderingIntent, and gradientOpacity fields. RGB color is represented by colourPoints; opacity is represented by the piecewise alphaPoints function. colourValue.a remains round-tripped for compatibility but does not replace alphaPoints.
let preset = try ClinicalTransferFunctionPreset.ctVRBone.loadTransferFunction()
let data = try JSONEncoder().encode(preset)
let restored = try JSONDecoder().decode(TransferFunction.self, from: data)
let session = try await ClinicalViewportSession.make(dataset: dataset)
try await session.applyTransferFunction(restored)When gradientOpacity is present, MTK builds a 2D transfer texture and multiplies scalar opacity by gradient magnitude during DVR. Presets without gradientOpacity keep the legacy 1D transfer-texture path.
DICOM-Decoder owns DICOM source loading, ZIP extraction, slice ordering, geometry validation, rescale slope/intercept, recommended window metadata, and DICOM errors. Use MTKCore alone when your app already has a VolumeDataset. Import MTKDicomBridge when you want to convert the default DICOM-Decoder result into a renderer-ready dataset:
import MTKCore
import MTKDicomBridge
let importer = DicomVolumeDatasetImporter()
importer.loadDataset(from: sourceURL, progress: { _ in }) { result in
let dataset = try? result.get().dataset
_ = dataset
}Progress and failures are surfaced from DicomCore; the bridge does not remap DICOM parser errors into MTKCore-specific error cases.
Typical inputs
- A synthetic or programmatically generated voxel buffer wrapped in
VolumeDataset - A DICOM directory, ZIP archive, or individual file routed through
DICOM-Decoderand converted byMTKDicomBridge - 16-bit scalar volume data with spatial metadata available for reconstruction
Typical outputs
- An in-memory
VolumeDatasetready for rendering DicomVolumeDatasetImportResultmetadata such assourceURLandseriesDescription- Interactive
MTLTextureframe outputs for drawable-backed presentation - SwiftUI rendering surfaces, MPR views, overlays, and transfer-function-driven visualization backed by MTKCore Metal adapters
MTK does not produce segmentation masks, classification labels, radiology reports, or treatment recommendations by itself. In other words, the package is a visualization/loading substrate, not a diagnostic model.
BackendResolverandMetalRuntimeAvailabilityenforce the Metal rendering requirement before controllers are created.ensureAvailability()throws explicit availability errors, andstatus()exposes structured diagnostics plus optional MPS capability flags.MetalRuntimeGuardexposes structured requirement status, missing required capabilities, and optional MPS feature availability for diagnostics.CommandBufferProfilerandVolumeRenderingDebugOptionshelp surface GPU runtime behavior during development.
func makeVolumeViewport() throws -> VolumeViewport3D {
try MetalRuntimeAvailability.ensureAvailability()
let status = MetalRuntimeAvailability.status()
print("MPS available: \(status.supportsMetalPerformanceShaders)")
return try VolumeViewport3D()
}
do {
let viewport = try makeVolumeViewport()
print(viewport.state.presentation)
} catch {
let status = MetalRuntimeAvailability.status()
print("Metal requirement failed: \(status.missingFeatures)")
print("MPS available: \(status.supportsMetalPerformanceShaders)")
}swift testrequires a Metal-capable host for GPU-dependent suites; those tests require Metal and skip when unavailable.- The standalone
VolumeRendererComparisonbenchmark requires an explicit local DICOM path through--dicom. If you want to use the sample data from the demo project, clone or download fixtures fromhttps://github.com/ThalesMMS/MTK-Demo.gitand pass the local path explicitly. - DICOM source security coverage lives in
DICOM-Decoder; visual-quality checks compare MPS-accelerated empty-space skipping (feature availability requires MPS) against core Metal ray marching on synthetic datasets.
- The package targets Apple-platform rendering workflows; it is not a cross-platform PACS, archive, or viewer.
- Public examples and tests mostly exercise synthetic datasets, renderer behaviors, and optional local fixtures rather than a versioned benchmark corpus committed in this repository.
- Rendering correctness checks and visual-regression tests are useful engineering signals, but they are not the same thing as clinical validation or reader-study evidence.
- DICOM import support depends on
DICOM-Decodermetadata coverage and input quality. Unsupported transfer syntaxes, malformed datasets, geometry failures, unsupported scalar formats, empty sources, and unsafe ZIP entries surface asDicomCoreerrors.
DocC documentation covers the two modules (MTKCore, MTKUI) with API reference, conceptual guides, and a Getting Started tutorial. The clinical rendering decision lives in Architecture/ClinicalRenderingADR.md. Runnable examples are in the Examples/ directory.
Generate documentation locally:
bash Tooling/build_docs.shThis creates .doccarchive files in the docs/ directory that can be opened in Xcode or hosted as static HTML.
Apache 2.0. See LICENSE.