src/ngfx/porting/metal/MTLGraphics.mm

Classes

Name
struct MTLGraphicsUtil

Source code

/*
* Copyright 2020 GoPro Inc.
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

#include "ngfx/porting/metal/MTLGraphics.h"
#include "ngfx/porting/metal/MTLGraphicsContext.h"
#include "ngfx/porting/metal/MTLFramebuffer.h"
#include "ngfx/porting/metal/MTLRenderPass.h"
#include "ngfx/porting/metal/MTLTexture.h"
#include "ngfx/porting/metal/MTLCommandBuffer.h"
#include "ngfx/porting/metal/MTLComputePipeline.h"
#include "ngfx/porting/metal/MTLGraphicsPipeline.h"
#include "ngfx/porting/metal/MTLComputeCommandEncoder.h"
#include "ngfx/porting/metal/MTLRenderCommandEncoder.h"
#include "ngfx/graphics/Config.h"
#include "ngfx/core/DebugUtil.h"
using namespace ngfx;

void MTLGraphics::create() {}

void MTLGraphics::beginComputePass(CommandBuffer* commandBuffer) {
    auto mtlCommandBuffer = mtl(commandBuffer);
    currentComputeCommandEncoder.v = [mtlCommandBuffer->v computeCommandEncoder];
    currentCommandEncoder = &currentComputeCommandEncoder;
}
void MTLGraphics::endComputePass(CommandBuffer* commandBuffer) {
    [currentComputeCommandEncoder.v endEncoding];
    currentComputeCommandEncoder.v = nullptr;
}

struct MTLGraphicsUtil {
    static void setViewport(MTLGraphics* g, Rect2D& r) {
        [g->currentRenderCommandEncoder.v setViewport:(MTLViewport){ double(r.x), double(r.y), double(r.w), double(r.h), 0.0, 1.0 }];
    }

    static void setScissor(MTLGraphics* g, Rect2D &r) {
        #ifdef ORIGIN_BOTTOM_LEFT
            Rect2D &vp = g->viewport;
            MTLScissorRect mtlScissorRect = { NSUInteger(r.x), vp.h - r.y - r.h ,  r.w, r.h };
        #else
            MTLScissorRect mtlScissorRect = { NSUInteger(r.x), NSUInteger(r.y), r.w, r.h };
        #endif
            [g->currentRenderCommandEncoder.v setScissorRect:mtlScissorRect];
    }
};

void MTLGraphics::beginRenderPass(CommandBuffer* commandBuffer, RenderPass* renderPass, Framebuffer* framebuffer,
          glm::vec4 clearColor, float clearDepth, uint32_t clearStencil) {
    autoReleasePool = [[NSAutoreleasePool alloc] init];
    MTLRenderPassDescriptor* mtlRenderPassDescriptor = mtl(renderPass)->getDescriptor(mtl(ctx), mtl(framebuffer), clearColor, clearDepth, clearStencil);
    currentRenderCommandEncoder.v = [mtl(commandBuffer)->v renderCommandEncoderWithDescriptor:mtlRenderPassDescriptor];
    currentCommandEncoder = &currentRenderCommandEncoder;
}

void MTLGraphics::endRenderPass(CommandBuffer* commandBuffer) {
    [currentRenderCommandEncoder.v endEncoding];
    currentRenderCommandEncoder.v = nullptr;
    [autoReleasePool release];
}

void MTLGraphics::bindVertexBuffer(CommandBuffer* cmdBuffer, Buffer* buffer, uint32_t location, uint32_t stride) {
    auto renderEncoder = (MTLRenderCommandEncoder*)currentCommandEncoder;
    [renderEncoder->v setVertexBuffer:mtl(buffer)->v offset:0 atIndex:location];
}
void MTLGraphics::bindIndexBuffer(CommandBuffer* cmdBuffer, Buffer* buffer, IndexFormat indexFormat) {
    currentIndexBuffer = mtl(buffer);
    currentIndexFormat = indexFormat;
}
void MTLGraphics::bindUniformBuffer(CommandBuffer* cmdBuffer, Buffer* buffer, uint32_t binding, ShaderStageFlags shaderStageFlags) {
    if (MTLGraphicsPipeline* graphicsPipeline = dynamic_cast<MTLGraphicsPipeline*>(currentPipeline)) {
        auto renderEncoder = (MTLRenderCommandEncoder*)currentCommandEncoder;
        if (shaderStageFlags & SHADER_STAGE_VERTEX_BIT) {
            [renderEncoder->v setVertexBuffer:mtl(buffer)->v offset:0 atIndex:binding];
        }
        if (shaderStageFlags & SHADER_STAGE_FRAGMENT_BIT) {
            [renderEncoder->v setFragmentBuffer:mtl(buffer)->v offset:0 atIndex:binding];
        }
    }
    else if (MTLComputePipeline* computePipeline = dynamic_cast<MTLComputePipeline*>(currentPipeline)) {
        auto computeEncoder = (MTLComputeCommandEncoder*)currentCommandEncoder;
        [computeEncoder->v setBuffer:mtl(buffer)->v offset:0 atIndex:binding];
    }
}
void MTLGraphics::bindStorageBuffer(CommandBuffer* cmdBuffer, Buffer* buffer, uint32_t binding, ShaderStageFlags shaderStageFlags) {
    if (MTLGraphicsPipeline* graphicsPipeline = dynamic_cast<MTLGraphicsPipeline*>(currentPipeline)) {
        auto renderEncoder = (MTLRenderCommandEncoder*)currentCommandEncoder;
        if (shaderStageFlags & SHADER_STAGE_VERTEX_BIT) [renderEncoder->v setVertexBuffer:mtl(buffer)->v offset:0 atIndex:binding];
        if (shaderStageFlags & SHADER_STAGE_FRAGMENT_BIT) [renderEncoder->v setFragmentBuffer:mtl(buffer)->v offset:0 atIndex:binding];
    }
    else if (MTLComputePipeline* computePipeline = dynamic_cast<MTLComputePipeline*>(currentPipeline)) {
        auto computeEncoder = (MTLComputeCommandEncoder*)currentCommandEncoder;
        [computeEncoder->v setBuffer:mtl(buffer)->v offset:0 atIndex:binding];
    }
}
void MTLGraphics::bindComputePipeline(CommandBuffer* cmdBuffer, ComputePipeline* computePipeline) {
    auto computeEncoder = (MTLComputeCommandEncoder*)currentCommandEncoder;
    [computeEncoder->v setComputePipelineState: mtl(computePipeline)->mtlPipelineState];
    currentPipeline = computePipeline;
}
void MTLGraphics::bindGraphicsPipeline(CommandBuffer* cmdBuffer, GraphicsPipeline* graphicsPipeline) {
    auto renderEncoder = (MTLRenderCommandEncoder*)currentCommandEncoder;
    auto mtlPipeline = mtl(graphicsPipeline);
    [renderEncoder->v setRenderPipelineState: mtlPipeline->mtlPipelineState];
    [renderEncoder->v setCullMode: mtlPipeline->mtlCullMode];
    [renderEncoder->v setFrontFacingWinding: mtlPipeline->mtlFrontFaceWinding];
    if (mtlPipeline->mtlDepthStencilState) {
        [renderEncoder->v setDepthStencilState: mtlPipeline->mtlDepthStencilState];
    }
    currentPrimitiveType = mtl(graphicsPipeline)->mtlPrimitiveType;
    currentPipeline = graphicsPipeline;
}
void MTLGraphics::bindTexture(CommandBuffer* cmdBuffer, Texture* texture, uint32_t set) {
    if (MTLRenderCommandEncoder* renderEncoder = dynamic_cast<MTLRenderCommandEncoder*>(currentCommandEncoder)) {
        [renderEncoder->v setFragmentTexture: mtl(texture)->v atIndex: set];
        [renderEncoder->v setFragmentSamplerState: mtl(texture)->mtlSamplerState atIndex: set];
    }
    else if (MTLComputeCommandEncoder* computeEncoder = dynamic_cast<MTLComputeCommandEncoder*>(currentCommandEncoder)) {
        [computeEncoder->v setTexture: mtl(texture)->v atIndex: set];
        [computeEncoder->v setSamplerState: mtl(texture)->mtlSamplerState atIndex: set];
    }
}

void MTLGraphics::dispatch(CommandBuffer* cmdBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ,
       uint32_t threadsPerGroupX, uint32_t threadsPerGroupY, uint32_t threadsPerGroupZ) {
    auto computeEncoder = (MTLComputeCommandEncoder*)currentCommandEncoder;
    [computeEncoder->v dispatchThreadgroups:MTLSizeMake(groupCountX, groupCountY, groupCountZ)
                      threadsPerThreadgroup:MTLSizeMake(threadsPerGroupX, threadsPerGroupY, threadsPerGroupZ)];
}

void MTLGraphics::draw(CommandBuffer* cmdBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) {
    auto renderEncoder = (MTLRenderCommandEncoder*)currentCommandEncoder;
    [renderEncoder->v drawPrimitives: currentPrimitiveType vertexStart:firstVertex vertexCount:vertexCount
        instanceCount: instanceCount baseInstance: firstInstance];
}
void MTLGraphics::drawIndexed(CommandBuffer* cmdBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance) {
    auto renderEncoder = (MTLRenderCommandEncoder*)currentCommandEncoder;
    [renderEncoder->v drawIndexedPrimitives:currentPrimitiveType indexCount:indexCount
        indexType: (currentIndexFormat == INDEXFORMAT_UINT16 ? ::MTLIndexTypeUInt16 : ::MTLIndexTypeUInt32)
        indexBuffer:currentIndexBuffer->v indexBufferOffset:0
        instanceCount: instanceCount baseVertex: 0 baseInstance: firstInstance];
}
void MTLGraphics::setViewport(CommandBuffer* cmdBuffer, Rect2D r) {
    auto renderEncoder = (MTLRenderCommandEncoder*)currentCommandEncoder;
    if (renderEncoder) MTLGraphicsUtil::setViewport(this, r);
    viewport = r;
}
void MTLGraphics::setScissor(CommandBuffer* cmdBuffer, Rect2D r) {
    auto renderEncoder = (MTLRenderCommandEncoder*)currentCommandEncoder;
    if (renderEncoder) MTLGraphicsUtil::setScissor(this, r);
    scissorRect = r;
}

void MTLGraphics::waitIdle(CommandBuffer* cmdBuffer) {
    mtl(cmdBuffer)->waitUntilCompleted();
}

Graphics* Graphics::create(GraphicsContext* ctx) {
    MTLGraphics* mtlGraphics = new MTLGraphics();
    mtlGraphics->ctx = ctx;
    mtlGraphics->create();
    return mtlGraphics;
}

Updated on 3 April 2021 at 20:21:51 PDT