swapchain.cpp

cross platform rendering playground

src/backend/metal/swapchain.cpp

4.4 KB
#include <math.h>
#ifndef INFINITY
#define INFINITY __builtin_huge_valf()
#endif
#include <Foundation/Foundation.hpp>
#include <Metal/Metal.hpp>
#include <QuartzCore/QuartzCore.hpp>

#include <GLFW/glfw3.h>

#include "backend/metal/mt_api.h"
#include "backend/metal/mt_internal.h"
#include "core/logger.h"

namespace mt {
void mt_refresh_swapchain_wrapper(MTL::Texture *tex, const rhi::Swapchain &swapchain);
} // namespace mt

namespace rhi {
using namespace mt;

static MTL::PixelFormat to_mtl_pixel_format(rhi::ImageFormat format) {
    switch (format) {
    case rhi::ImageFormat::RGBA8_UNORM:
        return MTL::PixelFormatRGBA8Unorm;
    case rhi::ImageFormat::RGBA8_SRGB:
        return MTL::PixelFormatRGBA8Unorm_sRGB;
    case rhi::ImageFormat::BGRA8_UNORM:
        return MTL::PixelFormatBGRA8Unorm;
    default:
        return MTL::PixelFormatBGRA8Unorm;
    }
}

void create_swapchain(Device &device, const rhi::SwapchainDesc &desc, Swapchain &swapchain) {
    CA::MetalLayer *layer = mt_attach_metal_layer(desc.native_window, device.handle);
    layer->setPixelFormat(to_mtl_pixel_format(desc.format));

    GLFWwindow *window = static_cast<GLFWwindow *>(desc.native_window);
    int fb_width = 0;
    int fb_height = 0;
    glfwGetFramebufferSize(window, &fb_width, &fb_height);

    u32 width = desc.width > 0 ? desc.width : (u32)fb_width;
    u32 height = desc.height > 0 ? desc.height : (u32)fb_height;
    layer->setDrawableSize(CGSizeMake(width, height));
    layer->setMaximumDrawableCount(desc.image_count);
    layer->setDisplaySyncEnabled(desc.vsync_enabled);

    swapchain.layer = layer;
    swapchain.current = nullptr;
    swapchain.format = (desc.format != rhi::ImageFormat::UNDEFINED) ? desc.format : rhi::ImageFormat::BGRA8_UNORM;
    swapchain.extent.width = width;
    swapchain.extent.height = height;
    swapchain.maximumDrawableCount = desc.image_count;

    VEL_INFO("Created Metal swapchain ({}x{})", width, height);
}

void destroy_swapchain(Device &device, Swapchain &swapchain) {
    (void)device;
    mt_clear_current_drawable();
    if (swapchain.layer) {
        swapchain.layer->release();
        swapchain.layer = nullptr;
    }
    swapchain.current = nullptr;
}

bool acquire_swapchain_image(Device &device, Swapchain &swapchain, u32 &out_image_index, CmdBuffer &cmd) {
    // no acquire step.
    (void)cmd;

    CA::MetalDrawable *drawable = swapchain.layer->nextDrawable();
    if (!drawable) {
        return false;
    }

    mt::reside(device, drawable->texture());
    // drawable lives in layer set. use both sets.
    if (cmd.handle && device.heap && device.heap->residency) {
        MTL::ResidencySet *layer_set = swapchain.layer->residencySet();
        if (layer_set) {
            const MTL::ResidencySet *sets[2] = {device.heap->residency, layer_set};
            cmd.handle->useResidencySets(sets, 2);
        }
    }
    swapchain.current = drawable;
    mt_set_current_drawable(drawable);
    out_image_index = 0;

    mt::mt_refresh_swapchain_wrapper(drawable->texture(), swapchain);
    return true;
}

void present_swapchain(Device &device, Swapchain &swapchain, u32 image_index, SyncPoint frame_done) {
    (void)device;
    (void)swapchain;
    (void)image_index;
    (void)frame_done;
    // present runs at submit.
}

ImageFormat swapchain_format(const Swapchain &swapchain) {
    return swapchain.format;
}

Extent2D swapchain_extent(const Swapchain &swapchain) {
    return swapchain.extent;
}

bool swapchain_needs_recreate(const Swapchain &swapchain) {
    return swapchain.recreate;
}

static Image s_swap_images[SWAPCHAIN_IMAGE_COUNT];

} // namespace rhi

namespace mt {

void mt_refresh_swapchain_wrapper(MTL::Texture *tex, const rhi::Swapchain &swapchain) {
    rhi::Image &img = rhi::s_swap_images[0];
    for (auto &entry : img.view_cache) {
        if (entry.second) {
            entry.second->release();
        }
    }
    img.view_cache.clear();
    img.desc.width = swapchain.extent.width;
    img.desc.height = swapchain.extent.height;
    img.desc.format = swapchain.format;
    img.desc.usage = rhi::ImageUsage::ColorAttachment;
    img.handle = tex;
    img.state = rhi::ResourceState::ColorDraw;
}

} // namespace mt

namespace rhi {

Image *swapchain_image(Swapchain &swapchain, u32 index) {
    (void)swapchain;
    if (index >= SWAPCHAIN_IMAGE_COUNT) {
        return nullptr;
    }
    if (!s_swap_images[index].handle) {
        return nullptr;
    }
    return &s_swap_images[index];
}

} // namespace rhi