Last active
August 29, 2015 14:22
-
-
Save attilaz/f9f8cc132ecfee38fb7f to your computer and use it in GitHub Desktop.
renderer_mtl.mm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * 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