Hello,
I have a compute shader that draws in a read_write texture2d. Running this shader on macOS 10.14 gives the expected result, but on macOS 10.13 I notice that reading the texture gives black instead of the texture's color. I checked macOS 10.14 release notes & Metal Feature Set Tables and didn't notice anything that should affect read_write textures.
The Metal function is as follow:
kernel void drawDot(texture2d<half, access::read_write=""> texture [[texture(kComputeImageIndex)]], constant int2& gidOrigin [[buffer(kComputeGidOriginIndex)]], uint2 gid [[thread_position_in_grid]]) { const uint2 pixPos = gid + uint2(gidOrigin); texture.write(texture.read(pixPos), pixPos); // texture.write(half4(1.0, 0.0, 0.0, 1.0), pixPos); }
If I use line 7 instead of line 6, output image is white with a red square around position given by gidOrigin. If I do the texture read instead, output contains a black square, although the input texture is fully white (I checked in Xcode GPU debugger). So something wrong is happening with the texture read() call.
Am I using read_write texture access in a bad way? Adding texture.fence() does not change anything, as I would expect: I'm never trying to read a pixel written by another thread of the grid.
And in Metal Shading Language Specification pdf I didn't see any restriction there could be on using read_write texture.
Thanks!
Lucas
===============================================================
Edit: I just came upon release notes of macOS 10.12 where read_write textures were introduced: Function Texture Read-Writes
Apple is giving the following usage example:
kernel void my_kernel(texture2d<float, access::read_write=""> texA [[ texture(0) ]], ushort2 gid [[ thread_position_in_grid ]]) { float4 color = texA.read(gid); color = processColor(color); texA.write(color, gid); }
Which is the same kind of operation I am doing. So I suppose there is a bug in macOS 10.13…
===============================================================
Edit 2 : just to make sure I didn't miss something obvious, I took "Hello Compute" example provided by Apple and modified it just enough to use read_write texture. I observed the same behavior: all good on 10.14 but black output on 10.13. Going to file a bug report although I don't have much hope…
Below is the full diff to Hello Compute project:
index 3c1d1c8..b0307af 100644 --- a/Renderer/AAPLRenderer.m +++ b/Renderer/AAPLRenderer.m @@ -118,7 +118,7 @@ Implementation of renderer class which performs Metal setup and per frame render textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm; textureDescriptor.width = image.width; textureDescriptor.height = image.height; - textureDescriptor.usage = MTLTextureUsageShaderRead; + textureDescriptor.usage = MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead; // Create an input and output texture with similar descriptors. We'll only // fill in the inputTexture however. And we'll set the output texture's descriptor @@ -192,13 +192,17 @@ Implementation of renderer class which performs Metal setup and per frame render id commandBuffer = [_commandQueue commandBuffer]; commandBuffer.label = @"MyCommand"; + id blitEnc = [commandBuffer blitCommandEncoder]; + MTLOrigin orig = { 0, 0, 0}; + MTLSize size = { _inputTexture.width, _inputTexture.height, _inputTexture.depth }; + [blitEnc copyFromTexture:_inputTexture sourceSlice:0 sourceLevel:0 sourceOrigin:orig sourceSize:size + toTexture:_outputTexture destinationSlice:0 destinationLevel:0 destinationOrigin:orig]; + [blitEnc endEncoding]; + id computeEncoder = [commandBuffer computeCommandEncoder]; [computeEncoder setComputePipelineState:_computePipelineState]; - [computeEncoder setTexture:_inputTexture - atIndex:AAPLTextureIndexInput]; - [computeEncoder setTexture:_outputTexture atIndex:AAPLTextureIndexOutput]; diff --git a/Renderer/AAPLShaderTypes.h b/Renderer/AAPLShaderTypes.h index 7a57f9d..897e6d9 100644 --- a/Renderer/AAPLShaderTypes.h +++ b/Renderer/AAPLShaderTypes.h @@ -22,7 +22,6 @@ typedef enum AAPLVertexInputIndex // Metal API texture set calls typedef enum AAPLTextureIndex { - AAPLTextureIndexInput = 0, AAPLTextureIndexOutput = 1, } AAPLTextureIndex; diff --git a/Renderer/AAPLShaders.metal b/Renderer/AAPLShaders.metal index 47a5e29..8d0e2de 100644 --- a/Renderer/AAPLShaders.metal +++ b/Renderer/AAPLShaders.metal @@ -90,8 +90,7 @@ constant half3 kRec709Luma = half3(0.2126, 0.7152, 0.0722); // Grayscale compute kernel kernel void -grayscaleKernel(texture2d<half, access::read=""> inTexture [[texture(AAPLTextureIndexInput)]], - texture2d<half, access::write=""> outTexture [[texture(AAPLTextureIndexOutput)]], +grayscaleKernel(texture2d<half, access::read_write=""> outTexture [[texture(AAPLTextureIndexOutput)]], uint2 gid [[thread_position_in_grid]]) { // Check if the pixel is within the bounds of the output texture @@ -101,7 +100,7 @@ grayscaleKernel(texture2d<half, access::read=""> inTexture [[texture(AAPLTextureI return; } - half4 inColor = inTexture.read(gid); + half4 inColor = outTexture.read(gid); half gray = dot(inColor.rgb, kRec709Luma); outTexture.write(half4(gray, gray, gray, 1.0), gid); }
Through bug report I got the following feedback:
- read-write access is not necessarily available: it depends on readWriteTextureSupport property of MTLDevice => https://developer.apple.com/documentation/metal/mtldevice/2887289-readwritetexturesupport and it cannot be deduced just by the macOS version, it depends in hardware in the mac (one example given is that for MBP with 2 GPUs, this support can vary depending on the GPU being used)
- even when read-write access is supported, it is not available for any pixel format: more specifically tier 2 supports RGBA but not BGRA. So I was actually getting lucky (and unsafe) that it worked with BGRA on macOS 10.14.