You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Textured PBR materials render black on Intel iMac with AMD Radeon Pro 570X (macOS 15) #509
On an Intel iMac with an AMD Radeon Pro 570X, every mesh whose material has a
base color texture renders solid black. Untextured materials render correctly
(lighting, shadows and the environment all look right). The same app works
correctly on an Intel MacBook Pro (Intel UHD 630). Metal validation reports
no errors.
The same .glb files render correctly on the same iMac when loaded in Unity
6.5 (URP + glTFast, macOS standalone build), so the GPU and driver can sample
these textures.
Flutter 3.50.0-0.2.pre (channel master, engine e737c8145d), Impeller Metal
flutter_scene 0.20.0, also reproduced with 0.24.3
Working machine: MacBook Pro 16" 2019 (Intel UHD 630 + AMD 5300M; Flutter
uses the integrated GPU), same build
What we tried on the iMac (profile builds)
Variant
Result
Original glb materials
black
Base color + normal textures removed (metallicRoughness texture kept)
renders correctly
Base color replaced with a plain JPEG via Texture2D.fromAsset, no normal map, with and without mipmaps
black
Base color texture created manually, StorageMode.devicePrivate, RGBA8
black
Base color texture created manually, hostVisible, RGBA16F
black
Sampler without anisotropic filtering (GpuTextureSource defaults)
black
Shadows disabled
black
Different environment (HDR)
black
MTL_DEBUG_LAYER=1 and MTL_SHADER_VALIDATION=1
black, no errors reported
flutter_scene 0.24.3 instead of 0.20.0
black
The only log output is the usual Impeller startup line ("Using the Impeller
rendering backend"); no warnings or errors.
So the problem seems specific to the base color texture path in the PBR
shader on this AMD GPU. The metallicRoughness texture, sampled by the same
shader, does not cause it.
Screenshots
I can share the .glb files and run test builds on the iMac if that helps.
Thanks for the thorough matrix, it rules out a lot. One thing it can't rule out is that every texture sample comes back as zero on this GPU, not just base color. A metallicRoughness texture that reads as zero gives a smooth dielectric, which still looks plausible, so that run may hide the same failure.
Could you share the .glb files, and try two things on the iMac?
An UnlitMaterial with the same base color texture on a plain PlaneGeometry. If that's black too, the problem is texture sampling in general rather than the PBR shader.
The surface debug views. Add this to the app's pubspec, rebuild, and set scene.debug.view = const DebugView(channel: SurfaceDebugChannel.uv0Checker);, then the same with SurfaceDebugChannel.baseColor. A screenshot of each tells us whether the UVs reach the fragment shader intact.
Thanks! Your hunch was right that every texture sample is zero, but the cause seems to be upstream of flutter_scene: on this iMac, reading back any ui.Image returns all zeros, even in plain Flutter.
I built a small test app with flutter_scene 0.24.3 and debug_views: true, and ran it on the iMac (Radeon Pro 570X, Metal 2, macOS 15.7.9) and on a MacBook Pro 2019 (Intel UHD 630, where Flutter runs, macOS 26.7.1):
Scene
MacBook
iMac
UnlitMaterial + PlaneGeometry, texture from JPG asset (Texture2D.fromAsset)
OK
invisible (transparent)
UnlitMaterial + PlaneGeometry, texture from in-memory pixels (Texture2D.fromPixels)
OK
OK
Board .glb, normal
OK
black
Board .glb, DebugView uv0
OK
OK, identical
Board .glb, DebugView uv0Checker
OK
OK, identical
Board .glb, DebugView baseColor
OK
black
So the UVs reach the fragment shader intact, and a texture uploaded from raw pixels samples fine. Only textures decoded from images fail.
Plain Flutter readback test (no flutter_scene), checking non-zero bytes from Image.toByteData (rawRgba and rawStraightRgba):
PictureRecorder red rect → Picture.toImage: MacBook OK / iMac all zeros
decodeImageFromPixels (solid red): MacBook OK / iMac all zeros
Since Texture2D.fromImage reads the image back with toByteData before uploading, every decoded texture ends up zero on this GPU. My guess is a missing synchronize on managed storage for discrete GPUs on Intel Macs, but I haven't verified it. I'll report the readback to flutter/flutter as well. Would wrapping the image's GPU texture (the gpu.Texture.fromImage path) be a viable workaround here?
Screenshots attached. Flutter 3.50.0-0.2.pre (master, 3594a63245); flutter doctor -v from the build machine below (Flutter isn't installed on the iMac).
Great digging, and your guess is right. On a Mac without unified memory, Impeller allocates the readback buffer with MTLStorageModeManaged, and the texture-to-buffer blit in BlitPassMTL::OnCopyTextureToBufferCommand never calls synchronizeResource: on it, so the CPU reads whatever was there before the copy. Apple Silicon and Intel integrated GPUs use shared storage, which is why only the Radeon shows it. I'll put up the engine fix; link your flutter/flutter issue here when you file it and I'll reference it.
Until then, Texture2D.fromImage(image, opaque: true, sampling: const TextureSampling(mipmaps: false)) wraps the image's GPU texture instead of reading it back, which works for opaque textures that can do without mipmaps. Decoding with package:image and passing the pixels to Texture2D.fromPixels avoids the readback for everything else, at the cost of a slower decode. Neither helps textures loaded from a .glb, which always read back.
Activity
bdero commentedon Oct 26, 2026
Thanks for the thorough matrix, it rules out a lot. One thing it can't rule out is that every texture sample comes back as zero on this GPU, not just base color. A metallicRoughness texture that reads as zero gives a smooth dielectric, which still looks plausible, so that run may hide the same failure.
Could you share the .glb files, and try two things on the iMac?
UnlitMaterialwith the same base color texture on a plainPlaneGeometry. If that's black too, the problem is texture sampling in general rather than the PBR shader.scene.debug.view = const DebugView(channel: SurfaceDebugChannel.uv0Checker);, then the same withSurfaceDebugChannel.baseColor. A screenshot of each tells us whether the UVs reach the fragment shader intact.flutter doctor -voutput from the iMac would help too.OAConsulenze commentedon Oct 26, 2026
Thanks! Your hunch was right that every texture sample is zero, but the cause seems to be upstream of flutter_scene: on this iMac, reading back any ui.Image returns all zeros, even in plain Flutter.
I built a small test app with flutter_scene 0.24.3 and debug_views: true, and ran it on the iMac (Radeon Pro 570X, Metal 2, macOS 15.7.9) and on a MacBook Pro 2019 (Intel UHD 630, where Flutter runs, macOS 26.7.1):
So the UVs reach the fragment shader intact, and a texture uploaded from raw pixels samples fine. Only textures decoded from images fail.
Plain Flutter readback test (no flutter_scene), checking non-zero bytes from Image.toByteData (rawRgba and rawStraightRgba):
Since Texture2D.fromImage reads the image back with toByteData before uploading, every decoded texture ends up zero on this GPU. My guess is a missing synchronize on managed storage for discrete GPUs on Intel Macs, but I haven't verified it. I'll report the readback to flutter/flutter as well. Would wrapping the image's GPU texture (the gpu.Texture.fromImage path) be a viable workaround here?
Screenshots attached. Flutter 3.50.0-0.2.pre (master, 3594a63245); flutter doctor -v from the build machine below (Flutter isn't installed on the iMac).
flutter_doctor_macbook.txt












imac_gpu.txt
imac_log.txt
macbook_log.txt
bdero commentedon Oct 27, 2026
Great digging, and your guess is right. On a Mac without unified memory, Impeller allocates the readback buffer with
MTLStorageModeManaged, and the texture-to-buffer blit inBlitPassMTL::OnCopyTextureToBufferCommandnever callssynchronizeResource:on it, so the CPU reads whatever was there before the copy. Apple Silicon and Intel integrated GPUs use shared storage, which is why only the Radeon shows it. I'll put up the engine fix; link your flutter/flutter issue here when you file it and I'll reference it.Until then,
Texture2D.fromImage(image, opaque: true, sampling: const TextureSampling(mipmaps: false))wraps the image's GPU texture instead of reading it back, which works for opaque textures that can do without mipmaps. Decoding withpackage:imageand passing the pixels toTexture2D.fromPixelsavoids the readback for everything else, at the cost of a slower decode. Neither helps textures loaded from a .glb, which always read back.OAConsulenze commentedon Oct 27, 2026
Filed upstream flutter/flutter#195402