Ajout de Jolt Physics + 1ere version des factory entitecomposants - camera, transform, rigidbody, collider, renderer
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
/// Convert Metal error to readable text and alert
|
||||
void FatalErrorIfFailed(NSError *inResult);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/FatalErrorIfFailedMTL.h>
|
||||
#include <Utils/Log.h>
|
||||
|
||||
void FatalErrorIfFailed(NSError *inResult)
|
||||
{
|
||||
if (inResult != nullptr)
|
||||
FatalError("Metal error returned: %s", [[inResult localizedDescription] cStringUsingEncoding: NSUTF8StringEncoding]);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/PipelineState.h>
|
||||
#include <Renderer/MTL/VertexShaderMTL.h>
|
||||
#include <Renderer/MTL/PixelShaderMTL.h>
|
||||
|
||||
class RendererMTL;
|
||||
|
||||
/// Metal pipeline state object
|
||||
class PipelineStateMTL : public PipelineState
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
PipelineStateMTL(RendererMTL *inRenderer, const VertexShaderMTL *inVertexShader, const EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShaderMTL *inPixelShader, EDrawPass inDrawPass, EFillMode inFillMode, ETopology inTopology, EDepthTest inDepthTest, EBlendMode inBlendMode, ECullMode inCullMode);
|
||||
virtual ~PipelineStateMTL() override;
|
||||
|
||||
/// Make this pipeline state active (any primitives rendered after this will use this state)
|
||||
virtual void Activate() override;
|
||||
|
||||
private:
|
||||
RendererMTL * mRenderer;
|
||||
RefConst<VertexShaderMTL> mVertexShader;
|
||||
RefConst<PixelShaderMTL> mPixelShader;
|
||||
id<MTLRenderPipelineState> mPipelineState;
|
||||
id<MTLDepthStencilState> mDepthState;
|
||||
MTLCullMode mCullMode;
|
||||
MTLTriangleFillMode mFillMode;
|
||||
};
|
||||
@@ -0,0 +1,163 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/PipelineStateMTL.h>
|
||||
#include <Renderer/MTL/RendererMTL.h>
|
||||
#include <Renderer/MTL/FatalErrorIfFailedMTL.h>
|
||||
|
||||
PipelineStateMTL::PipelineStateMTL(RendererMTL *inRenderer, const VertexShaderMTL *inVertexShader, const EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShaderMTL *inPixelShader, EDrawPass inDrawPass, EFillMode inFillMode, ETopology inTopology, EDepthTest inDepthTest, EBlendMode inBlendMode, ECullMode inCullMode) :
|
||||
mRenderer(inRenderer),
|
||||
mVertexShader(inVertexShader),
|
||||
mPixelShader(inPixelShader)
|
||||
{
|
||||
// Create a vertex descriptor
|
||||
MTLVertexDescriptor *vertex_descriptor = [[MTLVertexDescriptor alloc] init];
|
||||
uint vertex_offset = 0;
|
||||
uint instance_offset = 0, instance_alignment = 4;
|
||||
uint index = 0;
|
||||
for (uint i = 0; i < inInputDescriptionCount; ++i)
|
||||
switch (inInputDescription[i])
|
||||
{
|
||||
case EInputDescription::Position:
|
||||
case EInputDescription::Normal:
|
||||
vertex_descriptor.attributes[index].format = MTLVertexFormatFloat3;
|
||||
vertex_descriptor.attributes[index].offset = vertex_offset;
|
||||
vertex_descriptor.attributes[index].bufferIndex = 0;
|
||||
vertex_offset += 3 * sizeof(float);
|
||||
++index;
|
||||
break;
|
||||
|
||||
case EInputDescription::Color:
|
||||
vertex_descriptor.attributes[index].format = MTLVertexFormatUChar4;
|
||||
vertex_descriptor.attributes[index].offset = vertex_offset;
|
||||
vertex_descriptor.attributes[index].bufferIndex = 0;
|
||||
vertex_offset += 4 * sizeof(uint8);
|
||||
++index;
|
||||
break;
|
||||
|
||||
case EInputDescription::TexCoord:
|
||||
vertex_descriptor.attributes[index].format = MTLVertexFormatFloat2;
|
||||
vertex_descriptor.attributes[index].offset = vertex_offset;
|
||||
vertex_descriptor.attributes[index].bufferIndex = 0;
|
||||
vertex_offset += 2 * sizeof(float);
|
||||
++index;
|
||||
break;
|
||||
|
||||
case EInputDescription::InstanceColor:
|
||||
vertex_descriptor.attributes[index].format = MTLVertexFormatUChar4;
|
||||
vertex_descriptor.attributes[index].offset = instance_offset;
|
||||
vertex_descriptor.attributes[index].bufferIndex = 1;
|
||||
instance_offset += 4 * sizeof(uint8);
|
||||
++index;
|
||||
break;
|
||||
|
||||
case EInputDescription::InstanceTransform:
|
||||
case EInputDescription::InstanceInvTransform:
|
||||
instance_alignment = max(instance_alignment, 16u);
|
||||
instance_offset = AlignUp(instance_offset, 16u);
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
vertex_descriptor.attributes[index].format = MTLVertexFormatFloat4;
|
||||
vertex_descriptor.attributes[index].offset = instance_offset;
|
||||
vertex_descriptor.attributes[index].bufferIndex = 1;
|
||||
instance_offset += 4 * sizeof(float);
|
||||
++index;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Configure layouts
|
||||
vertex_descriptor.layouts[0].stride = vertex_offset;
|
||||
vertex_descriptor.layouts[0].stepRate = 1;
|
||||
vertex_descriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
|
||||
|
||||
if (instance_offset > 0)
|
||||
{
|
||||
vertex_descriptor.layouts[1].stride = AlignUp(instance_offset, instance_alignment);
|
||||
vertex_descriptor.layouts[1].stepRate = 1;
|
||||
vertex_descriptor.layouts[1].stepFunction = MTLVertexStepFunctionPerInstance;
|
||||
}
|
||||
|
||||
// Create the pipeline descriptor
|
||||
MTLRenderPipelineDescriptor *descriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
||||
descriptor.vertexFunction = inVertexShader->GetFunction();
|
||||
descriptor.fragmentFunction = inPixelShader->GetFunction();
|
||||
descriptor.vertexDescriptor = vertex_descriptor;
|
||||
switch (inDrawPass)
|
||||
{
|
||||
case EDrawPass::Shadow:
|
||||
descriptor.depthAttachmentPixelFormat = static_cast<TextureMTL *>(mRenderer->GetShadowMap())->GetTexture().pixelFormat;
|
||||
break;
|
||||
|
||||
case EDrawPass::Normal:
|
||||
descriptor.colorAttachments[0].pixelFormat = mRenderer->GetView().colorPixelFormat;
|
||||
switch (inBlendMode)
|
||||
{
|
||||
case EBlendMode::Write:
|
||||
descriptor.colorAttachments[0].blendingEnabled = NO;
|
||||
break;
|
||||
|
||||
case EBlendMode::AlphaBlend:
|
||||
descriptor.colorAttachments[0].blendingEnabled = YES;
|
||||
descriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
|
||||
descriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
descriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
|
||||
descriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorZero;
|
||||
descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorZero;
|
||||
descriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
|
||||
break;
|
||||
}
|
||||
descriptor.depthAttachmentPixelFormat = mRenderer->GetView().depthStencilPixelFormat;
|
||||
}
|
||||
|
||||
NSError *error = nullptr;
|
||||
mPipelineState = [mRenderer->GetDevice() newRenderPipelineStateWithDescriptor: descriptor error: &error];
|
||||
FatalErrorIfFailed(error);
|
||||
[descriptor release];
|
||||
[vertex_descriptor release];
|
||||
|
||||
// Create depth descriptor
|
||||
MTLDepthStencilDescriptor *depth_descriptor = [[MTLDepthStencilDescriptor new] init];
|
||||
if (inDepthTest == EDepthTest::On)
|
||||
{
|
||||
depth_descriptor.depthCompareFunction = MTLCompareFunctionGreaterEqual;
|
||||
depth_descriptor.depthWriteEnabled = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
depth_descriptor.depthCompareFunction = MTLCompareFunctionAlways;
|
||||
depth_descriptor.depthWriteEnabled = NO;
|
||||
}
|
||||
mDepthState = [mRenderer->GetDevice() newDepthStencilStateWithDescriptor: depth_descriptor];
|
||||
[depth_descriptor release];
|
||||
|
||||
// Determine cull mode
|
||||
if (inCullMode == ECullMode::FrontFace)
|
||||
mCullMode = MTLCullModeFront;
|
||||
else
|
||||
mCullMode = MTLCullModeBack;
|
||||
|
||||
// Determine fill mode
|
||||
if (inFillMode == EFillMode::Solid)
|
||||
mFillMode = MTLTriangleFillModeFill;
|
||||
else
|
||||
mFillMode = MTLTriangleFillModeLines;
|
||||
}
|
||||
|
||||
PipelineStateMTL::~PipelineStateMTL()
|
||||
{
|
||||
[mPipelineState release];
|
||||
[mDepthState release];
|
||||
}
|
||||
|
||||
void PipelineStateMTL::Activate()
|
||||
{
|
||||
id<MTLRenderCommandEncoder> encoder = mRenderer->GetRenderEncoder();
|
||||
[encoder setRenderPipelineState: mPipelineState];
|
||||
[encoder setDepthStencilState: mDepthState];
|
||||
[encoder setCullMode: mCullMode];
|
||||
[encoder setTriangleFillMode: mFillMode];
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/PixelShader.h>
|
||||
|
||||
#include <MetalKit/MetalKit.h>
|
||||
|
||||
/// Pixel shader handle for Metal
|
||||
class PixelShaderMTL : public PixelShader
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
PixelShaderMTL(id<MTLFunction> inFunction) : mFunction(inFunction) { }
|
||||
virtual ~PixelShaderMTL() override { [mFunction release]; }
|
||||
|
||||
/// Access to the function
|
||||
id<MTLFunction> GetFunction() const { return mFunction; }
|
||||
|
||||
private:
|
||||
id<MTLFunction> mFunction;
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/MTL/RendererMTL.h>
|
||||
#include <Renderer/RenderInstances.h>
|
||||
|
||||
class RenderPrimitive;
|
||||
|
||||
/// Metal implementation of a render instances object
|
||||
class RenderInstancesMTL : public RenderInstances
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
RenderInstancesMTL(RendererMTL *inRenderer) : mRenderer(inRenderer) { }
|
||||
virtual ~RenderInstancesMTL() override { Clear(); }
|
||||
|
||||
/// Erase all instance data
|
||||
virtual void Clear() override;
|
||||
|
||||
/// Instance buffer management functions
|
||||
virtual void CreateBuffer(int inNumInstances, int inInstanceSize) override;
|
||||
virtual void * Lock() override;
|
||||
virtual void Unlock() override;
|
||||
|
||||
/// Draw the instances when context has been set by Renderer::BindShader
|
||||
virtual void Draw(RenderPrimitive *inPrimitive, int inStartInstance, int inNumInstances) const override;
|
||||
|
||||
private:
|
||||
RendererMTL * mRenderer;
|
||||
id<MTLBuffer> mBuffer;
|
||||
NSUInteger mBufferSize;
|
||||
NSUInteger mInstanceSize;
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/RenderInstancesMTL.h>
|
||||
#include <Renderer/MTL/RenderPrimitiveMTL.h>
|
||||
|
||||
void RenderInstancesMTL::Clear()
|
||||
{
|
||||
[mBuffer release];
|
||||
mBuffer = nil;
|
||||
}
|
||||
|
||||
void RenderInstancesMTL::CreateBuffer(int inNumInstances, int inInstanceSize)
|
||||
{
|
||||
mInstanceSize = NSUInteger(inInstanceSize);
|
||||
NSUInteger size = mInstanceSize * inNumInstances;
|
||||
if (mBuffer == nullptr || mBufferSize < size)
|
||||
{
|
||||
Clear();
|
||||
|
||||
mBuffer = [mRenderer->GetView().device newBufferWithLength: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeShared | MTLResourceHazardTrackingModeTracked];
|
||||
mBufferSize = size;
|
||||
}
|
||||
}
|
||||
|
||||
void *RenderInstancesMTL::Lock()
|
||||
{
|
||||
return mBuffer.contents;
|
||||
}
|
||||
|
||||
void RenderInstancesMTL::Unlock()
|
||||
{
|
||||
}
|
||||
|
||||
void RenderInstancesMTL::Draw(RenderPrimitive *inPrimitive, int inStartInstance, int inNumInstances) const
|
||||
{
|
||||
if (inNumInstances <= 0)
|
||||
return;
|
||||
|
||||
id<MTLRenderCommandEncoder> encoder = mRenderer->GetRenderEncoder();
|
||||
RenderPrimitiveMTL *prim = static_cast<RenderPrimitiveMTL *>(inPrimitive);
|
||||
|
||||
[encoder setVertexBuffer: prim->mVertexBuffer offset: 0 atIndex: 0];
|
||||
[encoder setVertexBuffer: mBuffer offset: mInstanceSize * inStartInstance atIndex: 1];
|
||||
if (prim->mIndexBuffer == nil)
|
||||
[encoder drawPrimitives: prim->mPrimitiveType vertexStart: 0 vertexCount: prim->mNumVtxToDraw instanceCount: inNumInstances];
|
||||
else
|
||||
[encoder drawIndexedPrimitives: prim->mPrimitiveType indexCount: prim->mNumIdxToDraw indexType: MTLIndexTypeUInt32 indexBuffer: prim->mIndexBuffer indexBufferOffset: 0 instanceCount: inNumInstances];
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/RenderPrimitive.h>
|
||||
#include <Renderer/MTL/RendererMTL.h>
|
||||
|
||||
/// Metal implementation of a render primitive
|
||||
class RenderPrimitiveMTL : public RenderPrimitive
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
RenderPrimitiveMTL(RendererMTL *inRenderer, MTLPrimitiveType inType) : mRenderer(inRenderer), mPrimitiveType(inType) { }
|
||||
virtual ~RenderPrimitiveMTL() override { Clear(); }
|
||||
|
||||
/// Vertex buffer management functions
|
||||
virtual void CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData = nullptr) override;
|
||||
virtual void ReleaseVertexBuffer() override;
|
||||
virtual void * LockVertexBuffer() override;
|
||||
virtual void UnlockVertexBuffer() override;
|
||||
|
||||
/// Index buffer management functions
|
||||
virtual void CreateIndexBuffer(int inNumIdx, const uint32 *inData = nullptr) override;
|
||||
virtual void ReleaseIndexBuffer() override;
|
||||
virtual uint32 * LockIndexBuffer() override;
|
||||
virtual void UnlockIndexBuffer() override;
|
||||
|
||||
/// Draw the primitive
|
||||
virtual void Draw() const override;
|
||||
|
||||
private:
|
||||
friend class RenderInstancesMTL;
|
||||
|
||||
RendererMTL * mRenderer;
|
||||
MTLPrimitiveType mPrimitiveType;
|
||||
id<MTLBuffer> mVertexBuffer;
|
||||
id<MTLBuffer> mIndexBuffer;
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/RenderPrimitiveMTL.h>
|
||||
|
||||
void RenderPrimitiveMTL::ReleaseVertexBuffer()
|
||||
{
|
||||
[mVertexBuffer release];
|
||||
mVertexBuffer = nil;
|
||||
|
||||
RenderPrimitive::ReleaseVertexBuffer();
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::ReleaseIndexBuffer()
|
||||
{
|
||||
[mIndexBuffer release];
|
||||
mIndexBuffer = nil;
|
||||
|
||||
RenderPrimitive::ReleaseIndexBuffer();
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData)
|
||||
{
|
||||
RenderPrimitive::CreateVertexBuffer(inNumVtx, inVtxSize, inData);
|
||||
|
||||
NSUInteger size = NSUInteger(inNumVtx) * inVtxSize;
|
||||
if (inData != nullptr)
|
||||
mVertexBuffer = [mRenderer->GetDevice() newBufferWithBytes: inData length: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeShared | MTLResourceHazardTrackingModeTracked];
|
||||
else
|
||||
mVertexBuffer = [mRenderer->GetDevice() newBufferWithLength: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeShared | MTLResourceHazardTrackingModeTracked];
|
||||
}
|
||||
|
||||
void *RenderPrimitiveMTL::LockVertexBuffer()
|
||||
{
|
||||
return mVertexBuffer.contents;
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::UnlockVertexBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::CreateIndexBuffer(int inNumIdx, const uint32 *inData)
|
||||
{
|
||||
RenderPrimitive::CreateIndexBuffer(inNumIdx, inData);
|
||||
|
||||
NSUInteger size = NSUInteger(inNumIdx) * sizeof(uint32);
|
||||
if (inData != nullptr)
|
||||
mIndexBuffer = [mRenderer->GetDevice() newBufferWithBytes: inData length: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeManaged | MTLResourceHazardTrackingModeTracked];
|
||||
else
|
||||
mIndexBuffer = [mRenderer->GetDevice() newBufferWithLength: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeShared | MTLResourceHazardTrackingModeTracked];
|
||||
}
|
||||
|
||||
uint32 *RenderPrimitiveMTL::LockIndexBuffer()
|
||||
{
|
||||
return (uint32 *)mIndexBuffer.contents;
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::UnlockIndexBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::Draw() const
|
||||
{
|
||||
id<MTLRenderCommandEncoder> encoder = mRenderer->GetRenderEncoder();
|
||||
|
||||
[encoder setVertexBuffer: mVertexBuffer offset: 0 atIndex: 0];
|
||||
if (mIndexBuffer == nil)
|
||||
[encoder drawPrimitives: mPrimitiveType vertexStart: 0 vertexCount: mNumVtxToDraw];
|
||||
else
|
||||
[encoder drawIndexedPrimitives: mPrimitiveType indexCount: mNumIdxToDraw indexType: MTLIndexTypeUInt32 indexBuffer: mIndexBuffer indexBufferOffset: 0];
|
||||
}
|
||||
46
lib/All/JoltPhysics/TestFramework/Renderer/MTL/RendererMTL.h
Normal file
46
lib/All/JoltPhysics/TestFramework/Renderer/MTL/RendererMTL.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Renderer/MTL/TextureMTL.h>
|
||||
|
||||
#include <MetalKit/MetalKit.h>
|
||||
|
||||
/// Metal renderer
|
||||
class RendererMTL : public Renderer
|
||||
{
|
||||
public:
|
||||
virtual ~RendererMTL() override;
|
||||
|
||||
// See: Renderer
|
||||
virtual void Initialize(ApplicationWindow *inWindow) override;
|
||||
virtual bool BeginFrame(const CameraState &inCamera, float inWorldScale) override;
|
||||
virtual void EndShadowPass() override;
|
||||
virtual void EndFrame() override;
|
||||
virtual void SetProjectionMode() override;
|
||||
virtual void SetOrthoMode() override;
|
||||
virtual Ref<Texture> CreateTexture(const Surface *inSurface) override;
|
||||
virtual Ref<VertexShader> CreateVertexShader(const char *inName) override;
|
||||
virtual Ref<PixelShader> CreatePixelShader(const char *inName) override;
|
||||
virtual unique_ptr<PipelineState> 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) override;
|
||||
virtual RenderPrimitive * CreateRenderPrimitive(PipelineState::ETopology inType) override;
|
||||
virtual RenderInstances * CreateRenderInstances() override;
|
||||
virtual Texture * GetShadowMap() const override { return mShadowMap; }
|
||||
virtual void OnWindowResize() override { }
|
||||
|
||||
MTKView * GetView() const { return mView; }
|
||||
id<MTLDevice> GetDevice() const { return mView.device; }
|
||||
id<MTLRenderCommandEncoder> GetRenderEncoder() const { return mRenderEncoder; }
|
||||
|
||||
private:
|
||||
MTKView * mView;
|
||||
MTLRenderPassDescriptor * mShadowRenderPass;
|
||||
Ref<TextureMTL> mShadowMap;
|
||||
id<MTLLibrary> mShaderLibrary;
|
||||
id<MTLCommandQueue> mCommandQueue;
|
||||
id<MTLCommandBuffer> mCommandBuffer;
|
||||
id<MTLRenderCommandEncoder> mRenderEncoder;
|
||||
};
|
||||
184
lib/All/JoltPhysics/TestFramework/Renderer/MTL/RendererMTL.mm
Normal file
184
lib/All/JoltPhysics/TestFramework/Renderer/MTL/RendererMTL.mm
Normal file
@@ -0,0 +1,184 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/RendererMTL.h>
|
||||
#include <Renderer/MTL/RenderPrimitiveMTL.h>
|
||||
#include <Renderer/MTL/RenderInstancesMTL.h>
|
||||
#include <Renderer/MTL/PipelineStateMTL.h>
|
||||
#include <Renderer/MTL/VertexShaderMTL.h>
|
||||
#include <Renderer/MTL/PixelShaderMTL.h>
|
||||
#include <Renderer/MTL/TextureMTL.h>
|
||||
#include <Renderer/MTL/FatalErrorIfFailedMTL.h>
|
||||
#include <Window/ApplicationWindowMacOS.h>
|
||||
#include <Utils/Log.h>
|
||||
#include <Utils/AssetStream.h>
|
||||
#include <Jolt/Core/Profiler.h>
|
||||
|
||||
RendererMTL::~RendererMTL()
|
||||
{
|
||||
[mCommandQueue release];
|
||||
[mShadowRenderPass release];
|
||||
[mShaderLibrary release];
|
||||
}
|
||||
|
||||
void RendererMTL::Initialize(ApplicationWindow *inWindow)
|
||||
{
|
||||
Renderer::Initialize(inWindow);
|
||||
|
||||
mView = static_cast<ApplicationWindowMacOS *>(inWindow)->GetMetalView();
|
||||
|
||||
id<MTLDevice> 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];
|
||||
}
|
||||
|
||||
bool 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();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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<Texture> RendererMTL::CreateTexture(const Surface *inSurface)
|
||||
{
|
||||
return new TextureMTL(this, inSurface);
|
||||
}
|
||||
|
||||
Ref<VertexShader> RendererMTL::CreateVertexShader(const char *inName)
|
||||
{
|
||||
id<MTLFunction> function = [mShaderLibrary newFunctionWithName: [NSString stringWithCString: inName encoding: NSUTF8StringEncoding]];
|
||||
if (function == nil)
|
||||
FatalError("Vertex shader %s not found", inName);
|
||||
return new VertexShaderMTL(function);
|
||||
}
|
||||
|
||||
Ref<PixelShader> RendererMTL::CreatePixelShader(const char *inName)
|
||||
{
|
||||
id<MTLFunction> function = [mShaderLibrary newFunctionWithName: [NSString stringWithCString: inName encoding: NSUTF8StringEncoding]];
|
||||
if (function == nil)
|
||||
FatalError("Pixel shader %s not found", inName);
|
||||
return new PixelShaderMTL(function);
|
||||
}
|
||||
|
||||
unique_ptr<PipelineState> 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<PipelineStateMTL>(this, static_cast<const VertexShaderMTL *>(inVertexShader), inInputDescription, inInputDescriptionCount, static_cast<const PixelShaderMTL *>(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
|
||||
32
lib/All/JoltPhysics/TestFramework/Renderer/MTL/TextureMTL.h
Normal file
32
lib/All/JoltPhysics/TestFramework/Renderer/MTL/TextureMTL.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/Texture.h>
|
||||
|
||||
#include <MetalKit/MetalKit.h>
|
||||
|
||||
class RendererMTL;
|
||||
|
||||
/// Metal texture
|
||||
class TextureMTL : public Texture
|
||||
{
|
||||
public:
|
||||
/// Constructor, called by Renderer::CreateTextureMTL
|
||||
TextureMTL(RendererMTL *inRenderer, const Surface *inSurface); // Create a normal Texture
|
||||
TextureMTL(RendererMTL *inRenderer, int inWidth, int inHeight); // Create a render target (depth only)
|
||||
virtual ~TextureMTL() override;
|
||||
|
||||
/// Bind texture to the pixel shader
|
||||
virtual void Bind() const override;
|
||||
|
||||
/// Access to the metal texture
|
||||
id<MTLTexture> GetTexture() const { return mTexture; }
|
||||
|
||||
private:
|
||||
RendererMTL * mRenderer;
|
||||
id<MTLTexture> mTexture;
|
||||
};
|
||||
|
||||
98
lib/All/JoltPhysics/TestFramework/Renderer/MTL/TextureMTL.mm
Normal file
98
lib/All/JoltPhysics/TestFramework/Renderer/MTL/TextureMTL.mm
Normal file
@@ -0,0 +1,98 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/TextureMTL.h>
|
||||
#include <Renderer/MTL/RendererMTL.h>
|
||||
#include <Renderer/MTL/FatalErrorIfFailedMTL.h>
|
||||
#include <Image/BlitSurface.h>
|
||||
|
||||
TextureMTL::TextureMTL(RendererMTL *inRenderer, const Surface *inSurface) :
|
||||
Texture(inSurface->GetWidth(), inSurface->GetHeight()),
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
ESurfaceFormat format = inSurface->GetFormat();
|
||||
MTLPixelFormat mt_format = MTLPixelFormatBGRA8Unorm;
|
||||
switch (format)
|
||||
{
|
||||
case ESurfaceFormat::A4L4:
|
||||
case ESurfaceFormat::A8L8:
|
||||
case ESurfaceFormat::A4R4G4B4:
|
||||
case ESurfaceFormat::R8G8B8:
|
||||
case ESurfaceFormat::B8G8R8:
|
||||
case ESurfaceFormat::X8R8G8B8:
|
||||
case ESurfaceFormat::X8B8G8R8:
|
||||
case ESurfaceFormat::A8R8G8B8:
|
||||
case ESurfaceFormat::A8B8G8R8: mt_format = MTLPixelFormatBGRA8Unorm; format = ESurfaceFormat::A8R8G8B8; break;
|
||||
case ESurfaceFormat::L8: mt_format = MTLPixelFormatR8Unorm; break;
|
||||
case ESurfaceFormat::A8: mt_format = MTLPixelFormatA8Unorm; break;
|
||||
case ESurfaceFormat::R5G6B5:
|
||||
case ESurfaceFormat::X1R5G5B5:
|
||||
case ESurfaceFormat::X4R4G4B4: mt_format = MTLPixelFormatB5G6R5Unorm; format = ESurfaceFormat::R5G6B5; break;
|
||||
case ESurfaceFormat::A1R5G5B5: mt_format = MTLPixelFormatA1BGR5Unorm; break;
|
||||
case ESurfaceFormat::Invalid:
|
||||
default: JPH_ASSERT(false); break;
|
||||
}
|
||||
|
||||
// Blit the surface to another temporary surface if the format changed
|
||||
const Surface *surface = inSurface;
|
||||
Ref<Surface> tmp;
|
||||
if (format != inSurface->GetFormat())
|
||||
{
|
||||
tmp = new SoftwareSurface(mWidth, mHeight, format);
|
||||
BlitSurface(inSurface, tmp);
|
||||
surface = tmp;
|
||||
}
|
||||
|
||||
// Create descriptor
|
||||
MTLTextureDescriptor *descriptor = [[MTLTextureDescriptor alloc] init];
|
||||
descriptor.textureType = MTLTextureType2D;
|
||||
descriptor.usage = MTLTextureUsageShaderRead;
|
||||
descriptor.pixelFormat = mt_format;
|
||||
descriptor.width = mWidth;
|
||||
descriptor.height = mHeight;
|
||||
descriptor.storageMode = MTLStorageModeManaged;
|
||||
|
||||
MTLRegion region =
|
||||
{
|
||||
{ 0, 0, 0 },
|
||||
{ NSUInteger(mWidth), NSUInteger(mHeight), 1}
|
||||
};
|
||||
|
||||
// Create texture
|
||||
mTexture = [inRenderer->GetDevice() newTextureWithDescriptor: descriptor];
|
||||
surface->Lock(ESurfaceLockMode::Read);
|
||||
[mTexture replaceRegion: region mipmapLevel:0 withBytes: surface->GetData() bytesPerRow: surface->GetStride()];
|
||||
surface->UnLock();
|
||||
|
||||
[descriptor release];
|
||||
}
|
||||
|
||||
TextureMTL::TextureMTL(RendererMTL *inRenderer, int inWidth, int inHeight) :
|
||||
Texture(inWidth, inHeight),
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
MTLTextureDescriptor *descriptor = [[MTLTextureDescriptor alloc] init];
|
||||
descriptor.textureType = MTLTextureType2D;
|
||||
descriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
|
||||
descriptor.pixelFormat = MTLPixelFormatDepth32Float;
|
||||
descriptor.width = mWidth;
|
||||
descriptor.height = mHeight;
|
||||
descriptor.storageMode = MTLStorageModePrivate;
|
||||
|
||||
mTexture = [inRenderer->GetDevice() newTextureWithDescriptor: descriptor];
|
||||
|
||||
[descriptor release];
|
||||
}
|
||||
|
||||
TextureMTL::~TextureMTL()
|
||||
{
|
||||
[mTexture release];
|
||||
}
|
||||
|
||||
void TextureMTL::Bind() const
|
||||
{
|
||||
[mRenderer->GetRenderEncoder() setFragmentTexture: mTexture atIndex: 0];
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/VertexShader.h>
|
||||
|
||||
#include <MetalKit/MetalKit.h>
|
||||
|
||||
/// Vertex shader handle for Metal
|
||||
class VertexShaderMTL : public VertexShader
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
VertexShaderMTL(id<MTLFunction> inFunction) : mFunction(inFunction) { }
|
||||
virtual ~VertexShaderMTL() override { [mFunction release]; }
|
||||
|
||||
/// Access to the function
|
||||
id<MTLFunction> GetFunction() const { return mFunction; }
|
||||
|
||||
private:
|
||||
id<MTLFunction> mFunction;
|
||||
};
|
||||
Reference in New Issue
Block a user