5

I need to clear the depth buffer, for which i use glClear(GL_DEPTH_BUFFER_BIT) in OpenGL, how to do in metal ? I have gone through apple's documentation, there is no hint about it.

VivekParamasivam
  • 1,086
  • 2
  • 13
  • 23

3 Answers3

6

The short answer is that to clear the depth buffer you add these two lines before beginning a render pass: mRenderPassDescriptor.depthAttachment.loadAction = MTLLoadActionClear; mRenderPassDescriptor.depthAttachment.clearDepth = 1.0f; And you cannot do a clear without ending and restarting a render pass.

Long answer:

In Metal, you have to define that you want the colour and depth buffers cleared when you start rendering to a MTLTexture. There is no clear function like in OpenGL.

To do this, in your MTLRenderPassDescriptor, set depthAttachment.loadAction to MTLLoadActionClear and depthAttachment.clearDepth to 1.0f. You may also want to set colorAttachments[0].loadAction to MTLLoadActionClear to clear the colour buffer.

This render pass descriptor is then passed in to your call to MTLCommandBuffer::renderCommandEncoderWithDescriptor.

If you do want to clear a depth or colour buffer midway through rendering you have to call endEncoding on MTLRenderCommandEncoder, and then start encoding again with depthAttachment.loadAction set to MTLLoadActionClear.

Muzza
  • 1,236
  • 9
  • 13
  • Thanks for the clear and precise answer muzza. I've explained with sample codes below. – VivekParamasivam Apr 13 '15 at 06:21
  • it is impossible to understand that without seeing the code. – Duck Mar 29 '17 at 22:27
  • 2
    If you have got to this point, you already have a MTLRenderPassDescriptor, MTLCommandBuffer, renderCommandEncoderWithDescriptor and endEncoding. Setting four parameters on your existing Descriptor is not really 'impossible to understand' – Muzza Mar 29 '17 at 22:36
  • 3
    And to answer your previous comment (now deleted), Apple did not do it this way in order to make things '1400% more difficult for developers'. They did it because on tiled GPUs a clear operation is inherently linked to starting a render pass. Metal is all about keeping the code and hardware tied together and not hiding what is occurring behind the scenes. – Muzza Mar 29 '17 at 22:43
1

To explain the solution more clear with sample codes

Before start rendering:

void prepareRendering(){
    CMDBuffer = [_commandQueue commandBuffer]; // get command Buffer
    drawable = [_metalLayer nextDrawable]; // get drawable from metalLayer
    renderingTexture = drawable.texture; // set that as rendering te
    setupRenderPassDescriptorForTexture(drawable.texture); // set the depth and colour buffer properties
    RenderCMDBuffer = [CMDBuffer renderCommandEncoderWithDescriptor:_renderPassDescriptor];
    RenderCMDBuffer.label = @"MyRenderEncoder";
    setUpDepthState(CompareFunctionLessEqual,true,false); // 
    [RenderCMDBuffer setDepthStencilState:_depthState];
    [RenderCMDBuffer pushDebugGroup:@"DrawCube"];
}

void setupRenderPassDescriptorForTexture(id <MTLTexture> texture)
{
    if (_renderPassDescriptor == nil)
        _renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
        // set color buffer properties
        _renderPassDescriptor.colorAttachments[0].texture = texture;
        _renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
        _renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(1.0f, 1.0f,1.0f, 1.0f);
            _renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
// set depth buffer properties
            MTLTextureDescriptor* desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: MTLPixelFormatDepth32Float width: texture.width height: texture.height mipmapped: NO];
            _depthTex = [device newTextureWithDescriptor: desc];
            _depthTex.label = @"Depth";
            _renderPassDescriptor.depthAttachment.texture = _depthTex;
            _renderPassDescriptor.depthAttachment.loadAction = MTLLoadActionClear;
            _renderPassDescriptor.depthAttachment.clearDepth = 1.0f;
                _renderPassDescriptor.depthAttachment.storeAction = MTLStoreActionDontCare;
   }

Render Your contents here

Render();

After rendering

similar to ogles2 method [_context presentRenderbuffer:_colorRenderBuffer];

void endDisplay()
{
    [RenderCMDBuffer popDebugGroup];
    [RenderCMDBuffer endEncoding];
    [CMDBuffer presentDrawable:drawable];
    [CMDBuffer commit];
    _currentDrawable = nil;
}

The above methods clears the depth and colour buffers after rendering each frame

To clear the depth buffer in midway

    void clearDepthBuffer(){
    // end encoding the render command buffer  
            [RenderCMDBuffer popDebugGroup];
            [RenderCMDBuffer endEncoding];

            // here MTLLoadActionClear will clear your last drawn depth values
            _renderPassDescriptor.depthAttachment.loadAction = MTLLoadActionClear;            _renderPassDescriptor.depthAttachment.clearDepth = 1.0f;
            _renderPassDescriptor.depthAttachment.storeAction = MTLStoreActionDontCare;

            // here MTLLoadActionLoad will reuse your last drawn color buffer
            _renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionLoad;
            _renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;

            RenderCMDBuffer = [CMDBuffer renderCommandEncoderWithDescriptor:_renderPassDescriptor];
            RenderCMDBuffer.label = @"MyRenderEncoder";
            [RenderCMDBuffer pushDebugGroup:@"DrawCube"];
        }
VivekParamasivam
  • 1,086
  • 2
  • 13
  • 23
0

Here is the Swift 5 version. Run your first render pass:

        // RENDER PASS 1

        renderPassDescriptor = view.currentRenderPassDescriptor

        if let renderPassDescriptor = renderPassDescriptor, let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) {

            renderEncoder.label = "First Render Encoder"
            renderEncoder.pushDebugGroup("First Render Debug")

            // render stuff here...

            renderEncoder.popDebugGroup()
            renderEncoder.endEncoding()
        }
        

Then clear the depth buffer, but keep the colour buffer:

        renderPassDescriptor = view.currentRenderPassDescriptor

        // Schedule Metal to clear the depth buffer
        renderPassDescriptor!.depthAttachment.loadAction = MTLLoadAction.clear
        renderPassDescriptor!.depthAttachment.clearDepth = 1.0
        renderPassDescriptor!.depthAttachment.storeAction = MTLStoreAction.dontCare

        // Schedule Metal to reuse the previous colour buffer
        renderPassDescriptor!.colorAttachments[0].loadAction = MTLLoadAction.load
        renderPassDescriptor!.colorAttachments[0].storeAction = MTLStoreAction.store

Then run your second render:

        if let renderPassDescriptor = renderPassDescriptor, let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) {
           
            renderEncoder.label = "Second Render"
            renderEncoder.pushDebugGroup("Second Render Debug")

            // render stuff here...

            renderEncoder.popDebugGroup()
            renderEncoder.endEncoding()
        }