I have a very basic usdz file from this repo
I call loadTextures() after loading the usdz via MDLAsset. Inspecting the MDLTexture object I can tell it is assigning a colorspace of linear rgb instead of srgb although the image file in the usdz is srgb.
This causes the textures to ultimately render as over saturated.
In the code I later convert the MDLTexture to MTLTexture via MTKTextureLoader but if I set the srgb option it seems to ignore it.
This significantly impacts the usefulness of Model I/O if it can't load a simple usdz texture correctly. Am I missing something?
Thanks!
Thanks for the follow-ups, and for confirming it persists on the macOS 27 beta.
I reproduced this on shipping macOS 26.5 using your lowpoly-house.usdz, so it is not specific to the 27 beta. Loading the base-color texture through MDLAsset.loadTextures() and then MTKTextureLoader.newTexture(texture:options:) produces a texture with a linear pixel format (rgba8Unorm). The shader then receives the sRGB-encoded bytes without the sRGB-to-linear decode, which is the oversaturation you are seeing. I confirmed it by reading a texel back through the GPU: a stored byte of 152 arrives as 0.596 (raw/255) instead of the decoded 0.314.
MTKTextureLoader.Option.SRGB has no effect on this path. Passing true, false, or omitting it all leave the result linear. The same option is honored when you load the image from a URL or data, where .SRGB = true produces an rgba8Unorm_srgb texture. So the option is respected for URL and data sources and ignored when the source is an MDLTexture.
Your usdz is authored correctly: the PNG is tagged sRGB, and the UsdUVTexture does not set sourceColorSpace, which defaults to auto and resolves to sRGB for this image. That matches why Reality Composer Pro, the Xcode viewer, and Preview all render it correctly, so this is not a content issue on your side.
Two workarounds, both of which I verified produce the correct decoded color on 26.5:
- Reinterpret the loaded texture with an sRGB pixel format. Load it with pixel-format-view usage, then make an sRGB view:
let usage: MTLTextureUsage = [.shaderRead, .pixelFormatView]
let texture = try loader.newTexture(
texture: mdlTexture,
options: [.textureUsage: NSNumber(value: usage.rawValue)])
let srgbTexture = texture.makeTextureView(pixelFormat: .rgba8Unorm_srgb)
Use srgbTexture for rendering. Reading a texel back through this view returns the correct sRGB-to-linear value.
- Bypass
MDLTextureand load the image bytes directly, where theSRGBoption is honored:
let texture = try loader.newTexture(URL: imageURL, options: [.SRGB: true])
The workaround operates on the loaded MTLTexture, so it applies regardless of whether the source image is PNG or JPEG.
Since the SRGB option is ignored specifically on the MDLTexture path, that looks like a defect rather than intended behavior, and it is worth a Feedback report. For the report, attach this usdz and note that the resulting MTLTexture is rgba8Unorm regardless of the SRGB option, while the URL path honors it. That gives the team a precise, self-contained repro.
Please let me know if there are any questions.