#include "metal_backend.h" #include #include #include #include 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 setBackendSize(mln::Size size_) { size = size_; swapchain->setDrawableSize( {static_cast(size.width), static_cast(size.height)}); buffersInvalid = true; } mln::Size getSize() const { return size; } void bind() override { surface = NS::TransferPtr(swapchain->nextDrawable()); auto texSize = mln::Size{static_cast(swapchain->drawableSize().width), static_cast(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(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(stencilTexture.get()) ->setUsage(MTL::TextureUsageShaderRead | MTL::TextureUsageShaderWrite | MTL::TextureUsageRenderTarget); } if (depthTexture) { depthTexture->create(); if (auto* depthTarget = renderPassDescriptor->depthAttachment()) { depthTarget->setTexture( static_cast(depthTexture.get())->getMetalTexture()); } } if (stencilTexture) { stencilTexture->create(); if (auto* stencilTarget = renderPassDescriptor->stencilAttachment()) { stencilTarget->setTexture( static_cast(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; mln::Size size; 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(*this)) { window.contentView.layer = (__bridge CALayer*)getDefaultRenderable() .getResource() .getSwapchain() .get(); window.contentView.wantsLayer = YES; } mln::gfx::Renderable& MetalBackend::getDefaultRenderable() { return *this; } void MetalBackend::activate() {} void MetalBackend::deactivate() {} void MetalBackend::updateAssumedState() {} void MetalBackend::setSize(mln::Size size_) { size = size_; getResource().setBackendSize(size_); } mln::Size MetalBackend::getSize() const { return getResource().getSize(); }