#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