2
votes

I've written a Compute shader that outputs to a Texture. The coordinate system of the output texture is in pixels. I then have a basic vertex and fragment shader that should simply sample the value and respond with what I thought would be in normalised coordinates. However, I thought this mapping between my programmatically drawn texture and the vertices of my rendering surface would match up.

The Compute Function

Can be summarized as

texture.write(color, uint2(x, y));

where x and y are integer pixel locations.

The Vertex Data

// position.x, position.y, texCoords.x, texCoords.y
let vertexData = [Float](arrayLiteral:
    -1,  1, 0, 0,
    -1, -1, 0, 1,
     1, -1, 1, 1,
     1, -1, 1, 1,
     1,  1, 1, 0,
    -1,  1, 0, 0)

The Metal Shader

typedef struct {
    packed_float2 position;
    packed_float2 texCoords;
} VertexIn;

typedef struct {
    float4 position [[ position ]];
    float2 texCoords;
} FragmentVertex;

vertex FragmentVertex simple_vertex(device VertexIn *vertexArray [[ buffer(0) ]],
                                      uint vertexIndex [[ vertex_id ]])
{
    VertexIn in = vertexArray[vertexIndex];

    FragmentVertex out;
    out.position = float4(in.position, 0.f, 1.f);
    out.texCoords = in.texCoords;

    return out;
}

fragment float4 simple_fragment(FragmentVertex in [[ stage_in ]],
                               texture2d<uint, access::sample> inputTexture [[ texture(0) ]],
                               sampler linearSampler [[ sampler(0) ]])
{
    const uint2 imageSizeInPixels = uint2(360, 230);
    float imageSizeInPixelsWidth = imageSizeInPixels.x;
    float imageSizeInPixelsHeight = imageSizeInPixels.y;
    float2 coords = float2(in.position.x / 360.f, in.position.y / 230.f);
    float color = inputTexture.sample(linearSampler, in.texCoords).x / 255.f;

    return float4(float3(color), 1.f);
}

The Sampler

let samplerDescriptor = MTLSamplerDescriptor()
            samplerDescriptor.normalizedCoordinates = true
            samplerDescriptor.minFilter = .linear
            samplerDescriptor.magFilter = .linear
            samplerDescriptor.sAddressMode = .clampToZero
            samplerDescriptor.rAddressMode = .clampToZero
            self.samplerState = self.metalDevice?.makeSamplerState(descriptor: samplerDescriptor)

In this experiment the only value that seems to work is coords, based upon the normalized in.position value. in.texCoords seems to always be zero. Shouldn't the texcoords and position received by the vertex and fragment shader be values be in the range of values defined in the vertex data?

1
How are you determining that in.texCoords is always zero? Incidentally, how are you defining your sampler state?warrenm
Experimentally. I've tried outputting values from from the interpolated position. While outputting the values of in.position and in.texcoords from the fragment shader as colors I realised the position value needed to be normalized to get a nice gradient field of color. Switching from position to texcoords yields a solid field of color using normalized or raw values. Which indicates to me that it is always picking the same value, or zero since the output color is black.Cameron Lowell Palmer
@warrenm Fixed it. Thank you for looking at this.Cameron Lowell Palmer

1 Answers

1
votes

My Vertex Buffer was right, but wrong

In the process of converting Obj-C code to Swift I failed to copy the vertex completely.

The Correct Copy

let byteCount = vertexData.count * MemoryLayout<Float>.size
let vertexBuffer = self.metalDevice?.makeBuffer(bytes: vertexData, length: byteCount, options: options)

The Source of my Woes

let vertexBuffer = self.metalDevice?.makeBuffer(bytes: vertexData, length: vertexData.count, options: options)

The Complete Vertex Buffer Creation

// Vertex data for a full-screen quad. The first two numbers in each row represent
// the x, y position of the point in normalized coordinates. The second two numbers
// represent the texture coordinates for the corresponding position.
let vertexData = [Float](arrayLiteral:
        -1,  1, 0, 0,
        -1, -1, 0, 1,
         1, -1, 1, 1,
         1, -1, 1, 1,
         1,  1, 1, 0,
        -1,  1, 0, 0)

// Create a buffer to hold the static vertex data

let options = MTLResourceOptions().union(.storageModeShared)
let byteCount = vertexData.count * MemoryLayout<Float>.size
let vertexBuffer = self.metalDevice?.makeBuffer(bytes: vertexData, length: byteCount, options: options)
vertexBuffer?.label = "Image Quad Vertices"
self.vertexBuffer = vertexBuffer