diff options
Diffstat (limited to '3rdparty/bgfx/src/renderer_mtl.mm')
-rw-r--r-- | 3rdparty/bgfx/src/renderer_mtl.mm | 1601 |
1 files changed, 1203 insertions, 398 deletions
diff --git a/3rdparty/bgfx/src/renderer_mtl.mm b/3rdparty/bgfx/src/renderer_mtl.mm index 5de80bd577d..b2dcdd36900 100644 --- a/3rdparty/bgfx/src/renderer_mtl.mm +++ b/3rdparty/bgfx/src/renderer_mtl.mm @@ -1,5 +1,5 @@ /* - * Copyright 2011-2015 Attila Kocsis. All rights reserved. + * Copyright 2011-2016 Attila Kocsis. All rights reserved. * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause */ @@ -17,36 +17,57 @@ #import <Foundation/Foundation.h> -#define UNIFORM_BUFFER_SIZE (1024*1024) -#define UNIFORM_BUFFER_COUNT (3) +#define UNIFORM_BUFFER_SIZE (8*1024*1024) /* -Known issues / TODOs: -- 15-shadowmaps-simple (modified shaderc and example needs modification too, mtxCrop znew = z * 0.5 + 0.5 is not needed ) could be hacked in shader too -- 19-oit ( hacked shaderc to support MRT output) -- 21-deferred ( hacked shaderc to support MRT output and fs_deferred_light needed modification for metal (similar to BGFX_SHADER_LANGUAGE_HLSL ) - -07-callback, saveScreenshot should be implemented with one frame latency (using saveScreenshotBegin and End) - -16-shadowmaps, //problem with essl -> metal: SAMPLER2D(u_shadowMap0, 4); sampler index is lost. Shadowmap is set to slot 4, but - metal shader uses sampler/texture slot 0. this could require changes outside of renderer_mtl? - Otherwise it works with hacking the slot. - -24-nbody - cannot generate compute shaders for metal - -20-nanonvg - TODO: remove sampler/texture hack - -- caps - -- optimization... - -create binary shader representation - - 13-stencil and 16-shadowmaps are very inefficient. every view stores/loads backbuffer data - - BGFX_RESET_FLIP_AFTER_RENDER on low level renderers should be true? (crashes even with BGFX_RESET_FLIP_AFTER_RENDER because there is - one rendering frame before reset). Do I have absolutely need to send result to View at flip or can I do it in submit? - + // known metal shader generation issues: + 03-raymarch: OSX10.11.3 nothing is visible ( depth/color swap in fragment output struct fixed this ) + 14-shadowvolumes: in texture as stencil mode - columns/bunny are dark. in fs_shadowvolume_color_lighting SAMPLER2D(s_texStencil, 1) is + converted to "texture2d<float> s_texStencil [[texture(0)]], sampler _mtlsmp_s_texStencil [[sampler(0)]]". Slot is 1 -> 0. + 15-shadowmaps-simple: shader compilation error + 16-shadowmaps: //problem with essl -> metal: SAMPLER2D(u_shadowMap0, 4); sampler index is lost. Shadowmap is set to slot 4, but + metal shader uses sampler/texture slot 0. this could require changes outside of renderer_mtl? + packFloatToRGBA needs highp. currently it uses half. + 24-nbody: no generated compute shaders for metal + 27-terrain: shaderc generates invalid metal shader for vs_terrain_height_texture. vertex output: half4 gl_Position [[position]], should be float4 + 31-rsm: + <program source>:6:23: error: type 'half4' (aka 'vector_half4') is not valid for attribute 'position' + half4 gl_Position [[position]]; + + +Known issues(driver problems??): + OSX mac mini(late 2014), OSX10.11.3 : nanovg-rendering: color writemask off causes problem... + 03-raymarch: OSX nothing is visible ( depth/color order should be swapped in fragment output struct) + works fine with newer OSX + iPad mini 2, iOS 8.1.1: 21-deferred: scissor not working properly + 26-occlusion: query doesn't work with two rendercommandencoders + Only on this device ( no problem on iPad Air 2 with iOS9.3.1) + + TODOs: + - remove sync points at mesh update. clearquad: 13-stencil, 26-occlusion, 30-picking + - framebufferMtl and TextureMtl resolve + + - FrameBufferMtl::postReset recreate framebuffer??? + + renderpass load/resolve + - capture with msaa: 07-callback + - implement fb discard. problematic with multiple views that has same fb... + - msaa color/depth/stencil is not saved. could have problem when we switch back to msaa framebuffer + - refactor store/load actions to support msaa/discard/capture/readback etc... + + - finish savescreenshot with screenshotbegin/end + + - support multiple windows: 22-windows + - multithreading with multiple commandbuffer + + - compute and drawindirect: 24-nbody (needs compute shaders) + + INFO: + - 15-shadowmaps-simple (example needs modification mtxCrop znew = z * 0.5 + 0.5 is not needed ) could be hacked in shader too + + ASK: + BGFX_RESET_FLIP_AFTER_RENDER on low level renderers should be true? + Do I have absolutely need to send result to screen at flip or can I do it in submit? */ namespace bgfx { namespace mtl @@ -122,12 +143,12 @@ namespace bgfx { namespace mtl }, //Uint10 - //TODO: normalized only + //Note: unnormalized is handled as normalized now { - { MTLVertexFormatInvalid, MTLVertexFormatUInt1010102Normalized }, - { MTLVertexFormatInvalid, MTLVertexFormatUInt1010102Normalized }, - { MTLVertexFormatInvalid, MTLVertexFormatUInt1010102Normalized }, - { MTLVertexFormatInvalid, MTLVertexFormatUInt1010102Normalized } + { MTLVertexFormatUInt1010102Normalized, MTLVertexFormatUInt1010102Normalized }, + { MTLVertexFormatUInt1010102Normalized, MTLVertexFormatUInt1010102Normalized }, + { MTLVertexFormatUInt1010102Normalized, MTLVertexFormatUInt1010102Normalized }, + { MTLVertexFormatUInt1010102Normalized, MTLVertexFormatUInt1010102Normalized } }, //Int16 @@ -142,8 +163,8 @@ namespace bgfx { namespace mtl { { MTLVertexFormatHalf2, MTLVertexFormatHalf2 }, { MTLVertexFormatHalf2, MTLVertexFormatHalf2 }, - { MTLVertexFormatHalf3, MTLVertexFormatHalf2 }, - { MTLVertexFormatHalf4, MTLVertexFormatHalf2 } + { MTLVertexFormatHalf3, MTLVertexFormatHalf3 }, + { MTLVertexFormatHalf4, MTLVertexFormatHalf4 } }, //Float @@ -193,7 +214,7 @@ namespace bgfx { namespace mtl static const MTLCompareFunction s_cmpFunc[] = { - MTLCompareFunctionAlways, //TODO: depth disable? + MTLCompareFunctionAlways, MTLCompareFunctionLess, MTLCompareFunctionLessEqual, MTLCompareFunctionEqual, @@ -245,81 +266,87 @@ namespace bgfx { namespace mtl static TextureFormatInfo s_textureFormat[] = { - { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // BC1 - { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // BC2 - { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // BC3 - { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // BC4 - { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // BC5 - { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // BC6H - { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // BC7 - { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // ETC1 - { MTLPixelFormat(180) /*ETC2_RGB8*/, MTLPixelFormat(181) /*ETC2_RGB8_sRGB*/ }, // ETC2 - { MTLPixelFormat(178) /*EAC_RGBA8*/, MTLPixelFormat(179) /*EAC_RGBA8_sRGB*/ }, // ETC2A - { MTLPixelFormat(182) /*ETC2_RGB8A1*/, MTLPixelFormat(183) /*ETC2_RGB8A1_sRGB*/ }, // ETC2A1 - { MTLPixelFormat(160) /*PVRTC_RGB_2BPP*/, MTLPixelFormat(161) /*PVRTC_RGB_2BPP_sRGB*/ }, // PTC12 - { MTLPixelFormat(162) /*PVRTC_RGB_4BPP*/, MTLPixelFormat(163) /*PVRTC_RGB_4BPP_sRGB*/ }, // PTC14 - { MTLPixelFormat(164) /*PVRTC_RGBA_2BPP*/, MTLPixelFormat(165) /*PVRTC_RGBA_2BPP_sRGB*/ }, // PTC12A - { MTLPixelFormat(166) /*PVRTC_RGBA_4BPP*/, MTLPixelFormat(167) /*PVRTC_RGBA_4BPP_sRGB*/ }, // PTC14A - { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // PTC22 - { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // PTC24 - { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // Unknown - { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // R1 - { MTLPixelFormatA8Unorm, MTLPixelFormatInvalid }, // A8 - { MTLPixelFormatR8Unorm, MTLPixelFormat(11) /*R8Unorm_sRGB*/ }, // R8 - { MTLPixelFormatR8Sint, MTLPixelFormatInvalid }, // R8I - { MTLPixelFormatR8Uint, MTLPixelFormatInvalid }, // R8U - { MTLPixelFormatR8Snorm, MTLPixelFormatInvalid }, // R8S - { MTLPixelFormatR16Unorm, MTLPixelFormatInvalid }, // R16 - { MTLPixelFormatR16Sint, MTLPixelFormatInvalid }, // R16I - { MTLPixelFormatR16Uint, MTLPixelFormatInvalid }, // R16U - { MTLPixelFormatR16Float, MTLPixelFormatInvalid }, // R16F - { MTLPixelFormatR16Snorm, MTLPixelFormatInvalid }, // R16S - { MTLPixelFormatR32Sint, MTLPixelFormatInvalid }, // R32I - { MTLPixelFormatR32Uint, MTLPixelFormatInvalid }, // R32U - { MTLPixelFormatR32Float, MTLPixelFormatInvalid }, // R32F - { MTLPixelFormatRG8Unorm, MTLPixelFormat(31) /*RG8Unorm_sRGB*/ }, // RG8 - { MTLPixelFormatRG8Sint, MTLPixelFormatInvalid }, // RG8I - { MTLPixelFormatRG8Uint, MTLPixelFormatInvalid }, // RG8U - { MTLPixelFormatRG8Snorm, MTLPixelFormatInvalid }, // RG8S - { MTLPixelFormatRG16Unorm, MTLPixelFormatInvalid }, // RG16 - { MTLPixelFormatRG16Sint, MTLPixelFormatInvalid }, // RG16I - { MTLPixelFormatRG16Uint, MTLPixelFormatInvalid }, // RG16U - { MTLPixelFormatRG16Float, MTLPixelFormatInvalid }, // RG16F - { MTLPixelFormatRG16Snorm, MTLPixelFormatInvalid }, // RG16S - { MTLPixelFormatRG32Sint, MTLPixelFormatInvalid }, // RG32I - { MTLPixelFormatRG32Uint, MTLPixelFormatInvalid }, // RG32U - { MTLPixelFormatRG32Float, MTLPixelFormatInvalid }, // RG32F - { MTLPixelFormatRGB9E5Float, MTLPixelFormatInvalid }, // RGB9E5F - { MTLPixelFormatBGRA8Unorm, MTLPixelFormatBGRA8Unorm_sRGB }, // BGRA8 - { MTLPixelFormatRGBA8Unorm, MTLPixelFormatRGBA8Unorm_sRGB }, // RGBA8 - { MTLPixelFormatRGBA8Sint, MTLPixelFormatInvalid }, // RGBA8I - { MTLPixelFormatRGBA8Uint, MTLPixelFormatInvalid }, // RGBA8U - { MTLPixelFormatRGBA8Snorm, MTLPixelFormatInvalid }, // RGBA8S - { MTLPixelFormatRGBA16Unorm, MTLPixelFormatInvalid }, // RGBA16 - { MTLPixelFormatRGBA16Sint, MTLPixelFormatInvalid }, // RGBA16I - { MTLPixelFormatRGBA16Uint, MTLPixelFormatInvalid }, // RGBA16I - { MTLPixelFormatRGBA16Float, MTLPixelFormatInvalid }, // RGBA16F - { MTLPixelFormatRGBA16Snorm, MTLPixelFormatInvalid }, // RGBA16S - { MTLPixelFormatRGBA32Sint, MTLPixelFormatInvalid }, // RGBA32I - { MTLPixelFormatRGBA32Uint, MTLPixelFormatInvalid }, // RGBA32U - { MTLPixelFormatRGBA32Float, MTLPixelFormatInvalid }, // RGBA32F - { MTLPixelFormat(40) /*B5G6R5Unorm*/, MTLPixelFormatInvalid }, // R5G6B5 - { MTLPixelFormat(42) /*ABGR4Unorm*/, MTLPixelFormatInvalid }, // RGBA4 - { MTLPixelFormat(41) /*A1BGR5Unorm*/, MTLPixelFormatInvalid }, // RGB5A1 - { MTLPixelFormatRGB10A2Unorm, MTLPixelFormatInvalid }, // RGB10A2 - { MTLPixelFormatRG11B10Float, MTLPixelFormatInvalid }, // R11G11B10F - { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // UnknownDepth - { MTLPixelFormatDepth32Float, MTLPixelFormatInvalid }, // D16 - { MTLPixelFormatDepth32Float, MTLPixelFormatInvalid }, // D24 - { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // D24S8 - { MTLPixelFormatDepth32Float, MTLPixelFormatInvalid }, // D32 - { MTLPixelFormatDepth32Float, MTLPixelFormatInvalid }, // D16F - { MTLPixelFormatDepth32Float, MTLPixelFormatInvalid }, // D24F - { MTLPixelFormatDepth32Float, MTLPixelFormatInvalid }, // D32F - { MTLPixelFormatStencil8, MTLPixelFormatInvalid }, // D0S8 + { MTLPixelFormat(130) /*BC1_RGBA*/, MTLPixelFormat(131) /*BC1_RGBA_sRGB*/ }, // BC1 + { MTLPixelFormat(132) /*BC2_RGBA*/, MTLPixelFormat(133) /*BC2_RGBA_sRGB*/ }, // BC2 + { MTLPixelFormat(134) /*BC3_RGBA*/, MTLPixelFormat(135) /*BC3_RGBA_sRGB*/ }, // BC3 + { MTLPixelFormat(140) /*BC4_RUnorm*/, MTLPixelFormatInvalid }, // BC4 + { MTLPixelFormat(142) /*BC5_RGUnorm*/, MTLPixelFormatInvalid }, // BC5 + { MTLPixelFormat(150) /*BC6H_RGBFloat*/, MTLPixelFormatInvalid }, // BC6H + { MTLPixelFormat(152) /*BC7_RGBAUnorm*/, MTLPixelFormat(153) /*BC7_RGBAUnorm_sRGB*/ }, // BC7 + { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // ETC1 + { MTLPixelFormat(180) /*ETC2_RGB8*/, MTLPixelFormat(181) /*ETC2_RGB8_sRGB*/ }, // ETC2 + { MTLPixelFormat(178) /*EAC_RGBA8*/, MTLPixelFormat(179) /*EAC_RGBA8_sRGB*/ }, // ETC2A + { MTLPixelFormat(182) /*ETC2_RGB8A1*/, MTLPixelFormat(183) /*ETC2_RGB8A1_sRGB*/ }, // ETC2A1 + { MTLPixelFormat(160) /*PVRTC_RGB_2BPP*/, MTLPixelFormat(161) /*PVRTC_RGB_2BPP_sRGB*/ }, // PTC12 + { MTLPixelFormat(162) /*PVRTC_RGB_4BPP*/, MTLPixelFormat(163) /*PVRTC_RGB_4BPP_sRGB*/ }, // PTC14 + { MTLPixelFormat(164) /*PVRTC_RGBA_2BPP*/, MTLPixelFormat(165) /*PVRTC_RGBA_2BPP_sRGB*/ }, // PTC12A + { MTLPixelFormat(166) /*PVRTC_RGBA_4BPP*/, MTLPixelFormat(167) /*PVRTC_RGBA_4BPP_sRGB*/ }, // PTC14A + { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // PTC22 + { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // PTC24 + { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // Unknown + { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // R1 + { MTLPixelFormatA8Unorm, MTLPixelFormatInvalid }, // A8 + { MTLPixelFormatR8Unorm, MTLPixelFormat(11) /*R8Unorm_sRGB*/ }, // R8 + { MTLPixelFormatR8Sint, MTLPixelFormatInvalid }, // R8I + { MTLPixelFormatR8Uint, MTLPixelFormatInvalid }, // R8U + { MTLPixelFormatR8Snorm, MTLPixelFormatInvalid }, // R8S + { MTLPixelFormatR16Unorm, MTLPixelFormatInvalid }, // R16 + { MTLPixelFormatR16Sint, MTLPixelFormatInvalid }, // R16I + { MTLPixelFormatR16Uint, MTLPixelFormatInvalid }, // R16U + { MTLPixelFormatR16Float, MTLPixelFormatInvalid }, // R16F + { MTLPixelFormatR16Snorm, MTLPixelFormatInvalid }, // R16S + { MTLPixelFormatR32Sint, MTLPixelFormatInvalid }, // R32I + { MTLPixelFormatR32Uint, MTLPixelFormatInvalid }, // R32U + { MTLPixelFormatR32Float, MTLPixelFormatInvalid }, // R32F + { MTLPixelFormatRG8Unorm, MTLPixelFormat(31) /*RG8Unorm_sRGB*/ }, // RG8 + { MTLPixelFormatRG8Sint, MTLPixelFormatInvalid }, // RG8I + { MTLPixelFormatRG8Uint, MTLPixelFormatInvalid }, // RG8U + { MTLPixelFormatRG8Snorm, MTLPixelFormatInvalid }, // RG8S + { MTLPixelFormatRG16Unorm, MTLPixelFormatInvalid }, // RG16 + { MTLPixelFormatRG16Sint, MTLPixelFormatInvalid }, // RG16I + { MTLPixelFormatRG16Uint, MTLPixelFormatInvalid }, // RG16U + { MTLPixelFormatRG16Float, MTLPixelFormatInvalid }, // RG16F + { MTLPixelFormatRG16Snorm, MTLPixelFormatInvalid }, // RG16S + { MTLPixelFormatRG32Sint, MTLPixelFormatInvalid }, // RG32I + { MTLPixelFormatRG32Uint, MTLPixelFormatInvalid }, // RG32U + { MTLPixelFormatRG32Float, MTLPixelFormatInvalid }, // RG32F + { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // RGB8 + { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // RGB8I + { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // RGB8U + { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // RGB8S + { MTLPixelFormatRGB9E5Float, MTLPixelFormatInvalid }, // RGB9E5F + { MTLPixelFormatBGRA8Unorm, MTLPixelFormatBGRA8Unorm_sRGB }, // BGRA8 + { MTLPixelFormatRGBA8Unorm, MTLPixelFormatRGBA8Unorm_sRGB }, // RGBA8 + { MTLPixelFormatRGBA8Sint, MTLPixelFormatInvalid }, // RGBA8I + { MTLPixelFormatRGBA8Uint, MTLPixelFormatInvalid }, // RGBA8U + { MTLPixelFormatRGBA8Snorm, MTLPixelFormatInvalid }, // RGBA8S + { MTLPixelFormatRGBA16Unorm, MTLPixelFormatInvalid }, // RGBA16 + { MTLPixelFormatRGBA16Sint, MTLPixelFormatInvalid }, // RGBA16I + { MTLPixelFormatRGBA16Uint, MTLPixelFormatInvalid }, // RGBA16U + { MTLPixelFormatRGBA16Float, MTLPixelFormatInvalid }, // RGBA16F + { MTLPixelFormatRGBA16Snorm, MTLPixelFormatInvalid }, // RGBA16S + { MTLPixelFormatRGBA32Sint, MTLPixelFormatInvalid }, // RGBA32I + { MTLPixelFormatRGBA32Uint, MTLPixelFormatInvalid }, // RGBA32U + { MTLPixelFormatRGBA32Float, MTLPixelFormatInvalid }, // RGBA32F + { MTLPixelFormat(40) /*B5G6R5Unorm*/, MTLPixelFormatInvalid }, // R5G6B5 + { MTLPixelFormat(42) /*ABGR4Unorm*/, MTLPixelFormatInvalid }, // RGBA4 + { MTLPixelFormat(41) /*A1BGR5Unorm*/, MTLPixelFormatInvalid }, // RGB5A1 + { MTLPixelFormatRGB10A2Unorm, MTLPixelFormatInvalid }, // RGB10A2 + { MTLPixelFormatRG11B10Float, MTLPixelFormatInvalid }, // R11G11B10F + { MTLPixelFormatInvalid, MTLPixelFormatInvalid }, // UnknownDepth + { MTLPixelFormatDepth32Float, MTLPixelFormatInvalid }, // D16 + { MTLPixelFormatDepth32Float, MTLPixelFormatInvalid }, // D24 + { MTLPixelFormat(255) /*Depth24Unorm_Stencil8*/,MTLPixelFormatInvalid }, // D24S8 + { MTLPixelFormatDepth32Float, MTLPixelFormatInvalid }, // D32 + { MTLPixelFormatDepth32Float, MTLPixelFormatInvalid }, // D16F + { MTLPixelFormatDepth32Float, MTLPixelFormatInvalid }, // D24F + { MTLPixelFormatDepth32Float, MTLPixelFormatInvalid }, // D32F + { MTLPixelFormatStencil8, MTLPixelFormatInvalid }, // D0S8 }; BX_STATIC_ASSERT(TextureFormat::Count == BX_COUNTOF(s_textureFormat) ); + int s_msaa[5] = { 1,2,4,8,16 }; + #define SHADER_FUNCTION_NAME ("xlatMtlMain") #define SHADER_UNIFORM_NAME ("_mtl_u") @@ -329,10 +356,13 @@ namespace bgfx { namespace mtl : m_metalLayer(NULL) , m_backBufferPixelFormatHash(0) , m_maxAnisotropy(1) - , m_uniformBufferIndex(0) + , m_bufferIndex(0) , m_numWindows(1) , m_rtMsaa(false) + , m_capture(NULL) + , m_captureSize(0) , m_drawable(NULL) + , m_saveScreenshot(false) { } @@ -400,52 +430,107 @@ namespace bgfx { namespace mtl m_textureDescriptor = newTextureDescriptor(); m_samplerDescriptor = newSamplerDescriptor(); - for (uint8_t i=0; i < UNIFORM_BUFFER_COUNT; ++i) + m_framesSemaphore.post(MTL_MAX_FRAMES_IN_FLIGHT); + for (uint8_t i=0; i < MTL_MAX_FRAMES_IN_FLIGHT; ++i) { m_uniformBuffers[i] = m_device.newBufferWithLength(UNIFORM_BUFFER_SIZE, 0); } m_uniformBufferVertexOffset = 0; m_uniformBufferFragmentOffset = 0; + const char* vshSource = + "using namespace metal;\n" + "struct xlatMtlShaderOutput { float4 gl_Position [[position]]; float2 v_texcoord0; }; \n" + "vertex xlatMtlShaderOutput xlatMtlMain (uint v_id [[ vertex_id ]]) \n" + "{\n" + " xlatMtlShaderOutput _mtl_o;\n" + " if (v_id==0) { _mtl_o.gl_Position = float4(-1.0,-1.0,0.0,1.0); _mtl_o.v_texcoord0 = float2(0.0,1.0); } \n" + " else if (v_id==1) { _mtl_o.gl_Position = float4(3.0,-1.0,0.0,1.0); _mtl_o.v_texcoord0 = float2(2.0,1.0); } \n" + " else { _mtl_o.gl_Position = float4(-1.0,3.0,0.0,1.0); _mtl_o.v_texcoord0 = float2(0.0,-1.0); }\n" + " return _mtl_o;\n" + "}\n"; + + const char* fshSource = "using namespace metal; \n" + " struct xlatMtlShaderInput { float2 v_texcoord0; }; \n" + " fragment half4 xlatMtlMain (xlatMtlShaderInput _mtl_i[[stage_in]], texture2d<float> s_texColor [[texture(0)]], sampler _mtlsmp_s_texColor [[sampler(0)]] ) \n" + " { return half4(s_texColor.sample(_mtlsmp_s_texColor, _mtl_i.v_texcoord0)); } \n"; + + Library lib = m_device.newLibraryWithSource(vshSource); + if (NULL != lib) + { + m_screenshotBlitProgramVsh.m_function = lib.newFunctionWithName(SHADER_FUNCTION_NAME); + } + lib = m_device.newLibraryWithSource(fshSource); + if (NULL != lib) + { + m_screenshotBlitProgramFsh.m_function = lib.newFunctionWithName(SHADER_FUNCTION_NAME); + } + m_screenshotBlitProgram.create(&m_screenshotBlitProgramVsh, &m_screenshotBlitProgramFsh); + + reset(m_renderPipelineDescriptor); + m_renderPipelineDescriptor.colorAttachments[0].pixelFormat = m_metalLayer.pixelFormat; + m_renderPipelineDescriptor.vertexFunction = m_screenshotBlitProgram.m_vsh->m_function; + m_renderPipelineDescriptor.fragmentFunction = m_screenshotBlitProgram.m_fsh->m_function; + m_screenshotBlitRenderPipelineState = m_device.newRenderPipelineStateWithDescriptor(m_renderPipelineDescriptor); + g_caps.supported |= (0 + | BGFX_CAPS_TEXTURE_COMPARE_LEQUAL //NOTE: on IOS Gpu Family 1/2 have to set compare in shader + | BGFX_CAPS_TEXTURE_COMPARE_ALL | BGFX_CAPS_TEXTURE_3D - | BGFX_CAPS_TEXTURE_COMPARE_LEQUAL - | BGFX_CAPS_INSTANCING | BGFX_CAPS_VERTEX_ATTRIB_HALF -// | BGFX_CAPS_FRAGMENT_DEPTH + | BGFX_CAPS_VERTEX_ATTRIB_UINT10 + | BGFX_CAPS_INSTANCING + | BGFX_CAPS_FRAGMENT_DEPTH | BGFX_CAPS_BLEND_INDEPENDENT - | BGFX_CAPS_COMPUTE + //| BGFX_CAPS_COMPUTE // TODO: api/hw supports it but metal compute shaders are not yet supported | BGFX_CAPS_INDEX32 - | BGFX_CAPS_DRAW_INDIRECT -// | BGFX_CAPS_TEXTURE_BLIT -// | BGFX_CAPS_TEXTURE_READ_BACK - | BGFX_CAPS_OCCLUSION_QUERY + //| BGFX_CAPS_DRAW_INDIRECT // TODO: support on iOS9+gpu family3+ and on macOS + | BGFX_CAPS_TEXTURE_BLIT + | BGFX_CAPS_TEXTURE_READ_BACK + | BGFX_CAPS_OCCLUSION_QUERY + | BGFX_CAPS_ALPHA_TO_COVERAGE ); - g_caps.maxTextureSize = 2048; //ASK: real caps width/height: 4096, but max depth(3D) size is only: 2048 - g_caps.maxFBAttachments = 4; // uint8_t(bx::uint32_min(m_device.supportsFeatureSet(MTLFeatureSet_iOS_GPUFamily2_v1) ? 8 : 4, BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS)); + if (BX_ENABLED(BX_PLATFORM_IOS) ) + { + if ( iOSVersionEqualOrGreater("9.0.0") ) + { + g_caps.maxTextureSize = m_device.supportsFeatureSet((MTLFeatureSet)4 /* iOS_GPUFamily3_v1 */) ? 16384 : 8192; + } + else + { + g_caps.maxTextureSize = 4096; + } + g_caps.maxFBAttachments = uint8_t(bx::uint32_min(m_device.supportsFeatureSet((MTLFeatureSet)1 /* MTLFeatureSet_iOS_GPUFamily2_v1 */) ? 8 : 4, BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS)); + + } else if (BX_ENABLED(BX_PLATFORM_OSX) ) + { + g_caps.maxTextureSize = 16384; + g_caps.maxFBAttachments = 8; + } //todo: vendor id, device id, gpu enum - //todo: texture format caps - //add texture formats/caps/etc that are available only on new sdk/devices -#if BX_PLATFORM_IOS -# ifdef __IPHONE_8_0 - if (OsVersionEqualOrGreater("8.0.0")) + m_hasPixelFormatDepth32Float_Stencil8 = (BX_ENABLED(BX_PLATFORM_OSX) || + ( BX_ENABLED(BX_PLATFORM_IOS) && iOSVersionEqualOrGreater("9.0.0") ) ); + m_macOS11Runtime = (BX_ENABLED(BX_PLATFORM_OSX) && macOSVersionEqualOrGreater(10,11,0) ); + m_iOS9Runtime = (BX_ENABLED(BX_PLATFORM_IOS) && iOSVersionEqualOrGreater("9.0.0") ); + + if (BX_ENABLED(BX_PLATFORM_OSX) ) { - s_textureFormat[TextureFormat::D24S8].m_fmt = MTLPixelFormatDepth32Float; + s_textureFormat[TextureFormat::R8].m_fmtSrgb = MTLPixelFormatInvalid; + s_textureFormat[TextureFormat::RG8].m_fmtSrgb = MTLPixelFormatInvalid; } -# endif // __IPHONE_8_0 -#endif // BX_PLATFORM_* for (uint32_t ii = 0; ii < TextureFormat::Count; ++ii) { - uint8_t support = 0; + uint16_t support = 0; support |= MTLPixelFormatInvalid != s_textureFormat[ii].m_fmt ? BGFX_CAPS_FORMAT_TEXTURE_2D | BGFX_CAPS_FORMAT_TEXTURE_3D | BGFX_CAPS_FORMAT_TEXTURE_CUBE + | BGFX_CAPS_FORMAT_TEXTURE_VERTEX : BGFX_CAPS_FORMAT_TEXTURE_NONE ; @@ -453,20 +538,51 @@ namespace bgfx { namespace mtl ? BGFX_CAPS_FORMAT_TEXTURE_2D_SRGB | BGFX_CAPS_FORMAT_TEXTURE_3D_SRGB | BGFX_CAPS_FORMAT_TEXTURE_CUBE_SRGB + | BGFX_CAPS_FORMAT_TEXTURE_VERTEX : BGFX_CAPS_FORMAT_TEXTURE_NONE ; + if (!isCompressed((TextureFormat::Enum)(ii))) + { + support |= BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER + | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA; + } + //TODO: additional caps flags -// support |= BGFX_CAPS_FORMAT_TEXTURE_VERTEX : BGFX_CAPS_FORMAT_TEXTURE_NONE; // support |= BGFX_CAPS_FORMAT_TEXTURE_IMAGE : BGFX_CAPS_FORMAT_TEXTURE_NONE; -// support |= BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER : BGFX_CAPS_FORMAT_TEXTURE_NONE; g_caps.formats[ii] = support; } + g_caps.formats[TextureFormat::A8] &= ~(BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA); + g_caps.formats[TextureFormat::RG32I] &= ~(BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA); + g_caps.formats[TextureFormat::RG32U] &= ~(BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA); + g_caps.formats[TextureFormat::RGBA32I] &= ~(BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA); + g_caps.formats[TextureFormat::RGBA32U] &= ~(BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA); + + + if (BX_ENABLED(BX_PLATFORM_IOS) ) + { + s_textureFormat[TextureFormat::D24S8].m_fmt = MTLPixelFormatDepth32Float; + + g_caps.formats[TextureFormat::BC1] = + g_caps.formats[TextureFormat::BC2] = + g_caps.formats[TextureFormat::BC3] = + g_caps.formats[TextureFormat::BC4] = + g_caps.formats[TextureFormat::BC5] = + g_caps.formats[TextureFormat::BC6H] = + g_caps.formats[TextureFormat::BC7] = BGFX_CAPS_FORMAT_TEXTURE_NONE; + + g_caps.formats[TextureFormat::RG32F] &= ~(BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA); + g_caps.formats[TextureFormat::RGBA32F] &= ~(BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA); + } + if (BX_ENABLED(BX_PLATFORM_OSX) ) { - g_caps.formats[TextureFormat::ETC1 ] = + s_textureFormat[TextureFormat::D24S8].m_fmt = (MTLPixelFormat)(m_device.depth24Stencil8PixelFormatSupported() ? + 255 /* Depth24Unorm_Stencil8 */ : + MTLPixelFormatDepth32Float_Stencil8); + g_caps.formats[TextureFormat::ETC2 ] = g_caps.formats[TextureFormat::ETC2A ] = g_caps.formats[TextureFormat::ETC2A1] = @@ -474,8 +590,11 @@ namespace bgfx { namespace mtl g_caps.formats[TextureFormat::PTC14 ] = g_caps.formats[TextureFormat::PTC12A] = g_caps.formats[TextureFormat::PTC14A] = - g_caps.formats[TextureFormat::PTC22 ] = - g_caps.formats[TextureFormat::PTC24 ] = BGFX_CAPS_FORMAT_TEXTURE_NONE; + g_caps.formats[TextureFormat::R5G6B5] = + g_caps.formats[TextureFormat::RGBA4 ] = + g_caps.formats[TextureFormat::RGB5A1] = BGFX_CAPS_FORMAT_TEXTURE_NONE; + + g_caps.formats[TextureFormat::RGB9E5F] &= ~(BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA); } for (uint32_t ii = 0; ii < TextureFormat::Count; ++ii) @@ -487,6 +606,15 @@ namespace bgfx { namespace mtl } } + for(uint32_t ii=1; ii<5; ++ii) + { + if (!m_device.supportsTextureSampleCount(s_msaa[ii])) + { + s_msaa[ii] = s_msaa[ii-1]; + } + } + + // Init reserved part of view name. for (uint32_t ii = 0; ii < BGFX_CONFIG_MAX_VIEWS; ++ii) { @@ -494,6 +622,7 @@ namespace bgfx { namespace mtl } m_occlusionQuery.preReset(); + m_gpuTimer.init(); g_internalData.context = m_device; return true; @@ -502,6 +631,7 @@ namespace bgfx { namespace mtl void shutdown() { m_occlusionQuery.postReset(); + m_gpuTimer.shutdown(); for (uint32_t ii = 0; ii < BX_COUNTOF(m_shaders); ++ii) { @@ -513,6 +643,8 @@ namespace bgfx { namespace mtl m_textures[ii].destroy(); } + captureFinish(); + MTL_RELEASE(m_depthStencilDescriptor); MTL_RELEASE(m_frontFaceStencilDescriptor); MTL_RELEASE(m_backFaceStencilDescriptor); @@ -527,7 +659,7 @@ namespace bgfx { namespace mtl MTL_RELEASE(m_backBufferStencil); } - for (uint8_t i=0; i < UNIFORM_BUFFER_COUNT; ++i) + for (uint8_t i=0; i < MTL_MAX_FRAMES_IN_FLIGHT; ++i) { MTL_RELEASE(m_uniformBuffers[i]); } @@ -645,11 +777,27 @@ namespace bgfx { namespace mtl { } - void readTexture(TextureHandle /*_handle*/, void* /*_data*/) BX_OVERRIDE + void readTexture(TextureHandle _handle, void* _data) BX_OVERRIDE { + m_commandBuffer.commit(); + m_commandBuffer.waitUntilCompleted(); + MTL_RELEASE(m_commandBuffer) + + const TextureMtl& texture = m_textures[_handle.idx]; + + uint32_t width = texture.m_ptr.width(); + uint32_t height = texture.m_ptr.height(); + const uint8_t bpp = getBitsPerPixel(TextureFormat::Enum(texture.m_textureFormat) ); + + MTLRegion region = { { 0, 0, 0 }, { width, height, 1 } }; + + texture.m_ptr.getBytes(_data, width*bpp/8, 0, region, 0, 0); + + m_commandBuffer = m_commandQueue.commandBuffer(); + retain(m_commandBuffer); //NOTE: keep alive to be useable at 'flip' } - void resizeTexture(TextureHandle _handle, uint16_t _width, uint16_t _height) BX_OVERRIDE + void resizeTexture(TextureHandle _handle, uint16_t _width, uint16_t _height, uint8_t _numMips) BX_OVERRIDE { TextureMtl& texture = m_textures[_handle.idx]; @@ -665,7 +813,7 @@ namespace bgfx { namespace mtl tc.m_height = _height; tc.m_sides = 0; tc.m_depth = 0; - tc.m_numMips = 1; + tc.m_numMips = _numMips; tc.m_format = TextureFormat::Enum(texture.m_requestedFormat); tc.m_cubeMap = false; tc.m_mem = NULL; @@ -740,30 +888,35 @@ namespace bgfx { namespace mtl m_uniforms[_handle.idx] = NULL; } + //cmdPre + void saveScreenShotPre(const char* _filePath) BX_OVERRIDE + { + BX_UNUSED(_filePath); + m_saveScreenshot = true; + } + + //cmdPost void saveScreenShot(const char* _filePath) BX_OVERRIDE { - if (NULL == m_drawable - || NULL == m_drawable.texture) - { + if (NULL == m_screenshotTarget) return; - } - //TODO: we should wait for completion of pending commandBuffers - //TODO: implement this with saveScreenshotBegin/End + m_commandBuffer.commit(); + m_commandBuffer.waitUntilCompleted(); + MTL_RELEASE(m_commandBuffer) - Texture backBuffer = m_drawable.texture; - uint32_t width = backBuffer.width(); - uint32_t height = backBuffer.height(); + uint32_t width = m_screenshotTarget.width(); + uint32_t height = m_screenshotTarget.height(); uint32_t length = width*height*4; uint8_t* data = (uint8_t*)BX_ALLOC(g_allocator, length); MTLRegion region = { { 0, 0, 0 }, { width, height, 1 } }; - backBuffer.getBytes(data, 4*width, 0, region, 0, 0); + m_screenshotTarget.getBytes(data, 4*width, 0, region, 0, 0); g_callback->screenShot(_filePath - , backBuffer.width() - , backBuffer.height() + , m_screenshotTarget.width() + , m_screenshotTarget.height() , width*4 , data , length @@ -771,6 +924,9 @@ namespace bgfx { namespace mtl ); BX_FREE(g_allocator, data); + + m_commandBuffer = m_commandQueue.commandBuffer(); + retain(m_commandBuffer); //NOTE: keep alive to be useable at 'flip' } void updateViewName(uint8_t _id, const char* _name) BX_OVERRIDE @@ -812,8 +968,25 @@ namespace bgfx { namespace mtl //} FrameBufferHandle fbh = BGFX_INVALID_HANDLE; - //TODO: change to default framebuffer - we need a new encoder for this! - //setFrameBuffer(fbh, false); + + if ( NULL == rce || m_renderCommandEncoderFrameBufferHandle.idx != invalidHandle ) + { + if ( m_renderCommandEncoder ) + m_renderCommandEncoder.endEncoding(); + + RenderPassDescriptor renderPassDescriptor = newRenderPassDescriptor(); + + setFrameBuffer(renderPassDescriptor, fbh); + + renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionLoad; + renderPassDescriptor.colorAttachments[0].storeAction = NULL != renderPassDescriptor.colorAttachments[0].resolveTexture ? + MTLStoreActionMultisampleResolve : MTLStoreActionStore; + + rce = m_commandBuffer.renderCommandEncoderWithDescriptor(renderPassDescriptor); + m_renderCommandEncoder = rce; + m_renderCommandEncoderFrameBufferHandle = fbh; + MTL_RELEASE(renderPassDescriptor); + } MTLViewport viewport = { 0.0f, 0.0f, (float)width, (float)height, 0.0f, 1.0f}; rce.setViewport(viewport); @@ -849,9 +1022,6 @@ namespace bgfx { namespace mtl rce.setFragmentBuffer(m_uniformBuffer, m_uniformBufferFragmentOffset, 0); } - VertexBufferMtl& vb = m_vertexBuffers[_blitter.m_vb->handle.idx]; - rce.setVertexBuffer(vb.m_buffer, 0, 1); - float proj[16]; bx::mtxOrtho(proj, 0.0f, (float)width, (float)height, 0.0f, 0.0f, 1000.0f); @@ -859,7 +1029,7 @@ namespace bgfx { namespace mtl uint8_t flags = predefined.m_type; setShaderUniform(flags, predefined.m_loc, proj, 4); - m_textures[_blitter.m_texture.idx].commit(0); + m_textures[_blitter.m_texture.idx].commit(0, false, true); } void blitRender(TextVideoMemBlitter& _blitter, uint32_t _numIndices) BX_OVERRIDE @@ -867,30 +1037,44 @@ namespace bgfx { namespace mtl const uint32_t numVertices = _numIndices*4/6; if (0 < numVertices) { - m_indexBuffers [_blitter.m_ib->handle.idx].update(0, _numIndices*2, _blitter.m_ib->data); + m_indexBuffers [_blitter.m_ib->handle.idx].update(0, _numIndices*2, _blitter.m_ib->data, true); m_vertexBuffers[_blitter.m_vb->handle.idx].update(0, numVertices*_blitter.m_decl.m_stride, _blitter.m_vb->data, true); - m_renderCommandEncoder.drawIndexedPrimitives(MTLPrimitiveTypeTriangle, _numIndices, MTLIndexTypeUInt16, m_indexBuffers[_blitter.m_ib->handle.idx].m_buffer, 0, 1); + VertexBufferMtl& vb = m_vertexBuffers[_blitter.m_vb->handle.idx]; + m_renderCommandEncoder.setVertexBuffer(vb.getBuffer(), 0, 1); + + m_renderCommandEncoder.drawIndexedPrimitives(MTLPrimitiveTypeTriangle, _numIndices, MTLIndexTypeUInt16, m_indexBuffers[_blitter.m_ib->handle.idx].getBuffer(), 0, 1); } } + static void commandBufferFinishedCallback(void* _data) + { + RendererContextMtl* renderer = (RendererContextMtl*)_data; + if ( renderer ) + renderer->m_framesSemaphore.post(); + } + void flip(HMD& /*_hmd*/) BX_OVERRIDE { - if (NULL == m_drawable - || NULL == m_commandBuffer) + if (NULL == m_commandBuffer) { return; } // Present and commit the command buffer - m_commandBuffer.presentDrawable(m_drawable); - MTL_RELEASE(m_drawable); + if ( NULL != m_drawable) + { + m_commandBuffer.presentDrawable(m_drawable); + MTL_RELEASE(m_drawable); + } + + m_commandBuffer.addCompletedHandler(commandBufferFinishedCallback, this); m_commandBuffer.commit(); - // using heavy syncing now - // TODO: refactor it with double/triple buffering frame data - m_commandBuffer.waitUntilCompleted(); + MTL_RELEASE(m_prevCommandBuffer); + m_prevCommandBuffer = m_commandBuffer; + retain(m_prevCommandBuffer); MTL_RELEASE(m_commandBuffer); @@ -918,55 +1102,83 @@ namespace bgfx { namespace mtl : 1 ; - //TODO: _resolution has wrong dimensions, using m_drawable.texture size now - if (NULL == m_drawable.texture) + //TODO: there should be a way to specify if backbuffer needs stencil/depth. + const uint32_t maskFlags = ~(0 + | BGFX_RESET_HMD_RECENTER + | BGFX_RESET_MAXANISOTROPY + | BGFX_RESET_DEPTH_CLAMP + | BGFX_RESET_SUSPEND + ); + + if (m_resolution.m_width != _resolution.m_width + || m_resolution.m_height != _resolution.m_height + || (m_resolution.m_flags&maskFlags) != (_resolution.m_flags&maskFlags) ) { - return; - } + int sampleCount = s_msaa[(_resolution.m_flags&BGFX_RESET_MSAA_MASK)>>BGFX_RESET_MSAA_SHIFT]; - uint32_t width = (uint32_t)m_drawable.texture.width; - uint32_t height = (uint32_t)m_drawable.texture.height; + MTLPixelFormat prevMetalLayerPixelFormat = m_metalLayer.pixelFormat; + + m_metalLayer.drawableSize = CGSizeMake(_resolution.m_width, _resolution.m_height); + m_metalLayer.pixelFormat = (m_resolution.m_flags & BGFX_RESET_SRGB_BACKBUFFER) + ? MTLPixelFormatBGRA8Unorm_sRGB + : MTLPixelFormatBGRA8Unorm + ; - //TODO: there should be a way to specify if backbuffer needs stencil/depth. - //TODO: support msaa - if (NULL == m_backBufferDepth - || width != m_backBufferDepth.width() - || height != m_backBufferDepth.height() - || m_resolution.m_width != _resolution.m_width - || m_resolution.m_height != _resolution.m_height - || m_resolution.m_flags != _resolution.m_flags) - { m_resolution = _resolution; m_resolution.m_flags &= ~BGFX_RESET_INTERNAL_FORCE; - m_textureDescriptor.textureType = MTLTextureType2D; + m_textureDescriptor.textureType = sampleCount > 1 ? MTLTextureType2DMultisample : MTLTextureType2D; - m_textureDescriptor.pixelFormat = MTLPixelFormatDepth32Float_Stencil8; + if (m_hasPixelFormatDepth32Float_Stencil8) + m_textureDescriptor.pixelFormat = MTLPixelFormatDepth32Float_Stencil8; + else + m_textureDescriptor.pixelFormat = MTLPixelFormatDepth32Float; - m_textureDescriptor.width = width; - m_textureDescriptor.height = height; + m_textureDescriptor.width = _resolution.m_width; + m_textureDescriptor.height = _resolution.m_height; m_textureDescriptor.depth = 1; m_textureDescriptor.mipmapLevelCount = 1; - m_textureDescriptor.sampleCount = 1; + m_textureDescriptor.sampleCount = sampleCount; m_textureDescriptor.arrayLength = 1; - m_textureDescriptor.resourceOptions = MTLResourceStorageModePrivate; - m_textureDescriptor.cpuCacheMode = MTLCPUCacheModeDefaultCache; - m_textureDescriptor.storageMode = MTLStorageModePrivate; - m_textureDescriptor.usage = MTLTextureUsageRenderTarget; + if ( m_iOS9Runtime || m_macOS11Runtime ) + { + m_textureDescriptor.cpuCacheMode = MTLCPUCacheModeDefaultCache; + m_textureDescriptor.storageMode = MTLStorageModePrivate; + m_textureDescriptor.usage = MTLTextureUsageRenderTarget; + } if (NULL != m_backBufferDepth) { release(m_backBufferDepth); } m_backBufferDepth = m_device.newTextureWithDescriptor(m_textureDescriptor); - m_backBufferStencil = m_backBufferDepth; + + + if (m_hasPixelFormatDepth32Float_Stencil8) + m_backBufferStencil = m_backBufferDepth; + else + { + m_textureDescriptor.pixelFormat = MTLPixelFormatStencil8; + m_backBufferStencil = m_device.newTextureWithDescriptor(m_textureDescriptor); + } + + if ( sampleCount > 1 ) + { + if (NULL != m_backBufferColorMSAA) + { + release(m_backBufferColorMSAA); + } + m_textureDescriptor.pixelFormat = m_metalLayer.pixelFormat; + m_backBufferColorMSAA = m_device.newTextureWithDescriptor(m_textureDescriptor); + } bx::HashMurmur2A murmur; murmur.begin(); murmur.add(1); - murmur.add((uint32_t)m_drawable.texture.pixelFormat); + murmur.add((uint32_t)m_metalLayer.pixelFormat); murmur.add((uint32_t)m_backBufferDepth.pixelFormat()); murmur.add((uint32_t)m_backBufferStencil.pixelFormat()); + murmur.add((uint32_t)sampleCount); m_backBufferPixelFormatHash = murmur.end(); for (uint32_t ii = 0; ii < BX_COUNTOF(m_frameBuffers); ++ii) @@ -974,11 +1186,107 @@ namespace bgfx { namespace mtl m_frameBuffers[ii].postReset(); } - m_textVideoMem.resize(false, width, height); + updateCapture(); + + m_textVideoMem.resize(false, _resolution.m_width, _resolution.m_height); m_textVideoMem.clear(); + + if ( prevMetalLayerPixelFormat != m_metalLayer.pixelFormat) + { + MTL_RELEASE(m_screenshotBlitRenderPipelineState) + reset(m_renderPipelineDescriptor); + m_renderPipelineDescriptor.colorAttachments[0].pixelFormat = m_metalLayer.pixelFormat; + m_renderPipelineDescriptor.vertexFunction = m_screenshotBlitProgram.m_vsh->m_function; + m_renderPipelineDescriptor.fragmentFunction = m_screenshotBlitProgram.m_fsh->m_function; + m_screenshotBlitRenderPipelineState = m_device.newRenderPipelineStateWithDescriptor(m_renderPipelineDescriptor); + } } } + + void updateCapture() + { + if (m_resolution.m_flags&BGFX_RESET_CAPTURE) + { + m_captureSize = m_resolution.m_width*m_resolution.m_height*4; + m_capture = BX_REALLOC(g_allocator, m_capture, m_captureSize); + g_callback->captureBegin(m_resolution.m_width, m_resolution.m_height, m_resolution.m_width*4, TextureFormat::BGRA8, false); + } + else + { + captureFinish(); + } + } + + void capture() + { + if (NULL != m_capture) + { + if (NULL == m_screenshotTarget) + return; + + m_renderCommandEncoder.endEncoding(); + + m_commandBuffer.commit(); + m_commandBuffer.waitUntilCompleted(); + MTL_RELEASE(m_commandBuffer) + + MTLRegion region = { { 0, 0, 0 }, { m_resolution.m_width, m_resolution.m_height, 1 } }; + + //TODO: enable screenshot target when capturing + m_screenshotTarget.getBytes(m_capture, 4*m_resolution.m_width, 0, region, 0, 0); + + m_commandBuffer = m_commandQueue.commandBuffer(); + retain(m_commandBuffer); //NOTE: keep alive to be useable at 'flip' + + if (m_screenshotTarget.pixelFormat() == MTLPixelFormatRGBA8Uint) + { + imageSwizzleBgra8(m_resolution.m_width, m_resolution.m_height, m_resolution.m_width*4, m_capture, m_capture); + } + + g_callback->captureFrame(m_capture, m_captureSize); + + RenderPassDescriptor renderPassDescriptor = newRenderPassDescriptor(); + setFrameBuffer(renderPassDescriptor, m_renderCommandEncoderFrameBufferHandle); + + for(uint32_t ii = 0; ii < g_caps.maxFBAttachments; ++ii) + { + MTLRenderPassColorAttachmentDescriptor* desc = renderPassDescriptor.colorAttachments[ii]; + if ( desc.texture != NULL) + desc.loadAction = MTLLoadActionLoad; + } + + RenderPassDepthAttachmentDescriptor depthAttachment = renderPassDescriptor.depthAttachment; + if (NULL != depthAttachment.texture) + { + depthAttachment.loadAction = MTLLoadActionLoad; + depthAttachment.storeAction = MTLStoreActionStore; + } + + RenderPassStencilAttachmentDescriptor stencilAttachment = renderPassDescriptor.stencilAttachment; + if (NULL != stencilAttachment.texture) + { + stencilAttachment.loadAction = MTLLoadActionLoad; + stencilAttachment.storeAction = MTLStoreActionStore; + } + + m_renderCommandEncoder = m_commandBuffer.renderCommandEncoderWithDescriptor(renderPassDescriptor); + MTL_RELEASE(renderPassDescriptor); + } + } + + void captureFinish() + { + if (NULL != m_capture) + { + g_callback->captureEnd(); + BX_FREE(g_allocator, m_capture); + m_capture = NULL; + m_captureSize = 0; + } + } + + void setShaderUniform(uint8_t _flags, uint32_t _loc, const void* _val, uint32_t _numRegs) { uint32_t offset = 0 != (_flags&BGFX_UNIFORM_FRAGMENTBIT) @@ -1081,16 +1389,155 @@ namespace bgfx { namespace mtl } } - void clearQuad(ClearQuad& _clearQuad, const Rect& _rect, const Clear& _clear, const float _palette[][4]) + void clearQuad(ClearQuad& _clearQuad, const Rect& /*_rect*/, const Clear& _clear, const float _palette[][4]) { - BX_UNUSED(_clearQuad, _rect, _clear, _palette); + uint32_t width; + uint32_t height; + + if (isValid(m_fbh) ) + { + const FrameBufferMtl& fb = m_frameBuffers[m_fbh.idx]; + width = fb.m_width; + height = fb.m_height; + } + else + { + width = getBufferWidth(); + height = getBufferHeight(); + } + + + uint64_t state = 0; + state |= _clear.m_flags & BGFX_CLEAR_COLOR ? BGFX_STATE_RGB_WRITE|BGFX_STATE_ALPHA_WRITE : 0; + state |= _clear.m_flags & BGFX_CLEAR_DEPTH ? BGFX_STATE_DEPTH_TEST_ALWAYS|BGFX_STATE_DEPTH_WRITE : 0; + + uint64_t stencil = 0; + stencil |= _clear.m_flags & BGFX_CLEAR_STENCIL ? 0 + | BGFX_STENCIL_TEST_ALWAYS + | BGFX_STENCIL_FUNC_REF(_clear.m_stencil) + | BGFX_STENCIL_FUNC_RMASK(0xff) + | BGFX_STENCIL_OP_FAIL_S_REPLACE + | BGFX_STENCIL_OP_FAIL_Z_REPLACE + | BGFX_STENCIL_OP_PASS_Z_REPLACE + : 0 + ; + + setDepthStencilState(state, stencil); + + uint32_t numMrt = 1; + FrameBufferHandle fbh = m_fbh; + if (isValid(fbh) ) + { + const FrameBufferMtl& fb = m_frameBuffers[fbh.idx]; + numMrt = bx::uint32_max(1, fb.m_num); + } + + ProgramMtl& program = m_program[_clearQuad.m_program[numMrt-1].idx]; + m_renderCommandEncoder.setRenderPipelineState(program.getRenderPipelineState(state, 0, fbh, _clearQuad.m_vb->decl, 0)); + + uint32_t fragmentUniformBufferSize = program.m_fshConstantBufferSize; + + m_uniformBufferFragmentOffset = m_uniformBufferVertexOffset; + if (fragmentUniformBufferSize) + { + m_uniformBufferFragmentOffset = BX_ALIGN_MASK(m_uniformBufferFragmentOffset, program.m_fshConstantBufferAlignmentMask); + m_renderCommandEncoder.setFragmentBuffer(m_uniformBuffer, m_uniformBufferFragmentOffset, 0); + } + + if (BGFX_CLEAR_COLOR_USE_PALETTE & _clear.m_flags) + { + float mrtClear[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS][4]; + for (uint32_t ii = 0; ii < numMrt; ++ii) + { + uint8_t index = (uint8_t)bx::uint32_min(BGFX_CONFIG_MAX_COLOR_PALETTE-1, _clear.m_index[ii]); + memcpy(mrtClear[ii], _palette[index], 16); + } + + memcpy((uint8_t*)m_uniformBuffer.contents() + m_uniformBufferFragmentOffset, + mrtClear, + bx::uint32_min(fragmentUniformBufferSize, sizeof(mrtClear))); + } + else + { + float rgba[4] = + { + _clear.m_index[0]*1.0f/255.0f, + _clear.m_index[1]*1.0f/255.0f, + _clear.m_index[2]*1.0f/255.0f, + _clear.m_index[3]*1.0f/255.0f, + }; + + memcpy((uint8_t*)m_uniformBuffer.contents() + m_uniformBufferFragmentOffset, + rgba, + bx::uint32_min(fragmentUniformBufferSize, sizeof(rgba))); + } + + m_uniformBufferFragmentOffset += fragmentUniformBufferSize; + m_uniformBufferVertexOffset = m_uniformBufferFragmentOffset; + + const VertexBufferMtl& vb = m_vertexBuffers[_clearQuad.m_vb->handle.idx]; + const VertexDecl& vertexDecl = m_vertexDecls[_clearQuad.m_vb->decl.idx]; + const uint32_t stride = vertexDecl.m_stride; + const uint32_t offset = 0; + + { + struct Vertex + { + float m_x; + float m_y; + float m_z; + }; + + Vertex* vertex = (Vertex*)_clearQuad.m_vb->data; + BX_CHECK(stride == sizeof(Vertex) + , "Stride/Vertex mismatch (stride %d, sizeof(Vertex) %d)" + , stride + , sizeof(Vertex) + ); + BX_UNUSED(stride); + + const float depth = _clear.m_depth; + + vertex->m_x = -1.0f; + vertex->m_y = -1.0f; + vertex->m_z = depth; + vertex++; + vertex->m_x = 1.0f; + vertex->m_y = -1.0f; + vertex->m_z = depth; + vertex++; + vertex->m_x = -1.0f; + vertex->m_y = 1.0f; + vertex->m_z = depth; + vertex++; + vertex->m_x = 1.0f; + vertex->m_y = 1.0f; + vertex->m_z = depth; + } + + m_vertexBuffers[_clearQuad.m_vb->handle.idx].update(0, 4*_clearQuad.m_decl.m_stride, _clearQuad.m_vb->data); + m_renderCommandEncoder.setCullMode(MTLCullModeNone); + m_renderCommandEncoder.setVertexBuffer(vb.getBuffer(), offset, 1); + m_renderCommandEncoder.drawPrimitives(MTLPrimitiveTypeTriangleStrip, 0, 4, 1); } void setFrameBuffer(RenderPassDescriptor renderPassDescriptor, FrameBufferHandle _fbh, bool _msaa = true) { if (!isValid(_fbh) ) { - renderPassDescriptor.colorAttachments[0].texture = m_drawable.texture; + if ( NULL != m_backBufferColorMSAA ) + { + renderPassDescriptor.colorAttachments[0].texture = m_backBufferColorMSAA; + renderPassDescriptor.colorAttachments[0].resolveTexture = ((NULL != m_screenshotTarget) ? + m_screenshotTarget.m_obj : + currentDrawable().texture); + } + else + { + renderPassDescriptor.colorAttachments[0].texture = ((NULL != m_screenshotTarget) ? + m_screenshotTarget.m_obj : + currentDrawable().texture); + } renderPassDescriptor.depthAttachment.texture = m_backBufferDepth; renderPassDescriptor.stencilAttachment.texture = m_backBufferStencil; } @@ -1101,15 +1548,26 @@ namespace bgfx { namespace mtl for (uint32_t ii = 0; ii < frameBuffer.m_num; ++ii) { const TextureMtl& texture = m_textures[frameBuffer.m_colorHandle[ii].idx]; - renderPassDescriptor.colorAttachments[ii].texture = texture.m_ptr; + renderPassDescriptor.colorAttachments[ii].texture = texture.m_ptrMSAA ? texture.m_ptrMSAA : texture.m_ptr; + renderPassDescriptor.colorAttachments[ii].resolveTexture = texture.m_ptrMSAA ? texture.m_ptr.m_obj : NULL; } if (isValid(frameBuffer.m_depthHandle) ) { const TextureMtl& texture = m_textures[frameBuffer.m_depthHandle.idx]; - renderPassDescriptor.depthAttachment.texture = texture.m_ptr; + renderPassDescriptor.depthAttachment.texture = texture.m_ptrMSAA ? texture.m_ptrMSAA : texture.m_ptr; renderPassDescriptor.stencilAttachment.texture = texture.m_ptrStencil; - //TODO: stencilAttachment should be the same if packed/depth stencil format is used + + if ( texture.m_textureFormat == TextureFormat::D24S8)//TODO: msaa and stencil iOS8 hack + { + if ( texture.m_ptr.pixelFormat() == 255 /* Depth24Unorm_Stencil8 */|| + texture.m_ptr.pixelFormat() == 260 /* Depth32Float_Stencil8 */ ) + { + renderPassDescriptor.stencilAttachment.texture = renderPassDescriptor.depthAttachment.texture; + } + else + renderPassDescriptor.stencilAttachment.texture = texture.m_ptrMSAA ? texture.m_ptrMSAA : texture.m_ptrStencil; + } } } @@ -1203,11 +1661,12 @@ namespace bgfx { namespace mtl m_samplerDescriptor.normalizedCoordinates = TRUE; m_samplerDescriptor.maxAnisotropy = m_maxAnisotropy; - //TODO: I haven't found how to specify this. Comparison function can be specified in shader. - // On OSX this can be specified. There is no support for this on iOS right now. - //const uint32_t cmpFunc = (_flags&BGFX_TEXTURE_COMPARE_MASK)>>BGFX_TEXTURE_COMPARE_SHIFT; - //const uint8_t filter = 0 == cmpFunc ? 0 : D3D11_COMPARISON_FILTERING_BIT; - //m_samplerDescriptor.comparisonFunc = 0 == cmpFunc ? D3D11_COMPARISON_NEVER : s_cmpFunc[cmpFunc]; + //NOTE: Comparison function can be specified in shader on all metal hw. + if ( m_macOS11Runtime || [m_device supportsFeatureSet:(MTLFeatureSet)4/*MTLFeatureSet_iOS_GPUFamily3_v1*/]) + { + const uint32_t cmpFunc = (_flags&BGFX_TEXTURE_COMPARE_MASK)>>BGFX_TEXTURE_COMPARE_SHIFT; + m_samplerDescriptor.compareFunction = 0 == cmpFunc ? MTLCompareFunctionNever : s_cmpFunc[cmpFunc]; + } sampler = m_device.newSamplerStateWithDescriptor(m_samplerDescriptor); m_samplerStateCache.add(_flags, sampler); @@ -1232,21 +1691,68 @@ namespace bgfx { namespace mtl return m_backBufferDepth.height(); } - Device m_device; + void sync() + { + if ( m_prevCommandBuffer ) + m_prevCommandBuffer.waitUntilCompleted(); + } + + BlitCommandEncoder getBlitCommandEncoder() + { + if ( m_blitCommandEncoder == NULL) + { + if ( m_commandBuffer == NULL ) + { + m_commandBuffer = m_commandQueue.commandBuffer(); + retain(m_commandBuffer); + } + + m_blitCommandEncoder = m_commandBuffer.blitCommandEncoder(); + } + + return m_blitCommandEncoder; + } + + id<CAMetalDrawable> currentDrawable() + { + if (m_drawable == nil) + { + m_drawable = m_metalLayer.nextDrawable; +#if BX_PLATFORM_IOS + retain(m_drawable); // keep alive to be useable at 'flip' +#endif + } + + return m_drawable; + } + + + Device m_device; + OcclusionQueryMTL m_occlusionQuery; + TimerQueryMtl m_gpuTimer; + CommandQueue m_commandQueue; CAMetalLayer* m_metalLayer; + Texture m_backBufferColorMSAA; Texture m_backBufferDepth; Texture m_backBufferStencil; uint32_t m_backBufferPixelFormatHash; uint32_t m_maxAnisotropy; - OcclusionQueryMTL m_occlusionQuery; + bool m_iOS9Runtime; + bool m_macOS11Runtime; + bool m_hasPixelFormatDepth32Float_Stencil8; + + + + bx::Semaphore m_framesSemaphore; Buffer m_uniformBuffer; - Buffer m_uniformBuffers[UNIFORM_BUFFER_COUNT]; + Buffer m_uniformBuffers[MTL_MAX_FRAMES_IN_FLIGHT]; uint32_t m_uniformBufferVertexOffset; uint32_t m_uniformBufferFragmentOffset; - uint8_t m_uniformBufferIndex; + + uint8_t m_bufferIndex; uint16_t m_numWindows; FrameBufferHandle m_windows[BGFX_CONFIG_MAX_FRAME_BUFFERS]; @@ -1270,6 +1776,8 @@ namespace bgfx { namespace mtl bool m_rtMsaa; Resolution m_resolution; + void* m_capture; + uint32_t m_captureSize; // descriptors RenderPipelineDescriptor m_renderPipelineDescriptor; @@ -1281,9 +1789,19 @@ namespace bgfx { namespace mtl SamplerDescriptor m_samplerDescriptor; // currently active objects data - id <CAMetalDrawable> m_drawable; - CommandBuffer m_commandBuffer; - RenderCommandEncoder m_renderCommandEncoder; + id <CAMetalDrawable> m_drawable; + bool m_saveScreenshot; + Texture m_screenshotTarget; + ShaderMtl m_screenshotBlitProgramVsh; + ShaderMtl m_screenshotBlitProgramFsh; + ProgramMtl m_screenshotBlitProgram; + RenderPipelineState m_screenshotBlitRenderPipelineState; + + CommandBuffer m_commandBuffer; + CommandBuffer m_prevCommandBuffer; + BlitCommandEncoder m_blitCommandEncoder; + RenderCommandEncoder m_renderCommandEncoder; + FrameBufferHandle m_renderCommandEncoderFrameBufferHandle; }; static RendererContextMtl* s_renderMtl; @@ -1330,8 +1848,6 @@ namespace bgfx { namespace mtl break; } - //bool fragment = BGFX_CHUNK_MAGIC_FSH == magic; - uint32_t iohash; bx::read(&reader, iohash); @@ -1371,23 +1887,6 @@ namespace bgfx { namespace mtl const char* code = (const char*)reader.getDataPtr(); bx::skip(&reader, shaderSize+1); - int32_t codeLen = (int32_t)strlen(code); - int32_t tempLen = codeLen + (4<<10); - char* temp = (char*)alloca(tempLen); - bx::StaticMemoryBlockWriter writer(temp, tempLen); - - //TODO: remove this hack. some shaders have problem with half<->float conversion - writeString(&writer - , "#define half float\n" - "#define half2 float2\n" - "#define half3 float3\n" - "#define half4 float4\n" - ); - - bx::write(&writer, code, codeLen); - bx::write(&writer, '\0'); - code = temp; - //TODO: use binary format Library lib = s_renderMtl->m_device.newLibraryWithSource(code); @@ -1506,7 +2005,7 @@ namespace bgfx { namespace mtl RenderPipelineState ProgramMtl::getRenderPipelineState(uint64_t _state, uint32_t _rgba, FrameBufferHandle _fbHandle, VertexDeclHandle _declHandle, uint16_t _numInstanceData) { - _state &= (BGFX_STATE_BLEND_MASK|BGFX_STATE_BLEND_EQUATION_MASK|BGFX_STATE_ALPHA_WRITE|BGFX_STATE_RGB_WRITE|BGFX_STATE_BLEND_INDEPENDENT|BGFX_STATE_MSAA); + _state &= (BGFX_STATE_BLEND_MASK|BGFX_STATE_BLEND_EQUATION_MASK|BGFX_STATE_ALPHA_WRITE|BGFX_STATE_RGB_WRITE|BGFX_STATE_BLEND_INDEPENDENT|BGFX_STATE_MSAA|BGFX_STATE_BLEND_ALPHA_TO_COVERAGE); bool independentBlendEnable = !!(BGFX_STATE_BLEND_INDEPENDENT & _state); @@ -1532,11 +2031,15 @@ namespace bgfx { namespace mtl { RenderPipelineDescriptor& pd = s_renderMtl->m_renderPipelineDescriptor; reset(pd); + + pd.alphaToCoverageEnabled = !!(BGFX_STATE_BLEND_ALPHA_TO_COVERAGE & _state); + uint32_t frameBufferAttachment = 1; if (!isValid(_fbHandle) ) { - pd.colorAttachments[0].pixelFormat = s_renderMtl->m_drawable.texture.pixelFormat; + pd.sampleCount = NULL != s_renderMtl->m_backBufferColorMSAA ? s_renderMtl->m_backBufferColorMSAA.sampleCount() : 1; + pd.colorAttachments[0].pixelFormat = s_renderMtl->currentDrawable().texture.pixelFormat; pd.depthAttachmentPixelFormat = s_renderMtl->m_backBufferDepth.m_obj.pixelFormat; pd.stencilAttachmentPixelFormat = s_renderMtl->m_backBufferStencil.m_obj.pixelFormat; } @@ -1548,6 +2051,7 @@ namespace bgfx { namespace mtl for (uint32_t ii = 0; ii < frameBuffer.m_num; ++ii) { const TextureMtl& texture = s_renderMtl->m_textures[frameBuffer.m_colorHandle[ii].idx]; + pd.sampleCount = NULL != texture.m_ptrMSAA ? texture.m_ptrMSAA.sampleCount() : 1; pd.colorAttachments[ii].pixelFormat = texture.m_ptr.m_obj.pixelFormat; } @@ -1557,13 +2061,16 @@ namespace bgfx { namespace mtl pd.depthAttachmentPixelFormat = texture.m_ptr.m_obj.pixelFormat; if (NULL != texture.m_ptrStencil) { - pd.stencilAttachmentPixelFormat = MTLPixelFormatInvalid; //texture.m_ptrStencil.m_obj.pixelFormat; + pd.stencilAttachmentPixelFormat = texture.m_ptrStencil.m_obj.pixelFormat; + } + else + { + if ( texture.m_textureFormat == TextureFormat::D24S8) + pd.stencilAttachmentPixelFormat = texture.m_ptr.m_obj.pixelFormat; } - //todo: stencil attachment should be the same as depth for packed depth/stencil } } - // TODO: BGFX_STATE_MSAA using _fbHandle texture msaa values const uint32_t blend = uint32_t( (_state&BGFX_STATE_BLEND_MASK)>>BGFX_STATE_BLEND_SHIFT); const uint32_t equation = uint32_t( (_state&BGFX_STATE_BLEND_EQUATION_MASK)>>BGFX_STATE_BLEND_EQUATION_SHIFT); @@ -1649,7 +2156,7 @@ namespace bgfx { namespace mtl BX_TRACE("attrib:%s format: %d offset:%d", s_attribName[attr], (int)vertexDesc.attributes[loc].format, (int)vertexDesc.attributes[loc].offset); } else - { // missing attribute: using dummy attribute with smallest possible size + { // NOTE: missing attribute: using dummy attribute with smallest possible size vertexDesc.attributes[loc].format = MTLVertexFormatUChar2; vertexDesc.attributes[loc].bufferIndex = 1; vertexDesc.attributes[loc].offset = 0; @@ -1778,6 +2285,9 @@ namespace bgfx { namespace mtl } else if (arg.type == MTLArgumentTypeTexture) { + if ( shaderType == 0 ) m_usedVertexSamplerStages |= 1<<arg.index; + else m_usedFragmentSamplerStages |= 1<<arg.index; + BX_TRACE("texture: %s index:%d", utf8String(arg.name), arg.index); } else if (arg.type == MTLArgumentTypeSampler) @@ -1803,20 +2313,23 @@ namespace bgfx { namespace mtl return rps; } + void BufferMtl::create(uint32_t _size, void* _data, uint16_t _flags, uint16_t _stride, bool _vertex) { BX_UNUSED(_stride, _vertex); m_size = _size; m_flags = _flags; + m_dynamic = (NULL == _data); if (NULL == _data) { - m_buffer = s_renderMtl->m_device.newBufferWithLength(_size, 0); + for (uint32_t ii = 0; ii < MTL_MAX_FRAMES_IN_FLIGHT; ++ii) + m_buffers[ii] = s_renderMtl->m_device.newBufferWithLength(_size, 0); } else { - m_buffer = s_renderMtl->m_device.newBufferWithBytes(_data, _size, 0); + m_buffers[0] = s_renderMtl->m_device.newBufferWithBytes(_data, _size, 0); } } @@ -1824,7 +2337,26 @@ namespace bgfx { namespace mtl { BX_UNUSED(_discard); - memcpy( (uint8_t*)m_buffer.contents() + _offset, _data, _size); + //TODO: cannot call this more than once per frame + if ( m_dynamic && _discard ) + { + m_bufferIndex = (m_bufferIndex + 1) % MTL_MAX_FRAMES_IN_FLIGHT; + memcpy( (uint8_t*)getBuffer().contents() + _offset, _data, _size); + } + else if ( NULL != s_renderMtl->m_renderCommandEncoder ) + { // NOTE: cannot blit while rendercommander is active. have to sync. slow. remove these. + // ClearQuad triggers this now + s_renderMtl->sync(); + memcpy( (uint8_t*)getBuffer().contents() + _offset, _data, _size); + } + else + { + BlitCommandEncoder bce = s_renderMtl->getBlitCommandEncoder(); + + Buffer temp = s_renderMtl->m_device.newBufferWithBytes(_data, _size, 0); + bce.copyFromBuffer(temp, 0, getBuffer(), _offset, _size); + release(temp); + } } void VertexBufferMtl::create(uint32_t _size, void* _data, VertexDeclHandle _declHandle, uint16_t _flags) @@ -1854,6 +2386,9 @@ namespace bgfx { namespace mtl const uint32_t textureHeight = bx::uint32_max(blockInfo.blockHeight, imageContainer.m_height>>startLod); m_flags = _flags; + m_width = textureWidth; + m_height = textureHeight; + m_depth = imageContainer.m_depth; m_requestedFormat = uint8_t(imageContainer.m_format); m_textureFormat = uint8_t(getViableTextureFormat(imageContainer) ); const bool convert = m_textureFormat != m_requestedFormat; @@ -1889,11 +2424,11 @@ namespace bgfx { namespace mtl ); const bool writeOnly = 0 != (_flags&BGFX_TEXTURE_RT_WRITE_ONLY); -// const bool computeWrite = 0 != (_flags&BGFX_TEXTURE_COMPUTE_WRITE); -// const bool renderTarget = 0 != (_flags&BGFX_TEXTURE_RT_MASK); + const bool computeWrite = 0 != (_flags&BGFX_TEXTURE_COMPUTE_WRITE); + const bool renderTarget = 0 != (_flags&BGFX_TEXTURE_RT_MASK); const bool srgb = 0 != (_flags&BGFX_TEXTURE_SRGB) || imageContainer.m_srgb; -// const uint32_t msaaQuality = bx::uint32_satsub( (_flags&BGFX_TEXTURE_RT_MSAA_MASK)>>BGFX_TEXTURE_RT_MSAA_SHIFT, 1); -// const DXGI_SAMPLE_DESC& msaa = s_msaa[msaaQuality]; + const uint32_t msaaQuality = bx::uint32_satsub( (_flags&BGFX_TEXTURE_RT_MSAA_MASK)>>BGFX_TEXTURE_RT_MSAA_SHIFT, 1); + int sampleCount = s_msaa[msaaQuality]; MTLPixelFormat format = MTLPixelFormatInvalid; if (srgb) @@ -1916,22 +2451,35 @@ namespace bgfx { namespace mtl desc.height = textureHeight; desc.depth = bx::uint32_max(1,imageContainer.m_depth); desc.mipmapLevelCount = imageContainer.m_numMips; - desc.sampleCount = 1; //TODO: set samplecount - If textureType is not MTLTextureType2DMultisample, the value must be 1. - desc.resourceOptions = MTLResourceStorageModePrivate; - desc.cpuCacheMode = MTLCPUCacheModeDefaultCache; - - desc.storageMode = (MTLStorageMode)(writeOnly - ? 2 /*MTLStorageModePrivate*/ - : 1 /*MTLStorageModeManaged*/ - ); - desc.usage = writeOnly - ? MTLTextureUsageShaderWrite - : MTLTextureUsageShaderRead - ; + desc.sampleCount = 1; - //TODO: set resource flags depending on usage(renderTarget/computeWrite/etc) on iOS9/OSX + if (s_renderMtl->m_iOS9Runtime || s_renderMtl->m_macOS11Runtime) + { + desc.cpuCacheMode = MTLCPUCacheModeDefaultCache; + + desc.storageMode = (MTLStorageMode)(writeOnly||isDepth(TextureFormat::Enum(m_textureFormat)) + ? 2 /*MTLStorageModePrivate*/ + : ((BX_ENABLED(BX_PLATFORM_IOS)) ? 0 /* MTLStorageModeShared */ : 1 /*MTLStorageModeManaged*/) + ); + + desc.usage = MTLTextureUsageShaderRead; + if (computeWrite) + desc.usage |= MTLTextureUsageShaderWrite; + if (renderTarget) + desc.usage |= MTLTextureUsageRenderTarget; + } m_ptr = s_renderMtl->m_device.newTextureWithDescriptor(desc); + + if ( sampleCount > 1) + { + desc.textureType = MTLTextureType2DMultisample; + desc.sampleCount = sampleCount; + if (s_renderMtl->m_iOS9Runtime || s_renderMtl->m_macOS11Runtime) + desc.storageMode = (MTLStorageMode)( 2 /*MTLStorageModePrivate*/); + m_ptrMSAA = s_renderMtl->m_device.newTextureWithDescriptor(desc); + } + if (m_requestedFormat == TextureFormat::D24S8 && desc.pixelFormat == MTLPixelFormatDepth32Float) { @@ -1964,7 +2512,7 @@ namespace bgfx { namespace mtl if (convert) { - imageDecodeToRgba8(temp + imageDecodeToBgra8(temp , mip.m_data , mip.m_width , mip.m_height @@ -2023,12 +2571,6 @@ namespace bgfx { namespace mtl void TextureMtl::update(uint8_t _side, uint8_t _mip, const Rect& _rect, uint16_t _z, uint16_t _depth, uint16_t _pitch, const Memory* _mem) { - MTLRegion region = - { - { _rect.m_x, _rect.m_y, _z }, - { _rect.m_width, _rect.m_height, _depth }, - }; - const uint32_t bpp = getBitsPerPixel(TextureFormat::Enum(m_textureFormat) ); const uint32_t rectpitch = _rect.m_width*bpp/8; const uint32_t srcpitch = UINT16_MAX == _pitch ? rectpitch : _pitch; @@ -2051,7 +2593,38 @@ namespace bgfx { namespace mtl data = temp; } - m_ptr.replaceRegion(region, _mip, _side, data, srcpitch, srcpitch * _rect.m_height); + if ( NULL != s_renderMtl->m_renderCommandEncoder ) + { + s_renderMtl->sync(); + + MTLRegion region = + { + { _rect.m_x, _rect.m_y, _z }, + { _rect.m_width, _rect.m_height, _depth }, + }; + + m_ptr.replaceRegion(region, _mip, _side, data, srcpitch, srcpitch * _rect.m_height); + } + else + { + BlitCommandEncoder bce = s_renderMtl->getBlitCommandEncoder(); + + const uint32_t dstpitch = bx::strideAlign(rectpitch, 64); + + Buffer tempBuffer = s_renderMtl->m_device.newBufferWithLength(dstpitch*_rect.m_height, 0); + + const uint8_t* src = (uint8_t*)data; + uint8_t* dst = (uint8_t*)tempBuffer.contents(); + + for (uint32_t yy = 0; yy < _rect.m_height; ++yy, src += srcpitch, dst += dstpitch) + { + memcpy(dst, src, rectpitch); + } + + bce.copyFromBuffer(tempBuffer, 0, dstpitch, dstpitch * _rect.m_height, MTLSizeMake(_rect.m_width, _rect.m_height, _depth), + m_ptr, _side, _mip, MTLOriginMake(_rect.m_x, _rect.m_y, _z)); + release(tempBuffer); + } if (NULL != temp) { @@ -2059,13 +2632,23 @@ namespace bgfx { namespace mtl } } - void TextureMtl::commit(uint8_t _stage, uint32_t _flags) + void TextureMtl::commit(uint8_t _stage, bool _vertex, bool _fragment, uint32_t _flags) { - //TODO: vertex or fragment stage? - s_renderMtl->m_renderCommandEncoder.setFragmentTexture(m_ptr, _stage); - s_renderMtl->m_renderCommandEncoder.setFragmentSamplerState(0 == (BGFX_TEXTURE_INTERNAL_DEFAULT_SAMPLER & _flags) - ? s_renderMtl->getSamplerState(_flags) - : m_sampler, _stage); + if (_vertex) + { + s_renderMtl->m_renderCommandEncoder.setVertexTexture(m_ptr, _stage); + s_renderMtl->m_renderCommandEncoder.setVertexSamplerState(0 == (BGFX_TEXTURE_INTERNAL_DEFAULT_SAMPLER & _flags) + ? s_renderMtl->getSamplerState(_flags) + : m_sampler, _stage); + } + + if (_fragment) + { + s_renderMtl->m_renderCommandEncoder.setFragmentTexture(m_ptr, _stage); + s_renderMtl->m_renderCommandEncoder.setFragmentSamplerState(0 == (BGFX_TEXTURE_INTERNAL_DEFAULT_SAMPLER & _flags) + ? s_renderMtl->getSamplerState(_flags) + : m_sampler, _stage); + } } void FrameBufferMtl::create(uint8_t _num, const Attachment* _attachment) @@ -2078,7 +2661,6 @@ namespace bgfx { namespace mtl { const TextureMtl& texture = s_renderMtl->m_textures[handle.idx]; - //TODO: separate stencil buffer? or just use packed depth/stencil (which is not available on iOS8) if (isDepth( (TextureFormat::Enum)texture.m_textureFormat) ) { m_depthHandle = handle; @@ -2100,9 +2682,18 @@ namespace bgfx { namespace mtl const TextureMtl& texture = s_renderMtl->m_textures[m_colorHandle[ii].idx]; murmur.add((uint32_t)texture.m_ptr.pixelFormat()); } - const TextureMtl& depthTexture = s_renderMtl->m_textures[m_depthHandle.idx]; - murmur.add((uint32_t)depthTexture.m_ptr.pixelFormat()); - murmur.add((uint32_t)MTLPixelFormatInvalid); //stencil + if (!isValid(m_depthHandle)) + { + murmur.add((uint32_t)MTLPixelFormatInvalid); + murmur.add((uint32_t)MTLPixelFormatInvalid); + } + else + { + const TextureMtl& depthTexture = s_renderMtl->m_textures[m_depthHandle.idx]; + murmur.add((uint32_t)depthTexture.m_ptr.pixelFormat()); + murmur.add((uint32_t)(NULL != depthTexture.m_ptrStencil ? depthTexture.m_ptrStencil.pixelFormat() : MTLPixelFormatInvalid)); + } + murmur.add(1); //SampleCount m_pixelFormatHash = murmur.end(); } @@ -2131,6 +2722,51 @@ namespace bgfx { namespace mtl return denseIdx; } + void TimerQueryMtl::init() + { + m_frequency = bx::getHPFrequency(); + } + + void TimerQueryMtl::shutdown() + { + } + + static void setTimestamp(void* _data) + { + *((int64_t*)_data) = bx::getHPCounter(); + } + + void TimerQueryMtl::addHandlers(CommandBuffer& _commandBuffer) + { + while (0 == m_control.reserve(1) ) + { + m_control.consume(1); + } + + uint32_t offset = m_control.m_current * 2 + 0; + + _commandBuffer.addScheduledHandler(setTimestamp, &m_result[offset]); + _commandBuffer.addCompletedHandler(setTimestamp, &m_result[offset+1]); + m_control.commit(1); + } + + bool TimerQueryMtl::get() + { + if (0 != m_control.available() ) + { + uint32_t offset = m_control.m_read * 2; + m_begin = m_result[offset+0]; + m_end = m_result[offset+1]; + m_elapsed = m_end - m_begin; + + m_control.consume(1); + + return true; + } + + return false; + } + void OcclusionQueryMTL::postReset() { MTL_RELEASE(m_buffer); @@ -2177,42 +2813,82 @@ namespace bgfx { namespace mtl void RendererContextMtl::submit(Frame* _render, ClearQuad& _clearQuad, TextVideoMemBlitter& _textVideoMemBlitter) BX_OVERRIDE { - m_commandBuffer = m_commandQueue.commandBuffer(); - retain(m_commandBuffer); // keep alive to be useable at 'flip' + m_framesSemaphore.wait(); + + if ( m_commandBuffer == NULL ) + { + m_commandBuffer = m_commandQueue.commandBuffer(); + retain(m_commandBuffer); // keep alive to be useable at 'flip' + } - //TODO: multithreading with multiple commandbuffer - // is there a FAST way to tell which view is active? + int64_t elapsed = -bx::getHPCounter(); + int64_t captureElapsed = 0; - //TODO: acquire CAMetalDrawable just before we really need it. When we are using an encoder with target metalLayer's texture - m_drawable = m_metalLayer.nextDrawable; -// retain(m_drawable); // keep alive to be useable at 'flip' + m_gpuTimer.addHandlers(m_commandBuffer); - m_uniformBuffer = m_uniformBuffers[m_uniformBufferIndex]; - m_uniformBufferIndex = (m_uniformBufferIndex + 1) % UNIFORM_BUFFER_COUNT; - m_uniformBufferVertexOffset = 0; - m_uniformBufferFragmentOffset = 0; + if ( m_blitCommandEncoder ) + { + m_blitCommandEncoder.endEncoding(); + m_blitCommandEncoder = 0; + } updateResolution(_render->m_resolution); - int64_t elapsed = -bx::getHPCounter(); - int64_t captureElapsed = 0; + if ( m_saveScreenshot || NULL != m_capture ) + { + if ( m_screenshotTarget ) + { + if ( m_screenshotTarget.width() != m_resolution.m_width || + m_screenshotTarget.height() != m_resolution.m_height ) + { + MTL_RELEASE(m_screenshotTarget); + } + } - if (_render->m_debug & (BGFX_DEBUG_IFH|BGFX_DEBUG_STATS) ) + if ( NULL == m_screenshotTarget) + { + m_textureDescriptor.textureType = MTLTextureType2D; + m_textureDescriptor.pixelFormat = m_metalLayer.pixelFormat; + m_textureDescriptor.width = m_resolution.m_width; + m_textureDescriptor.height = m_resolution.m_height; + m_textureDescriptor.depth = 1; + m_textureDescriptor.mipmapLevelCount = 1; + m_textureDescriptor.sampleCount = 1; + m_textureDescriptor.arrayLength = 1; + if ( m_iOS9Runtime || m_macOS11Runtime ) + { + m_textureDescriptor.cpuCacheMode = MTLCPUCacheModeDefaultCache; + m_textureDescriptor.storageMode = (MTLStorageMode)(((BX_ENABLED(BX_PLATFORM_IOS)) ? 0 /* MTLStorageModeShared */ : 1 /*MTLStorageModeManaged*/) + ); + m_textureDescriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead; + } + + m_screenshotTarget = m_device.newTextureWithDescriptor(m_textureDescriptor); + } + m_saveScreenshot = false; + } + else { - //TODO - //m_gpuTimer.begin(); + MTL_RELEASE(m_screenshotTarget); } + m_uniformBuffer = m_uniformBuffers[m_bufferIndex]; + m_bufferIndex = (m_bufferIndex + 1) % MTL_MAX_FRAMES_IN_FLIGHT; + m_uniformBufferVertexOffset = 0; + m_uniformBufferFragmentOffset = 0; + + + if (0 < _render->m_iboffset) { TransientIndexBuffer* ib = _render->m_transientIb; - m_indexBuffers[ib->handle.idx].update(0, _render->m_iboffset, ib->data); + m_indexBuffers[ib->handle.idx].update(0, _render->m_iboffset, ib->data, true); } if (0 < _render->m_vboffset) { TransientVertexBuffer* vb = _render->m_transientVb; - m_vertexBuffers[vb->handle.idx].update(0, _render->m_vboffset, vb->data); + m_vertexBuffers[vb->handle.idx].update(0, _render->m_vboffset, vb->data, true); } _render->sort(); @@ -2230,23 +2906,16 @@ namespace bgfx { namespace mtl bool wireframe = !!(_render->m_debug&BGFX_DEBUG_WIREFRAME); - //TODO: REMOVE THIS - TEMPORARY HACK - m_textureDescriptor.textureType = MTLTextureType2D; - m_textureDescriptor.pixelFormat = MTLPixelFormatRGBA8Unorm; - m_textureDescriptor.width = 4; - m_textureDescriptor.height = 4; - m_textureDescriptor.depth = 1; - m_textureDescriptor.mipmapLevelCount = 1; - m_textureDescriptor.sampleCount = 1; //TODO: set samplecount - If textureType is not MTLTextureType2DMultisample, the value must be 1. - Texture zeroTexture = m_device.newTextureWithDescriptor(m_textureDescriptor); - uint16_t programIdx = invalidHandle; SortKey key; uint16_t view = UINT16_MAX; FrameBufferHandle fbh = { BGFX_CONFIG_MAX_FRAME_BUFFERS }; - //ASK: why should we use this? It changes topology, so possible renders a big mess, doesn't it? - //const uint64_t primType = _render->m_debug&BGFX_DEBUG_WIREFRAME ? BGFX_STATE_PT_LINES : 0; + BlitKey blitKey; + blitKey.decode(_render->m_blitKeys[0]); + uint16_t numBlitItems = _render->m_numBlitItems; + uint16_t blitItem = 0; + const uint64_t primType = 0; uint8_t primIndex = uint8_t(primType>>BGFX_STATE_PT_SHIFT); PrimInfo prim = s_primInfo[primIndex]; @@ -2329,94 +2998,175 @@ namespace bgfx { namespace mtl viewState.m_rect.m_width /= 2; } + + for (; blitItem < numBlitItems && blitKey.m_view <= view; blitItem++) + { + if (0 != m_renderCommandEncoder) + { + m_renderCommandEncoder.endEncoding(); + m_renderCommandEncoder = 0; + } + m_blitCommandEncoder = getBlitCommandEncoder(); + + const BlitItem& blit = _render->m_blitItem[blitItem]; + blitKey.decode(_render->m_blitKeys[blitItem+1]); + + const TextureMtl& src = m_textures[blit.m_src.idx]; + const TextureMtl& dst = m_textures[blit.m_dst.idx]; + + uint32_t srcWidth = bx::uint32_min(src.m_width, blit.m_srcX + blit.m_width) - blit.m_srcX; + uint32_t srcHeight = bx::uint32_min(src.m_height, blit.m_srcY + blit.m_height) - blit.m_srcY; + uint32_t srcDepth = bx::uint32_min(src.m_depth, blit.m_srcZ + blit.m_depth) - blit.m_srcZ; + uint32_t dstWidth = bx::uint32_min(dst.m_width, blit.m_dstX + blit.m_width) - blit.m_dstX; + uint32_t dstHeight = bx::uint32_min(dst.m_height, blit.m_dstY + blit.m_height) - blit.m_dstY; + uint32_t dstDepth = bx::uint32_min(dst.m_depth, blit.m_dstZ + blit.m_depth) - blit.m_dstZ; + uint32_t width = bx::uint32_min(srcWidth, dstWidth); + uint32_t height = bx::uint32_min(srcHeight, dstHeight); + uint32_t depth = bx::uint32_min(srcDepth, dstDepth); + + if ( MTLTextureType3D == src.m_ptr.textureType()) + { + m_blitCommandEncoder.copyFromTexture(src.m_ptr, 0, 0, MTLOriginMake(blit.m_srcX, blit.m_srcY, blit.m_srcZ), MTLSizeMake(width, height, bx::uint32_imax(depth, 1)), + dst.m_ptr, 0, 0, MTLOriginMake(blit.m_dstX, blit.m_dstY, blit.m_dstZ)); + } + else + { + m_blitCommandEncoder.copyFromTexture(src.m_ptr, blit.m_srcZ, blit.m_srcMip, MTLOriginMake(blit.m_srcX, blit.m_srcY, 0), MTLSizeMake(width, height, 1), + dst.m_ptr, blit.m_dstZ, blit.m_dstMip, MTLOriginMake(blit.m_dstX, blit.m_dstY, 0)); + } + } + + if (0 != m_blitCommandEncoder) + { + m_blitCommandEncoder.endEncoding(); + m_blitCommandEncoder = 0; + } + const Rect& scissorRect = _render->m_scissor[view]; viewHasScissor = !scissorRect.isZero(); viewScissorRect = viewHasScissor ? scissorRect : viewState.m_rect; Clear& clr = _render->m_clear[view]; - RenderPassDescriptor renderPassDescriptor = newRenderPassDescriptor(); - renderPassDescriptor.visibilityResultBuffer = m_occlusionQuery.m_buffer; - uint32_t width = getBufferWidth(); uint32_t height = getBufferHeight(); Rect viewRect = viewState.m_rect; - bool fullscreenRect = true + bool clearWithRenderPass = true && 0 == viewRect.m_x && 0 == viewRect.m_y && width == viewRect.m_width && height == viewRect.m_height ; - fbh = _render->m_fb[view]; - setFrameBuffer(renderPassDescriptor, fbh); + if ( NULL == m_renderCommandEncoder || fbh.idx != _render->m_fb[view].idx ) + { + if (0 != m_renderCommandEncoder) + { + m_renderCommandEncoder.endEncoding(); + } - RenderPassColorAttachmentDescriptor colorAttachment0 = renderPassDescriptor.colorAttachments[0]; + RenderPassDescriptor renderPassDescriptor = newRenderPassDescriptor(); + renderPassDescriptor.visibilityResultBuffer = m_occlusionQuery.m_buffer; - if (0 != (BGFX_CLEAR_COLOR & clr.m_flags) ) - { - if (0 != (BGFX_CLEAR_COLOR_USE_PALETTE & clr.m_flags) ) + fbh = _render->m_fb[view]; + setFrameBuffer(renderPassDescriptor, fbh); + + if ( clearWithRenderPass ) { - uint8_t index = (uint8_t)bx::uint32_min(BGFX_CONFIG_MAX_COLOR_PALETTE-1, clr.m_index[0]); - const float* rgba = _render->m_colorPalette[index]; - const float rr = rgba[0]; - const float gg = rgba[1]; - const float bb = rgba[2]; - const float aa = rgba[3]; - colorAttachment0.clearColor = MTLClearColorMake(rr, gg, bb, aa); + for(uint32_t ii = 0; ii < g_caps.maxFBAttachments; ++ii) + { + MTLRenderPassColorAttachmentDescriptor* desc = renderPassDescriptor.colorAttachments[ii]; + + if ( desc.texture != NULL) + { + if (0 != (BGFX_CLEAR_COLOR & clr.m_flags) ) + { + if (0 != (BGFX_CLEAR_COLOR_USE_PALETTE & clr.m_flags) ) + { + uint8_t index = (uint8_t)bx::uint32_min(BGFX_CONFIG_MAX_COLOR_PALETTE-1, clr.m_index[ii]); + const float* rgba = _render->m_colorPalette[index]; + const float rr = rgba[0]; + const float gg = rgba[1]; + const float bb = rgba[2]; + const float aa = rgba[3]; + desc.clearColor = MTLClearColorMake(rr, gg, bb, aa); + } + else + { + float rr = clr.m_index[0]*1.0f/255.0f; + float gg = clr.m_index[1]*1.0f/255.0f; + float bb = clr.m_index[2]*1.0f/255.0f; + float aa = clr.m_index[3]*1.0f/255.0f; + desc.clearColor = MTLClearColorMake(rr, gg, bb, aa); + } + + desc.loadAction = MTLLoadActionClear; + } + else + { + desc.loadAction = MTLLoadActionLoad; + } + desc.storeAction = desc.texture.sampleCount > 1 ? MTLStoreActionMultisampleResolve : MTLStoreActionStore; + } + } + + //TODO: optimize store actions use discard flag + RenderPassDepthAttachmentDescriptor depthAttachment = renderPassDescriptor.depthAttachment; + if (NULL != depthAttachment.texture) + { + depthAttachment.clearDepth = clr.m_depth; + depthAttachment.loadAction = 0 != (BGFX_CLEAR_DEPTH & clr.m_flags) + ? MTLLoadActionClear + : MTLLoadActionLoad + ; + depthAttachment.storeAction = NULL != m_backBufferColorMSAA ? MTLStoreActionDontCare : MTLStoreActionStore; + } + + RenderPassStencilAttachmentDescriptor stencilAttachment = renderPassDescriptor.stencilAttachment; + if (NULL != stencilAttachment.texture) + { + stencilAttachment.clearStencil = clr.m_stencil; + stencilAttachment.loadAction = 0 != (BGFX_CLEAR_STENCIL & clr.m_flags) + ? MTLLoadActionClear + : MTLLoadActionLoad + ; + stencilAttachment.storeAction = NULL != m_backBufferColorMSAA ? MTLStoreActionDontCare : MTLStoreActionStore; + } } else { - float rr = clr.m_index[0]*1.0f/255.0f; - float gg = clr.m_index[1]*1.0f/255.0f; - float bb = clr.m_index[2]*1.0f/255.0f; - float aa = clr.m_index[3]*1.0f/255.0f; - colorAttachment0.clearColor = MTLClearColorMake(rr, gg, bb, aa); - } + for(uint32_t ii = 0; ii < g_caps.maxFBAttachments; ++ii) + { + MTLRenderPassColorAttachmentDescriptor* desc = renderPassDescriptor.colorAttachments[ii]; + if ( desc.texture != NULL) + desc.loadAction = MTLLoadActionLoad; + } - colorAttachment0.loadAction = MTLLoadActionClear; - } - else - { - colorAttachment0.loadAction = MTLLoadActionLoad; - } + //TODO: optimize store actions use discard flag + RenderPassDepthAttachmentDescriptor depthAttachment = renderPassDescriptor.depthAttachment; + if (NULL != depthAttachment.texture) + { + depthAttachment.loadAction = MTLLoadActionLoad; + depthAttachment.storeAction = MTLStoreActionStore; + } - //TODO: optimize store actions use discard flag - RenderPassDepthAttachmentDescriptor depthAttachment = renderPassDescriptor.depthAttachment; - if (NULL != depthAttachment.texture) - { - depthAttachment.clearDepth = clr.m_depth; - depthAttachment.loadAction = 0 != (BGFX_CLEAR_DEPTH & clr.m_flags) - ? MTLLoadActionClear - : MTLLoadActionLoad - ; - depthAttachment.storeAction = MTLStoreActionStore; - } + RenderPassStencilAttachmentDescriptor stencilAttachment = renderPassDescriptor.stencilAttachment; + if (NULL != stencilAttachment.texture) + { + stencilAttachment.loadAction = MTLLoadActionLoad; + stencilAttachment.storeAction = MTLStoreActionStore; + } + } - RenderPassStencilAttachmentDescriptor stencilAttachment = renderPassDescriptor.stencilAttachment; - if (NULL != stencilAttachment.texture) - { - stencilAttachment.clearStencil = clr.m_stencil; - stencilAttachment.loadAction = 0 != (BGFX_CLEAR_STENCIL & clr.m_flags) - ? MTLLoadActionClear - : MTLLoadActionLoad - ; - stencilAttachment.storeAction = MTLStoreActionStore; + rce = m_commandBuffer.renderCommandEncoderWithDescriptor(renderPassDescriptor); + m_renderCommandEncoder = rce; + m_renderCommandEncoderFrameBufferHandle = fbh; + MTL_RELEASE(renderPassDescriptor); } - - if (0 != m_renderCommandEncoder) + else { - m_renderCommandEncoder.endEncoding(); + clearWithRenderPass = false; } - rce = m_commandBuffer.renderCommandEncoderWithDescriptor(renderPassDescriptor); - m_renderCommandEncoder = rce; - MTL_RELEASE(renderPassDescriptor); - - //TODO: REMOVE THIS!!!! - // TERRIBLE HACK TO SUPPRESS DEBUG LAYER WARNING ABOUT MISSING TEXTURE/SAMPLER AT 0 in 20-nanovg - m_renderCommandEncoder.setFragmentTexture(zeroTexture, 0); - m_renderCommandEncoder.setFragmentSamplerState(getSamplerState(0), 0); - rce.setTriangleFillMode(wireframe? MTLTriangleFillModeLines : MTLTriangleFillModeFill); if (BX_ENABLED(BGFX_CONFIG_DEBUG_MTL) ) @@ -2439,7 +3189,7 @@ namespace bgfx { namespace mtl rce.setViewport(vp); if (BGFX_CLEAR_NONE != (clr.m_flags & BGFX_CLEAR_MASK) - && !fullscreenRect) + && !clearWithRenderPass) { clearQuad(_clearQuad, viewState.m_rect, clr, _render->m_colorPalette); } @@ -2527,6 +3277,9 @@ namespace bgfx { namespace mtl rc.y = scissorRect.m_y; rc.width = scissorRect.m_width; rc.height = scissorRect.m_height; + + if ( rc.width == 0 || rc.height == 0 ) + continue; } rce.setScissorRect(rc); } @@ -2541,7 +3294,6 @@ namespace bgfx { namespace mtl | BGFX_STATE_CULL_MASK | BGFX_STATE_ALPHA_REF_MASK | BGFX_STATE_PT_MASK -// | BGFX_STATE_POINT_SIZE_MASK ) & changedFlags) { if (BGFX_STATE_CULL_MASK & changedFlags) @@ -2583,7 +3335,7 @@ namespace bgfx { namespace mtl rendererUpdateUniforms(this, _render->m_uniformBuffer, draw.m_constBegin, draw.m_constEnd); if (key.m_program != programIdx - || (BGFX_STATE_BLEND_MASK|BGFX_STATE_BLEND_EQUATION_MASK|BGFX_STATE_ALPHA_WRITE|BGFX_STATE_RGB_WRITE|BGFX_STATE_BLEND_INDEPENDENT|BGFX_STATE_MSAA) & changedFlags + || (BGFX_STATE_BLEND_MASK|BGFX_STATE_BLEND_EQUATION_MASK|BGFX_STATE_ALPHA_WRITE|BGFX_STATE_RGB_WRITE|BGFX_STATE_BLEND_INDEPENDENT|BGFX_STATE_MSAA|BGFX_STATE_BLEND_ALPHA_TO_COVERAGE) & changedFlags || currentState.m_vertexBuffer.idx != draw.m_vertexBuffer.idx || currentState.m_vertexDecl.idx != draw.m_vertexDecl.idx || currentState.m_instanceDataStride != draw.m_instanceDataStride @@ -2603,12 +3355,18 @@ namespace bgfx { namespace mtl ProgramMtl& program = m_program[programIdx]; currentProgram = &program; - uint16_t handle = draw.m_vertexBuffer.idx; - const VertexBufferMtl& vb = m_vertexBuffers[handle]; - VertexDeclHandle decl; - decl.idx = !isValid(vb.m_decl) ? draw.m_vertexDecl.idx : vb.m_decl.idx; + RenderPipelineState pipelineState = NULL; + + if ( isValid(draw.m_vertexBuffer) ) + { + uint16_t handle = draw.m_vertexBuffer.idx; + const VertexBufferMtl& vb = m_vertexBuffers[handle]; + VertexDeclHandle decl; + decl.idx = !isValid(vb.m_decl) ? draw.m_vertexDecl.idx : vb.m_decl.idx; + + pipelineState = program.getRenderPipelineState(newFlags, draw.m_rgba, fbh, decl, draw.m_instanceDataStride/16); + } - RenderPipelineState pipelineState = program.getRenderPipelineState(newFlags, draw.m_rgba, fbh, decl, draw.m_instanceDataStride/16); if (NULL == pipelineState ) { //call with invalid program currentProgram = NULL; @@ -2665,6 +3423,16 @@ namespace bgfx { namespace mtl } { + uint32_t usedVertexSamplerStages = 0; + uint32_t usedFragmentSamplerStages = 0; + + if (invalidHandle != programIdx) + { + ProgramMtl& program = m_program[programIdx]; + usedVertexSamplerStages = program.m_usedVertexSamplerStages; + usedFragmentSamplerStages = program.m_usedFragmentSamplerStages; + } + for (uint8_t stage = 0; stage < BGFX_CONFIG_MAX_TEXTURE_SAMPLERS; ++stage) { const Binding& sampler = draw.m_bind[stage]; @@ -2676,7 +3444,9 @@ namespace bgfx { namespace mtl if (invalidHandle != sampler.m_idx) { TextureMtl& texture = m_textures[sampler.m_idx]; - texture.commit(stage, sampler.m_un.m_draw.m_textureFlags); + texture.commit(stage, (usedVertexSamplerStages&(1<<stage))!=0, + (usedFragmentSamplerStages&(1<<stage))!=0, + sampler.m_un.m_draw.m_textureFlags); } } @@ -2703,12 +3473,12 @@ namespace bgfx { namespace mtl const VertexDecl& vertexDecl = m_vertexDecls[decl]; uint32_t offset = draw.m_startVertex * vertexDecl.getStride(); - rce.setVertexBuffer(vb.m_buffer, offset, 1); + rce.setVertexBuffer(vb.getBuffer(), offset, 1); if (isValid(draw.m_instanceDataBuffer) ) { const VertexBufferMtl& inst = m_vertexBuffers[draw.m_instanceDataBuffer.idx]; - rce.setVertexBuffer(inst.m_buffer, draw.m_instanceDataOffset, 2); + rce.setVertexBuffer(inst.getBuffer(), draw.m_instanceDataOffset, 2); } } } @@ -2753,7 +3523,7 @@ namespace bgfx { namespace mtl numInstances = draw.m_numInstances; numPrimsRendered = numPrimsSubmitted*draw.m_numInstances; - rce.drawIndexedPrimitives(prim.m_type, numIndices, indexType, ib.m_buffer, 0, draw.m_numInstances); + rce.drawIndexedPrimitives(prim.m_type, numIndices, indexType, ib.getBuffer(), 0, draw.m_numInstances); } else if (prim.m_min <= draw.m_numIndices) { @@ -2763,7 +3533,7 @@ namespace bgfx { namespace mtl numInstances = draw.m_numInstances; numPrimsRendered = numPrimsSubmitted*draw.m_numInstances; - rce.drawIndexedPrimitives(prim.m_type, numIndices, indexType, ib.m_buffer, draw.m_startIndex * indexSize,numInstances); + rce.drawIndexedPrimitives(prim.m_type, numIndices, indexType, ib.getBuffer(), draw.m_startIndex * indexSize,numInstances); } } else @@ -2797,7 +3567,8 @@ namespace bgfx { namespace mtl if (0 < _render->m_num) { captureElapsed = -bx::getHPCounter(); - //capture(); + capture(); + rce = m_renderCommandEncoder; //TODO: ugly, can create new encoder captureElapsed += bx::getHPCounter(); } } @@ -2814,31 +3585,44 @@ namespace bgfx { namespace mtl elapsed += now; static int64_t last = now; + + Stats& perfStats = _render->m_perfStats; + perfStats.cpuTimeBegin = last; + int64_t frameTime = now - last; last = now; static int64_t min = frameTime; static int64_t max = frameTime; - min = min > frameTime ? frameTime : min; - max = max < frameTime ? frameTime : max; + min = bx::int64_min(min, frameTime); + max = bx::int64_max(max, frameTime); - if (_render->m_debug & (BGFX_DEBUG_IFH|BGFX_DEBUG_STATS) ) + static uint32_t maxGpuLatency = 0; + static double maxGpuElapsed = 0.0f; + double elapsedGpuMs = 0.0; + + do { - rce.pushDebugGroup("debugstats"); + double toGpuMs = 1000.0 / double(m_gpuTimer.m_frequency); + elapsedGpuMs = m_gpuTimer.m_elapsed * toGpuMs; + maxGpuElapsed = elapsedGpuMs > maxGpuElapsed ? elapsedGpuMs : maxGpuElapsed; + } + while (m_gpuTimer.get() ); + + maxGpuLatency = bx::uint32_imax(maxGpuLatency, m_gpuTimer.m_control.available()-1); + + const int64_t timerFreq = bx::getHPFrequency(); - static uint32_t maxGpuLatency = 0; - static double maxGpuElapsed = 0.0f; -// double elapsedGpuMs = 0.0; + perfStats.cpuTimeEnd = now; + perfStats.cpuTimerFreq = timerFreq; + perfStats.gpuTimeBegin = m_gpuTimer.m_begin; + perfStats.gpuTimeEnd = m_gpuTimer.m_end; + perfStats.gpuTimerFreq = m_gpuTimer.m_frequency; -// m_gpuTimer.end(); -// -// while (m_gpuTimer.get() ) -// { -// double toGpuMs = 1000.0 / double(m_gpuTimer.m_frequency); -// elapsedGpuMs = m_gpuTimer.m_elapsed * toGpuMs; -// maxGpuElapsed = elapsedGpuMs > maxGpuElapsed ? elapsedGpuMs : maxGpuElapsed; -// } -// maxGpuLatency = bx::uint32_imax(maxGpuLatency, m_gpuTimer.m_control.available()-1); + rce.setTriangleFillMode(MTLTriangleFillModeFill); + if (_render->m_debug & (BGFX_DEBUG_IFH|BGFX_DEBUG_STATS) ) + { + rce.pushDebugGroup("debugstats"); TextVideoMem& tvm = m_textVideoMem; @@ -2916,6 +3700,7 @@ namespace bgfx { namespace mtl } blit(this, _textVideoMemBlitter, tvm); + rce = m_renderCommandEncoder; //TODO: ugly, blit can create encoder rce.popDebugGroup(); } @@ -2924,15 +3709,35 @@ namespace bgfx { namespace mtl rce.pushDebugGroup("debugtext"); blit(this, _textVideoMemBlitter, _render->m_textVideoMem); + rce = m_renderCommandEncoder; //TODO: ugly, blit can create encoder rce.popDebugGroup(); } - //TODO: REMOVE THIS - TEMPORARY HACK - release(zeroTexture); - rce.endEncoding(); m_renderCommandEncoder = 0; + m_renderCommandEncoderFrameBufferHandle.idx = invalidHandle; + + + if ( m_screenshotTarget ) + { + RenderPassDescriptor renderPassDescriptor = newRenderPassDescriptor(); + renderPassDescriptor.colorAttachments[0].texture = currentDrawable().texture; + renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore; + + rce = m_commandBuffer.renderCommandEncoderWithDescriptor(renderPassDescriptor); + + rce.setCullMode(MTLCullModeNone); + + rce.setRenderPipelineState(m_screenshotBlitRenderPipelineState); + + rce.setFragmentSamplerState(getSamplerState(BGFX_TEXTURE_U_CLAMP|BGFX_TEXTURE_V_CLAMP|BGFX_TEXTURE_MIN_POINT|BGFX_TEXTURE_MAG_POINT|BGFX_TEXTURE_MIP_POINT), 0); + rce.setFragmentTexture(m_screenshotTarget, 0); + + rce.drawPrimitives(MTLPrimitiveTypeTriangle, 0, 3, 1); + + rce.endEncoding(); + } } } /* namespace mtl */ } // namespace bgfx |