Skip to content

Instantly share code, notes, and snippets.

@attilaz
Last active August 29, 2015 14:22
Show Gist options
  • Save attilaz/f9f8cc132ecfee38fb7f to your computer and use it in GitHub Desktop.
Save attilaz/f9f8cc132ecfee38fb7f to your computer and use it in GitHub Desktop.
renderer_mtl.mm
/*
* Copyright 2011-2015 Attila Kocsis, Branimir Karadzic. All rights reserved.
* License: http://www.opensource.org/licenses/BSD-2-Clause
*/
#include "bgfx_p.h"
#if BGFX_CONFIG_RENDERER_METAL
#import <QuartzCore/CAMetalLayer.h>
#import <UIKit/UiView.h>
#import <Metal/Metal.h>
namespace bgfx { namespace mtl
{
//c++ wrapper
#define MTL_CLASS(name) \
class name \
{ \
public: \
name(id <MTL##name> _obj = nil) : m_##name(_obj) {} \
operator id <MTL##name>() { return m_##name; } \
id <MTL##name> m_##name;
#define MTL_CLASS_END };
typedef void (*mtlCallback)(void* userData);
//TODO: ??MTLBlitCommandEncoder??
MTL_CLASS(Buffer)
void* contents() { return m_Buffer.contents; }
uint32_t length() { return (uint32_t)m_Buffer.length; }
MTL_CLASS_END
MTL_CLASS(CommandBuffer)
// Creating Command Encoders
id<MTLRenderCommandEncoder> renderCommandEncoderWithDescriptor( MTLRenderPassDescriptor *renderPassDescriptor){
return [m_CommandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
}
id<MTLComputeCommandEncoder> computeCommandEncoder() { return [m_CommandBuffer computeCommandEncoder]; }
// Scheduling and Executing Commands
void enqueue() { [m_CommandBuffer enqueue]; }
void commit() { [m_CommandBuffer commit]; }
void addCompletedHandler(mtlCallback cb, void* data) { [m_CommandBuffer addCompletedHandler:^(id <MTLCommandBuffer> cmdb){ cb(data); }]; }
void presentDrawable(id<MTLDrawable> drawable) { [m_CommandBuffer presentDrawable:drawable]; }
void waitUntilCompleted() { [m_CommandBuffer waitUntilCompleted]; }
MTL_CLASS_END
MTL_CLASS(CommandQueue)
id<MTLCommandBuffer> commandBuffer() { return [m_CommandQueue commandBuffer]; }
id<MTLCommandBuffer> commandBufferWithUnretainedReferences() { return [m_CommandQueue commandBufferWithUnretainedReferences]; }
MTL_CLASS_END
MTL_CLASS(ComputeCommandEncoder)
void setComputePipelineState(id<MTLComputePipelineState> state) { [m_ComputeCommandEncoder setComputePipelineState:state]; }
void setBuffer(id<MTLBuffer> buffer, NSUInteger offset, NSUInteger index) { [m_ComputeCommandEncoder setBuffer:buffer offset:offset atIndex:index]; }
void setTexture(id<MTLTexture> texture, NSUInteger index) { [m_ComputeCommandEncoder setTexture:texture atIndex:index]; }
void setSamplerState(id<MTLSamplerState> sampler, NSUInteger index) { [m_ComputeCommandEncoder setSamplerState:sampler atIndex:index]; }
void endEncoding() {[m_ComputeCommandEncoder endEncoding] ;}
MTL_CLASS_END
MTL_CLASS(Device)
bool supportsFeatureSet(MTLFeatureSet featureSet) { return [m_Device supportsFeatureSet:featureSet]; }
id<MTLLibrary> newDefaultLibrary() { return [m_Device newDefaultLibrary]; }
id<MTLCommandQueue> newCommandQueue() { return [m_Device newCommandQueue]; }
id<MTLCommandQueue> newCommandQueueWithMaxCommandBufferCount(NSUInteger maxCommandBufferCount) { return [m_Device newCommandQueueWithMaxCommandBufferCount:maxCommandBufferCount]; }
// Creating Resources
id<MTLBuffer> newBufferWithLength(unsigned int length, MTLResourceOptions options) {
return [m_Device newBufferWithLength:length options:options ]; }
id<MTLTexture> newTextureWithDescriptor(MTLTextureDescriptor * descriptor) { return [m_Device newTextureWithDescriptor:descriptor]; }
id<MTLSamplerState> newSamplerStateWithDescriptor(MTLSamplerDescriptor * descriptor) { return [m_Device newSamplerStateWithDescriptor:descriptor]; }
// Creating Command Objects Needed to Render Graphics
id<MTLDepthStencilState> newDepthStencilStateWithDescriptor(MTLDepthStencilDescriptor * descriptor) { return [m_Device newDepthStencilStateWithDescriptor:descriptor]; }
id <MTLRenderPipelineState> newRenderPipelineStateWithDescriptor(MTLRenderPipelineDescriptor *descriptor)
{
NSError *error;
//TODO: log error
return [m_Device newRenderPipelineStateWithDescriptor:descriptor error:&error];
}
// Creating Command Objects Needed to Perform Computational Tasks
id <MTLComputePipelineState> newComputePipelineStateWithFunction(id <MTLFunction> computeFunction)
{
NSError *error;
//TODO: log error
return [m_Device newComputePipelineStateWithFunction:computeFunction error:&error];
}
MTL_CLASS_END
MTL_CLASS(Function)
NSArray *vertexAttributes() { return m_Function.vertexAttributes; }
MTL_CLASS_END
MTL_CLASS(Library)
id <MTLFunction> newFunctionWithName(const char* functionName) { return [m_Library newFunctionWithName:@(functionName)]; }
MTL_CLASS_END
MTL_CLASS(RenderCommandEncoder)
// Setting Graphics Rendering State
void setBlendColorRed(float red, float green, float blue, float alpha) { [m_RenderCommandEncoder setBlendColorRed:red green:green blue:blue alpha:alpha]; }
void setCullMode(MTLCullMode cullMode) { [m_RenderCommandEncoder setCullMode:cullMode]; }
void setDepthBias(float depthBias, float slopeScale, float clamp) { [m_RenderCommandEncoder setDepthBias:depthBias slopeScale:slopeScale clamp:clamp]; }
void setDepthStencilState(id<MTLDepthStencilState> depthStencilState) { [m_RenderCommandEncoder setDepthStencilState:depthStencilState];}
void setFrontFacingWinding(MTLWinding frontFacingWinding) { [m_RenderCommandEncoder setFrontFacingWinding:frontFacingWinding]; }
void setRenderPipelineState(id<MTLRenderPipelineState> pipelineState) { [m_RenderCommandEncoder setRenderPipelineState:pipelineState]; }
void setScissorRect(MTLScissorRect rect) { [m_RenderCommandEncoder setScissorRect:rect]; }
void setStencilReferenceValue(uint32_t ref) { [m_RenderCommandEncoder setStencilReferenceValue:ref]; }
void setTriangleFillMode(MTLTriangleFillMode fillMode) { [m_RenderCommandEncoder setTriangleFillMode:fillMode]; }
void setViewport(MTLViewport viewport) { [m_RenderCommandEncoder setViewport:viewport]; }
void setVisibilityResultMode(MTLVisibilityResultMode mode, NSUInteger offset) { [m_RenderCommandEncoder setVisibilityResultMode:mode offset:offset]; }
// Specifying Resources for a Vertex Function
void setVertexBuffer(id<MTLBuffer> buffer, NSUInteger offset, NSUInteger index) { [m_RenderCommandEncoder setVertexBuffer:buffer offset:offset atIndex:index]; }
void setVertexSamplerState(id<MTLSamplerState> sampler, NSUInteger index) { [m_RenderCommandEncoder setVertexSamplerState:sampler atIndex:index]; }
void setVertexTexture(id<MTLTexture> texture, NSUInteger index) { [m_RenderCommandEncoder setVertexTexture:texture atIndex:index]; }
// Specifying Resources for a Fragment Function
void setFragmentBuffer(id<MTLBuffer> buffer, NSUInteger offset, NSUInteger index) { [m_RenderCommandEncoder setFragmentBuffer:buffer offset:offset atIndex:index]; }
void setFragmentSamplerState(id<MTLSamplerState> sampler, NSUInteger index) { [m_RenderCommandEncoder setFragmentSamplerState:sampler atIndex:index]; }
void setFragmentTexture(id<MTLTexture> texture, NSUInteger index) { [m_RenderCommandEncoder setFragmentTexture:texture atIndex:index]; }
//Drawing Geometric Primitives
void drawIndexedPrimitives(MTLPrimitiveType primitiveType, NSUInteger indexCount, MTLIndexType indexType, id<MTLBuffer> indexBuffer, NSUInteger indexBufferOffset, NSUInteger instanceCount)
{
[m_RenderCommandEncoder drawIndexedPrimitives:primitiveType indexCount:indexCount indexType:indexType indexBuffer:indexBuffer indexBufferOffset:indexBufferOffset instanceCount:instanceCount];
}
void drawPrimitives(MTLPrimitiveType primitiveType, NSUInteger vertexStart, NSUInteger vertexCount, NSUInteger instanceCount) {
[m_RenderCommandEncoder drawPrimitives:primitiveType vertexStart:vertexStart vertexCount:vertexCount instanceCount:instanceCount];}
void pushDebugGroup(const char* string) { [m_RenderCommandEncoder pushDebugGroup:@(string)]; }
void popDebugGroup() {[m_RenderCommandEncoder popDebugGroup];}
void endEncoding() {[m_RenderCommandEncoder endEncoding] ;}
MTL_CLASS_END
MTL_CLASS(Texture)
// Copying Data into a Texture Image
void replaceRegion(MTLRegion region, NSUInteger level, NSUInteger slice, const void *pixelBytes, NSUInteger bytesPerRow, NSUInteger bytesPerImage) { [m_Texture replaceRegion:region mipmapLevel:level slice:slice withBytes:pixelBytes bytesPerRow:bytesPerRow bytesPerImage:bytesPerImage]; }
void replaceRegion(MTLRegion region, NSUInteger level, const void *pixelBytes, NSUInteger bytesPerRow) { [m_Texture replaceRegion:region mipmapLevel:level withBytes:pixelBytes bytesPerRow:bytesPerRow]; }
// Copying Data from a Texture Image
void getBytes(void *pixelBytes, NSUInteger bytesPerRow, NSUInteger bytesPerImage, MTLRegion region, NSUInteger mipmapLevel, NSUInteger slice) { [m_Texture getBytes:pixelBytes bytesPerRow:bytesPerRow bytesPerImage:bytesPerImage fromRegion:region mipmapLevel:mipmapLevel slice:slice]; }
void getBytes(void *pixelBytes, NSUInteger bytesPerRow, MTLRegion region, NSUInteger mipmapLevel) { [m_Texture getBytes:pixelBytes bytesPerRow:bytesPerRow fromRegion:region mipmapLevel:mipmapLevel]; }
// Creating Textures by Reusing Image Data
id<MTLTexture> newTextureViewWithPixelFormat(MTLPixelFormat pixelFormat) { return [m_Texture newTextureViewWithPixelFormat:pixelFormat]; }
MTL_CLASS_END
typedef id<MTLComputePipelineState> ComputePipelineState;
typedef id<MTLDepthStencilState> DepthStencilState;
typedef id<MTLRenderPipelineState> RenderPipelineState;
typedef id<MTLSamplerState> SamplerState;
// Descriptors
typedef MTLRenderPipelineDescriptor* RenderPipelineDescriptor;
MTLRenderPipelineDescriptor* newRenderPipelineDescriptor() { return [MTLRenderPipelineDescriptor new]; }
typedef MTLDepthStencilDescriptor* DepthStencilDescriptor;
MTLDepthStencilDescriptor* newDepthStencilDescriptor() { return [MTLDepthStencilDescriptor new]; }
typedef MTLRenderPassColorAttachmentDescriptor* RenderPassColorAttachmentDescriptor;
typedef MTLRenderPassDescriptor* RenderPassDescriptor;
MTLRenderPassDescriptor* newRenderPassDescriptor() { return [MTLRenderPassDescriptor new]; }
// end of c++ wrapper
struct RendererContextMtl : public RendererContextI
{
RendererContextMtl()
{
}
~RendererContextMtl()
{
}
bool init()
{
if ( NSClassFromString(@"CAMetalLayer") != nil)
{
m_device = (id<MTLDevice>)g_platformData.context;
if (NULL == m_device)
m_device = MTLCreateSystemDefaultDevice();
}
if (m_device==NULL)
{
BX_WARN(NULL != m_device, "Unable to create Metal device.");
return false;
}
m_commandQueue = m_device.newCommandQueue();
BGFX_FATAL(NULL != m_commandQueue, Fatal::UnableToInitialize, "Unable to create Metal device.");
return true;
}
void shutdown()
{
}
RendererType::Enum getRendererType() const BX_OVERRIDE
{
return RendererType::Metal;
}
const char* getRendererName() const BX_OVERRIDE
{
return BGFX_RENDERER_METAL_NAME;
}
void flip(HMD& /*_hmd*/) BX_OVERRIDE
{
}
void createIndexBuffer(IndexBufferHandle /*_handle*/, Memory* /*_mem*/, uint16_t /*_flags*/) BX_OVERRIDE
{
}
void destroyIndexBuffer(IndexBufferHandle /*_handle*/) BX_OVERRIDE
{
}
void createVertexDecl(VertexDeclHandle /*_handle*/, const VertexDecl& /*_decl*/) BX_OVERRIDE
{
}
void destroyVertexDecl(VertexDeclHandle /*_handle*/) BX_OVERRIDE
{
}
void createVertexBuffer(VertexBufferHandle /*_handle*/, Memory* /*_mem*/, VertexDeclHandle /*_declHandle*/, uint16_t /*_flags*/) BX_OVERRIDE
{
}
void destroyVertexBuffer(VertexBufferHandle /*_handle*/) BX_OVERRIDE
{
}
void createDynamicIndexBuffer(IndexBufferHandle /*_handle*/, uint32_t /*_size*/, uint16_t /*_flags*/) BX_OVERRIDE
{
}
void updateDynamicIndexBuffer(IndexBufferHandle /*_handle*/, uint32_t /*_offset*/, uint32_t /*_size*/, Memory* /*_mem*/) BX_OVERRIDE
{
}
void destroyDynamicIndexBuffer(IndexBufferHandle /*_handle*/) BX_OVERRIDE
{
}
void createDynamicVertexBuffer(VertexBufferHandle /*_handle*/, uint32_t /*_size*/, uint16_t /*_flags*/) BX_OVERRIDE
{
}
void updateDynamicVertexBuffer(VertexBufferHandle /*_handle*/, uint32_t /*_offset*/, uint32_t /*_size*/, Memory* /*_mem*/) BX_OVERRIDE
{
}
void destroyDynamicVertexBuffer(VertexBufferHandle /*_handle*/) BX_OVERRIDE
{
}
void createShader(ShaderHandle /*_handle*/, Memory* /*_mem*/) BX_OVERRIDE
{
}
void destroyShader(ShaderHandle /*_handle*/) BX_OVERRIDE
{
}
void createProgram(ProgramHandle /*_handle*/, ShaderHandle /*_vsh*/, ShaderHandle /*_fsh*/) BX_OVERRIDE
{
}
void destroyProgram(ProgramHandle /*_handle*/) BX_OVERRIDE
{
}
void createTexture(TextureHandle /*_handle*/, Memory* /*_mem*/, uint32_t /*_flags*/, uint8_t /*_skip*/) BX_OVERRIDE
{
}
void updateTextureBegin(TextureHandle /*_handle*/, uint8_t /*_side*/, uint8_t /*_mip*/) BX_OVERRIDE
{
}
void updateTexture(TextureHandle /*_handle*/, uint8_t /*_side*/, uint8_t /*_mip*/, const Rect& /*_rect*/, uint16_t /*_z*/, uint16_t /*_depth*/, uint16_t /*_pitch*/, const Memory* /*_mem*/) BX_OVERRIDE
{
}
void updateTextureEnd() BX_OVERRIDE
{
}
void resizeTexture(TextureHandle /*_handle*/, uint16_t /*_width*/, uint16_t /*_height*/) BX_OVERRIDE
{
}
void destroyTexture(TextureHandle /*_handle*/) BX_OVERRIDE
{
}
void createFrameBuffer(FrameBufferHandle /*_handle*/, uint8_t /*_num*/, const TextureHandle* /*_textureHandles*/) BX_OVERRIDE
{
}
void createFrameBuffer(FrameBufferHandle /*_handle*/, void* /*_nwh*/, uint32_t /*_width*/, uint32_t /*_height*/, TextureFormat::Enum /*_depthFormat*/) BX_OVERRIDE
{
}
void destroyFrameBuffer(FrameBufferHandle /*_handle*/) BX_OVERRIDE
{
}
void createUniform(UniformHandle /*_handle*/, UniformType::Enum /*_type*/, uint16_t /*_num*/, const char* /*_name*/) BX_OVERRIDE
{
}
void destroyUniform(UniformHandle /*_handle*/) BX_OVERRIDE
{
}
void saveScreenShot(const char* /*_filePath*/) BX_OVERRIDE
{
}
void updateViewName(uint8_t /*_id*/, const char* /*_name*/) BX_OVERRIDE
{
}
void updateUniform(uint16_t /*_loc*/, const void* /*_data*/, uint32_t /*_size*/) BX_OVERRIDE
{
}
void setMarker(const char* /*_marker*/, uint32_t /*_size*/) BX_OVERRIDE
{
}
void submit(Frame* /*_render*/, ClearQuad& /*_clearQuad*/, TextVideoMemBlitter& /*_textVideoMemBlitter*/) BX_OVERRIDE
{
mtl::CommandBuffer commandBuffer = m_commandQueue.commandBuffer();
UIView* view = (UIView*)g_platformData.nwh;
id <CAMetalDrawable> metalDrawable = (id<CAMetalDrawable>)view.layer;
//TODO: acquire CAMetalDrawable just before we really need it. When we encoding commands to the UIView
//create renderPassDescriptor
//TODO: we should use reuse renderPassDecriptor or cache them for each view and recreate if any parameter changed
mtl::RenderPassDescriptor renderPassDescriptor = newRenderPassDescriptor();
// create a color attachment every frame since we have to recreate the texture every frame
RenderPassColorAttachmentDescriptor colorAttachment = renderPassDescriptor.colorAttachments[0];
colorAttachment.texture = metalDrawable.texture;
// make sure to clear every frame for best performance
colorAttachment.loadAction = MTLLoadActionClear;
colorAttachment.clearColor = MTLClearColorMake(0.65f, 0.65f, 0.65f, 1.0f);
// Get a render encoder
mtl::RenderCommandEncoder renderEncoder = commandBuffer.renderCommandEncoderWithDescriptor(renderPassDescriptor);
//todo: submit draw
// Present and commit the command buffer
commandBuffer.presentDrawable(metalDrawable);
commandBuffer.commit();
//TODO: async wait with double/triple buffering frame data
commandBuffer.waitUntilCompleted();
}
void blitSetup(TextVideoMemBlitter& /*_blitter*/) BX_OVERRIDE
{
}
void blitRender(TextVideoMemBlitter& /*_blitter*/, uint32_t /*_numIndices*/) BX_OVERRIDE
{
}
Device m_device;
CommandQueue m_commandQueue;
};
static RendererContextMtl* s_renderMtl;
RendererContextI* render
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment