Chapter 15: GBufferOut

After reading and rereading the TiledDeferredRenderPass and its related PSOs I could not understand how the GPU can possibly know which buffer/memory/whatever it should supply to the GBufferOut argument in both tiled fragment functions “fragment_tiled_deferredSun” and “fragment_tiled_pointLight”, especially because its the only argument in which there is no [[attribute]] to accompany that argument.

Maybe I am missing something obvious, but I am completely oblivious to how that argument value is provided, and how can the GPU know what to supply to those fragment functions as the value to that parameter.

How does the gpu know which part of memory should be passed into that function as the GBufferOut argument?

(Edit: spelling)

Hi @goazevedo and welcome to the forums :smiling_face:!

It is complicated. I would suggest that you capture the frame and examine all the attachments on the GPU for the various passes.

The structure GBufferOut is defined in the shader file Deferred.metal. It is a list of three textures: albedo, normal, and position. Each of these has an attribute [[color(x)]] which specifies the color attachment index. Albedo is index 1, normal is index 2, and position is index 3 (enum RenderTargetIndices, defined in Common.h). The textures for these color attachments are loaded into the descriptor at the top of TiledDeferredRenderPass.draw(commandBuffer:scene:uniforms:params:). When you create the render command encoder, it uses the descriptor, which points at these color attachments. Color attachment 0 is the view’s drawable texture by default.

Previously, you were simply returning a color from the fragment function, which is written to the texture in color attachment 0. This is the view’s drawable texture. But now, you’re setting up multiple color attachment textures, and you need a way of returning multiple textures from the one fragment function. That’s why you set up a structure containing the multiple textures.

fragment_gBuffer writes to this structure, which is the return from the fragment function. Notice that you don’t write anything to color attachment 0, because the GBuffer pass is an interim pass which doesn’t draw anything to the screen.

Using the albedo, normal and position textures, you can then calculate the lighting. Both fragment_tiled_pointLight and fragment_tiled_deferredSun are passed GBufferOut containing these textures to calculate the relevant lighting. Both these functions then write the final pixel to the texture in color attachment 0, which is the view’s drawable texture.

The difference between an immediate-mode (IM) GPU and a TBDR GPU is that the TBDR GPU retains the GBufferOut textures in tile memory, rather than writing the textures back to system memory.

The non-TBDR GBuffer pass creates the textures, which are transferred to system memory, then the separate lighting pass has to transfer the system memory textures back to the GPU using renderEncoder.setFragmentTexture(texture, index: x).

The TBDR GBuffer pass creates the textures, which are retained in GPU tile memory, then, in the same pass, calls the GPU lighting functions which read the GPU tile memory.

I hope this helps.

Hi Caroline! Thank you so much for your response.

In the previous chapters we always supplied the fragment functions with textures annotated with a texture buffer index directly on the parameter definition, and I was trying to find out how the GPU could supply the albedo, normal and position buffers, but I completely forgot that we had those defined with a [[color(index)]] attribute, so your response made that clear.

So if I got it correctly, even though we define a custom structure, the GPU is able to fill the properties of that structure with the colorAttachments we defined in the descriptor.

Thanks again for the response.

Yes.

  • You create the render command encoder using the render pass descriptor.
  • You create as many textures as you need (albedo, position, normal etc).
  • The render pass descriptor points to these textures as colour attachments.

The render command encoder holds a list of commands, such as setFragmentTexture. If the textures are color attachments, you don’t need to set the textures, as the render command encoder knows about them.

In IM mode, you create two render command encoders. In TBDR you create one.

After creating the render command encoder/s, the command buffer submits them to the GPU for processing.

1 Like