// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) // SPDX-FileCopyrightText: 2025 Jorrit Rouwe // SPDX-License-Identifier: MIT #include #include #include #include #include #include #include #include #include #include #include #include #include RendererMTL::~RendererMTL() { [mCommandQueue release]; [mShadowRenderPass release]; [mShaderLibrary release]; } void RendererMTL::Initialize(ApplicationWindow *inWindow) { Renderer::Initialize(inWindow); mView = static_cast(inWindow)->GetMetalView(); id device = GetDevice(); // Load the shader library containing all shaders for the test framework NSError *error = nullptr; NSURL *url = [NSURL URLWithString: [NSString stringWithCString: (AssetStream::sGetAssetsBasePath() + "Shaders/MTL/Shaders.metallib").c_str() encoding: NSUTF8StringEncoding]]; mShaderLibrary = [device newLibraryWithURL: url error: &error]; FatalErrorIfFailed(error); // Create depth only texture (no color buffer, as seen from light) mShadowMap = new TextureMTL(this, cShadowMapSize, cShadowMapSize); // Create render pass descriptor for shadow pass mShadowRenderPass = [[MTLRenderPassDescriptor alloc] init]; mShadowRenderPass.depthAttachment.texture = mShadowMap->GetTexture(); mShadowRenderPass.depthAttachment.loadAction = MTLLoadActionClear; mShadowRenderPass.depthAttachment.storeAction = MTLStoreActionStore; mShadowRenderPass.depthAttachment.clearDepth = 0.0f; // Create the command queue mCommandQueue = [device newCommandQueue]; } void RendererMTL::BeginFrame(const CameraState &inCamera, float inWorldScale) { JPH_PROFILE_FUNCTION(); Renderer::BeginFrame(inCamera, inWorldScale); // Update frame index mFrameIndex = (mFrameIndex + 1) % cFrameCount; // Create a new command buffer mCommandBuffer = [mCommandQueue commandBuffer]; // Create shadow render encoder mRenderEncoder = [mCommandBuffer renderCommandEncoderWithDescriptor: mShadowRenderPass]; // Set viewport to size of shadow map [mRenderEncoder setViewport: (MTLViewport){ 0.0, 0.0, double(cShadowMapSize), double(cShadowMapSize), 0.0, 1.0 }]; // Set pixel shader constants [mRenderEncoder setFragmentBytes: &mPSBuffer length: sizeof(mPSBuffer) atIndex: 0]; // Counter clockwise is default winding order [mRenderEncoder setFrontFacingWinding: MTLWindingCounterClockwise]; // Start with projection mode SetProjectionMode(); } void RendererMTL::EndShadowPass() { // Finish the shadow encoder [mRenderEncoder endEncoding]; mRenderEncoder = nil; // Get the descriptor for the main window MTLRenderPassDescriptor *render_pass_descriptor = mView.currentRenderPassDescriptor; if (render_pass_descriptor == nullptr) return; // Create render encoder mRenderEncoder = [mCommandBuffer renderCommandEncoderWithDescriptor: render_pass_descriptor]; // Set viewport [mRenderEncoder setViewport: (MTLViewport){ 0.0, 0.0, double(mWindow->GetWindowWidth()), double(mWindow->GetWindowHeight()), 0.0, 1.0 }]; // Set pixel shader constants [mRenderEncoder setFragmentBytes: &mPSBuffer length: sizeof(mPSBuffer) atIndex: 0]; // Counter clockwise is default winding order [mRenderEncoder setFrontFacingWinding: MTLWindingCounterClockwise]; // Start with projection mode SetProjectionMode(); } void RendererMTL::EndFrame() { JPH_PROFILE_FUNCTION(); // Finish the encoder [mRenderEncoder endEncoding]; mRenderEncoder = nil; // Schedule a present [mCommandBuffer presentDrawable: mView.currentDrawable]; // Commit the command buffer [mCommandBuffer commit]; Renderer::EndFrame(); } void RendererMTL::SetProjectionMode() { JPH_ASSERT(mInFrame); [mRenderEncoder setVertexBytes: &mVSBuffer length: sizeof(mVSBuffer) atIndex: 2]; } void RendererMTL::SetOrthoMode() { JPH_ASSERT(mInFrame); [mRenderEncoder setVertexBytes: &mVSBufferOrtho length: sizeof(mVSBufferOrtho) atIndex: 2]; } Ref RendererMTL::CreateTexture(const Surface *inSurface) { return new TextureMTL(this, inSurface); } Ref RendererMTL::CreateVertexShader(const char *inName) { id function = [mShaderLibrary newFunctionWithName: [NSString stringWithCString: inName encoding: NSUTF8StringEncoding]]; if (function == nil) FatalError("Vertex shader %s not found", inName); return new VertexShaderMTL(function); } Ref RendererMTL::CreatePixelShader(const char *inName) { id function = [mShaderLibrary newFunctionWithName: [NSString stringWithCString: inName encoding: NSUTF8StringEncoding]]; if (function == nil) FatalError("Pixel shader %s not found", inName); return new PixelShaderMTL(function); } unique_ptr RendererMTL::CreatePipelineState(const VertexShader *inVertexShader, const PipelineState::EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShader *inPixelShader, PipelineState::EDrawPass inDrawPass, PipelineState::EFillMode inFillMode, PipelineState::ETopology inTopology, PipelineState::EDepthTest inDepthTest, PipelineState::EBlendMode inBlendMode, PipelineState::ECullMode inCullMode) { return make_unique(this, static_cast(inVertexShader), inInputDescription, inInputDescriptionCount, static_cast(inPixelShader), inDrawPass, inFillMode, inTopology, inDepthTest, inBlendMode, inCullMode); } RenderPrimitive *RendererMTL::CreateRenderPrimitive(PipelineState::ETopology inType) { return new RenderPrimitiveMTL(this, inType == PipelineState::ETopology::Line? MTLPrimitiveTypeLine : MTLPrimitiveTypeTriangle); } RenderInstances *RendererMTL::CreateRenderInstances() { return new RenderInstancesMTL(this); } #ifndef JPH_ENABLE_VULKAN Renderer *Renderer::sCreate() { return new RendererMTL; } #endif