Files
MapLibreParticleLab/platform/glfw/metal_backend.mm
2026-09-01 18:35:29 +03:00

131 lines
4.9 KiB
Plaintext

#include "metal_backend.h"
#include <mln/mtl/mtl_fwd.hpp>
#include <mln/mtl/renderable_resource.hpp>
#include <Metal/Metal.hpp>
#include <QuartzCore/CAMetalLayer.hpp>
namespace mln {
using namespace mtl;
class MetalRenderableResource final : public mtl::RenderableResource {
public:
MetalRenderableResource(MetalBackend& backend)
: rendererBackend(backend),
commandQueue(NS::TransferPtr(backend.getDevice()->newCommandQueue())),
swapchain(NS::TransferPtr(CA::MetalLayer::layer())) {
swapchain->setDevice(backend.getDevice().get());
}
void setDrawableSize(const mln::Size& drawableSize) {
swapchain->setDrawableSize(
{static_cast<CGFloat>(drawableSize.width), static_cast<CGFloat>(drawableSize.height)});
buffersInvalid = true;
}
void bind() override {
surface = NS::TransferPtr(swapchain->nextDrawable());
auto texSize = mln::Size{static_cast<uint32_t>(swapchain->drawableSize().width),
static_cast<uint32_t>(swapchain->drawableSize().height)};
commandBuffer = NS::TransferPtr(commandQueue->commandBuffer());
renderPassDescriptor = NS::TransferPtr(MTL::RenderPassDescriptor::renderPassDescriptor());
renderPassDescriptor->colorAttachments()->object(0)->setTexture(surface->texture());
if (buffersInvalid || !depthTexture || !stencilTexture) {
buffersInvalid = false;
depthTexture = rendererBackend.getContext().createTexture2D();
depthTexture->setSize(texSize);
depthTexture->setFormat(gfx::TexturePixelType::Depth, gfx::TextureChannelDataType::Float);
depthTexture->setSamplerConfiguration({gfx::TextureFilterType::Linear,
gfx::TextureWrapType::Clamp,
gfx::TextureWrapType::Clamp});
static_cast<mtl::Texture2D*>(depthTexture.get())
->setUsage(MTL::TextureUsageShaderRead | MTL::TextureUsageShaderWrite |
MTL::TextureUsageRenderTarget);
stencilTexture = rendererBackend.getContext().createTexture2D();
stencilTexture->setSize(texSize);
stencilTexture->setFormat(gfx::TexturePixelType::Stencil,
gfx::TextureChannelDataType::UnsignedByte);
stencilTexture->setSamplerConfiguration({gfx::TextureFilterType::Linear,
gfx::TextureWrapType::Clamp,
gfx::TextureWrapType::Clamp});
static_cast<mtl::Texture2D*>(stencilTexture.get())
->setUsage(MTL::TextureUsageShaderRead | MTL::TextureUsageShaderWrite |
MTL::TextureUsageRenderTarget);
}
if (depthTexture) {
depthTexture->create();
if (auto* depthTarget = renderPassDescriptor->depthAttachment()) {
depthTarget->setTexture(
static_cast<mtl::Texture2D*>(depthTexture.get())->getMetalTexture());
}
}
if (stencilTexture) {
stencilTexture->create();
if (auto* stencilTarget = renderPassDescriptor->stencilAttachment()) {
stencilTarget->setTexture(
static_cast<mtl::Texture2D*>(stencilTexture.get())->getMetalTexture());
}
}
}
void swap() override {
commandBuffer->presentDrawable(surface.get());
commandBuffer->commit();
commandBuffer.reset();
renderPassDescriptor.reset();
}
const mtl::RendererBackend& getBackend() const override { return rendererBackend; }
const mtl::MTLCommandBufferPtr& getCommandBuffer() const override { return commandBuffer; }
mtl::MTLBlitPassDescriptorPtr getUploadPassDescriptor() const override {
return NS::TransferPtr(MTL::BlitPassDescriptor::alloc()->init());
}
const mtl::MTLRenderPassDescriptorPtr& getRenderPassDescriptor() const override {
return renderPassDescriptor;
}
const CAMetalLayerPtr& getSwapchain() const { return swapchain; }
private:
MetalBackend& rendererBackend;
MTLCommandQueuePtr commandQueue;
MTLCommandBufferPtr commandBuffer;
MTLRenderPassDescriptorPtr renderPassDescriptor;
CAMetalDrawablePtr surface;
CAMetalLayerPtr swapchain;
gfx::Texture2DPtr depthTexture;
gfx::Texture2DPtr stencilTexture;
bool buffersInvalid = true;
};
} // namespace mln
MetalBackend::MetalBackend(NSWindow* window)
: mln::mtl::RendererBackend(mln::gfx::ContextMode::Unique),
mln::gfx::Renderable(mln::Size{0, 0}, std::make_unique<mln::MetalRenderableResource>(*this)) {
window.contentView.layer = (__bridge CALayer*)getDefaultRenderable()
.getResource<mln::MetalRenderableResource>()
.getSwapchain()
.get();
window.contentView.wantsLayer = YES;
}
mln::gfx::Renderable& MetalBackend::getDefaultRenderable() { return *this; }
void MetalBackend::activate() {}
void MetalBackend::deactivate() {}
void MetalBackend::setSize(mln::Size size_) {
setRenderableSize(size_);
getResource<mln::MetalRenderableResource>().setDrawableSize(size_);
}