Metal: Fixed occlusion queries and depth texture data, added HDR10 and unretained command buffers. Closes #2356, #2017, #1827, #2974, #3642. (#4060)

This commit is contained in:
Branimir Karadžić
2026-10-17 00:10:37 -07:00
committed by GitHub
parent 32966cfc7c
commit 227eac0d75
11 changed files with 559 additions and 72 deletions

View File

@@ -209,6 +209,53 @@ Linux
make linux-release64
Android
~~~~~~~
With ``ANDROID_NDK_ROOT`` set (see `Prerequisites <#prerequisites>`_):
::
make android-arm64-release
Libraries are in ``.build/android-arm64/bin``. Link ``libbgfxRelease.a``, ``libbimgRelease.a`` and
``libbxRelease.a`` into your native library, and add ``bgfx/include``, ``bimg/include`` and
``bx/include`` to its include paths.
iOS
~~~
For the command line:
::
make ios-arm64-release
Libraries are in ``.build/ios-arm64/bin``. To use them in your own Xcode project:
1. Link ``libbgfxRelease.a``, ``libbimgRelease.a`` and ``libbxRelease.a``.
2. Link the ``Foundation``, ``CoreGraphics``, ``QuartzCore``, ``UIKit`` and ``Metal`` frameworks,
plus ``VideoToolbox``, ``CoreMedia`` and ``CoreVideo`` for video decode.
3. Add ``bgfx/include``, ``bimg/include`` and ``bx/include`` to *Header Search Paths*.
4. Pass a ``CAMetalLayer``, or an ``MTKView``, as ``bgfx::PlatformData::nwh``.
Or build ``src/amalgamated.mm`` directly in your project instead of linking the libraries
(see `Amalgamated Build <#amalgamated-build>`_).
For the Xcode IDE:
::
../bx/tools/bin/darwin/genie --with-combined-examples --xcode=ios xcode9
open .build/projects/xcode9-ios/bgfx.xcworkspace
For tvOS, generate gmake projects with ``--gcc=tvos-arm64``:
::
../bx/tools/bin/darwin/genie --gcc=tvos-arm64 gmake
make -R -C .build/projects/gmake-tvos-arm64 config=release
For more options, see `General (directly with GENie) <#general-directly-with-genie>`_.
WinRT / UWP

View File

@@ -724,3 +724,14 @@ https://sluglibrary.com/
.. figure:: https://github.com/bkaradzic/bgfx/raw/master/examples/51-gpufont/screenshot.png
:alt: example-51-gpufont
`55-hdr10 <https://github.com/bkaradzic/bgfx/tree/master/examples/55-hdr10>`__
------------------------------------------------------------------------------
HDR10 test pattern. Gray steps at 0 to 10000 nits and BT.2020 color bars at 203 nits, PQ encoded into
an RGB10A2 back buffer, or as multiples of SDR white in an RGBA16F extended linear back buffer. HDR10
can be toggled to compare HDR output against SDR on an HDR capable display.
Reference(s):
- `ITU-R BT.2100 <https://www.itu.int/rec/R-REC-BT.2100>`__.
- `ITU-R BT.2408 <https://www.itu.int/pub/R-REP-BT.2408>`__.

309
examples/55-hdr10/hdr10.cpp Normal file
View File

@@ -0,0 +1,309 @@
/*
* Copyright 2011-2026 Branimir Karadzic. All rights reserved.
* License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
*/
#include "common.h"
#include "bgfx_utils.h"
#include "imgui/imgui.h"
#include <bx/math.h>
namespace
{
constexpr uint32_t kSwapChainFlags = 0
| BGFX_SWAP_CHAIN_FULLSCREEN_MASK
| BGFX_SWAP_CHAIN_MSAA_MASK
| BGFX_SWAP_CHAIN_SRGB_BACKBUFFER
| BGFX_SWAP_CHAIN_HDR10
| BGFX_SWAP_CHAIN_HIDPI
| BGFX_SWAP_CHAIN_TRANSPARENT_BACKBUFFER
;
constexpr uint32_t kNumGray = 7;
constexpr uint32_t kNumBars = 6;
constexpr float kGrayNits[kNumGray] = { 0.0f, 100.0f, 203.0f, 400.0f, 1000.0f, 4000.0f, 10000.0f };
constexpr float kGrayScale[kNumGray] = { 0.0f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f, 16.0f };
constexpr float kBarNits = 203.0f;
constexpr float kBarColor[kNumBars][3] =
{
{ 1.0f, 0.0f, 0.0f },
{ 0.0f, 1.0f, 0.0f },
{ 0.0f, 0.0f, 1.0f },
{ 0.0f, 1.0f, 1.0f },
{ 1.0f, 0.0f, 1.0f },
{ 1.0f, 1.0f, 0.0f },
};
constexpr const char* kBarName[kNumBars] = { "R", "G", "B", "C", "M", "Y" };
float encodePq(float _nits)
{
constexpr float m1 = 2610.0f/16384.0f;
constexpr float m2 = 2523.0f/4096.0f*128.0f;
constexpr float c1 = 3424.0f/4096.0f;
constexpr float c2 = 2413.0f/4096.0f*32.0f;
constexpr float c3 = 2392.0f/4096.0f*32.0f;
const float yy = bx::pow(bx::clamp(_nits/10000.0f, 0.0f, 1.0f), m1);
return bx::pow( (c1 + c2*yy) / (1.0f + c3*yy), m2);
}
class ExampleHdr10 : public entry::AppI
{
public:
ExampleHdr10(const char* _name, const char* _description, const char* _url)
: entry::AppI(_name, _description, _url)
{
}
void init(int32_t _argc, const char* const* _argv, uint32_t _width, uint32_t _height) override
{
Args args(_argc, _argv);
m_width = _width;
m_height = _height;
m_debug = BGFX_DEBUG_NONE;
m_reset = BGFX_RESET_VSYNC|BGFX_RESET_HDR10;
m_pq = true;
bgfx::Init init;
init.type = args.m_type;
init.vendorId = args.m_pciId;
init.swapChain.nwh = entry::getNativeWindowHandle(entry::kDefaultWindowHandle);
init.swapChain.ndt = entry::getNativeDisplayHandle();
init.platformData.type = entry::getNativeWindowHandleType();
init.swapChain.width = m_width;
init.swapChain.height = m_height;
init.swapChain.formatColor = getFormat();
init.reset = m_reset;
bgfx::init(init);
bgfx::setDebug(m_debug);
m_appliedWidth = m_width;
m_appliedHeight = m_height;
m_appliedReset = m_reset;
m_appliedPq = m_pq;
imguiCreate();
}
int shutdown() override
{
imguiDestroy();
bgfx::shutdown();
return 0;
}
bgfx::TextureFormat::Enum getFormat() const
{
return m_pq
? bgfx::TextureFormat::RGB10A2
: bgfx::TextureFormat::RGBA16F
;
}
void setPatch(uint8_t _index, float _red, float _green, float _blue)
{
const float rgba[4] = { _red, _green, _blue, 1.0f };
bgfx::setPaletteColor(_index, rgba);
}
float encode(float _nits, float _scale) const
{
return m_pq ? encodePq(_nits) : _scale;
}
void imguiLabel(float _x, float _y, const char* _id, const char* _text)
{
ImGui::SetNextWindowPos(ImVec2(_x, _y), 0, ImVec2(0.5f, 1.0f) );
ImGui::SetNextWindowBgAlpha(0.5f);
ImGui::Begin(_id, NULL, 0
| ImGuiWindowFlags_NoInputs
| ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoSavedSettings
| ImGuiWindowFlags_AlwaysAutoResize
);
ImGui::Text("%s", _text);
ImGui::End();
}
void submitPatch(bgfx::ViewId _view, uint8_t _index, uint16_t _x, uint16_t _y, uint16_t _width, uint16_t _height)
{
bgfx::setViewRect(_view, _x, _y, _width, _height);
bgfx::setViewClear(_view, BGFX_CLEAR_COLOR, 1.0f, 0, _index);
bgfx::touch(_view);
}
bool update() override
{
if (entry::processEvents(m_width, m_height, m_debug, m_reset, &m_mouseState) )
{
return false;
}
imguiBeginFrame(m_mouseState.m_mx
, m_mouseState.m_my
, (m_mouseState.m_buttons[entry::MouseButton::Left ] ? IMGUI_MBUT_LEFT : 0)
| (m_mouseState.m_buttons[entry::MouseButton::Right ] ? IMGUI_MBUT_RIGHT : 0)
| (m_mouseState.m_buttons[entry::MouseButton::Middle] ? IMGUI_MBUT_MIDDLE : 0)
, m_mouseState.m_mz
, uint16_t(m_width)
, uint16_t(m_height)
);
showExampleDialog(this);
bool hdr10 = 0 != (m_reset & BGFX_RESET_HDR10);
ImGui::SetNextWindowPos(ImVec2(m_width - m_width / 5.0f - 10.0f, 10.0f), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(m_width / 5.0f, m_height / 6.0f), ImGuiCond_FirstUseEver);
ImGui::Begin("Settings", NULL, 0);
if (ImGui::RadioButton("PQ, RGB10A2", m_pq) )
{
m_pq = true;
}
if (ImGui::RadioButton("Linear, RGBA16F", !m_pq) )
{
m_pq = false;
}
ImGui::Checkbox("HDR10", &hdr10);
ImGui::End();
const float labelRowHeight = float(m_height*2/5);
char id[32];
char text[128];
for (uint32_t ii = 0; ii < kNumGray; ++ii)
{
if (m_pq)
{
bx::snprintf(text, BX_COUNTOF(text), "%.0f nits", kGrayNits[ii]);
}
else
{
bx::snprintf(text, BX_COUNTOF(text), "%.1fx white", kGrayScale[ii]);
}
bx::snprintf(id, BX_COUNTOF(id), "gray%d", ii);
imguiLabel(m_width*(ii + 0.5f)/kNumGray, labelRowHeight - 10.0f, id, text);
}
for (uint32_t ii = 0; ii < kNumBars; ++ii)
{
if (m_pq)
{
bx::snprintf(text, BX_COUNTOF(text), "%s %.0f nits", kBarName[ii], kBarNits);
}
else
{
bx::snprintf(text, BX_COUNTOF(text), "%s 1.0x white", kBarName[ii]);
}
bx::snprintf(id, BX_COUNTOF(id), "bar%d", ii);
imguiLabel(m_width*(ii + 0.5f)/kNumBars, 2.0f*labelRowHeight - 10.0f, id, text);
}
const bgfx::Caps* caps = bgfx::getCaps();
bx::snprintf(text, BX_COUNTOF(text), "BGFX_CAPS_HDR10: %s, back buffer: %s"
, 0 != (caps->supported & BGFX_CAPS_HDR10) ? "supported" : "not supported"
, m_pq ? "RGB10A2, PQ BT.2020" : "RGBA16F, extended linear sRGB"
);
imguiLabel(m_width*0.5f, m_height - 10.0f, "status", text);
imguiEndFrame();
m_reset = (m_reset & ~BGFX_RESET_HDR10) | (hdr10 ? BGFX_RESET_HDR10 : 0);
if (m_width != m_appliedWidth
|| m_height != m_appliedHeight
|| m_reset != m_appliedReset
|| m_pq != m_appliedPq)
{
m_appliedWidth = m_width;
m_appliedHeight = m_height;
m_appliedReset = m_reset;
m_appliedPq = m_pq;
bgfx::SwapChain swapChain;
swapChain.width = m_width;
swapChain.height = m_height;
swapChain.flags = m_reset & kSwapChainFlags;
swapChain.formatColor = getFormat();
bgfx::reset(m_reset & ~kSwapChainFlags, &swapChain);
}
bgfx::setViewRect(0, 0, 0, uint16_t(m_width), uint16_t(m_height) );
bgfx::setViewClear(0, BGFX_CLEAR_COLOR, 0x000000ff, 1.0f, 0);
bgfx::touch(0);
const uint16_t rowHeight = uint16_t(m_height*2/5);
for (uint32_t ii = 0; ii < kNumGray; ++ii)
{
const float value = encode(kGrayNits[ii], kGrayScale[ii]);
setPatch(uint8_t(ii), value, value, value);
const uint16_t x0 = uint16_t(m_width* ii /kNumGray);
const uint16_t x1 = uint16_t(m_width*(ii + 1)/kNumGray);
submitPatch(bgfx::ViewId(1 + ii), uint8_t(ii), x0, 0, x1 - x0, rowHeight);
}
for (uint32_t ii = 0; ii < kNumBars; ++ii)
{
const float on = encode(kBarNits, 1.0f);
setPatch(uint8_t(kNumGray + ii)
, kBarColor[ii][0]*on
, kBarColor[ii][1]*on
, kBarColor[ii][2]*on
);
const uint16_t x0 = uint16_t(m_width* ii /kNumBars);
const uint16_t x1 = uint16_t(m_width*(ii + 1)/kNumBars);
submitPatch(bgfx::ViewId(1 + kNumGray + ii), uint8_t(kNumGray + ii), x0, rowHeight, x1 - x0, rowHeight);
}
bgfx::frame();
return true;
}
entry::MouseState m_mouseState;
uint32_t m_width;
uint32_t m_height;
uint32_t m_debug;
uint32_t m_reset;
bool m_pq;
uint32_t m_appliedWidth;
uint32_t m_appliedHeight;
uint32_t m_appliedReset;
bool m_appliedPq;
};
} // namespace
ENTRY_IMPLEMENT_MAIN(
ExampleHdr10
, "55-hdr10"
, "HDR10 test pattern."
, "https://bkaradzic.github.io/bgfx/examples.html#hdr10"
);

View File

@@ -9,13 +9,7 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <QuartzCore/CAEAGLLayer.h>
#if __IPHONE_8_0 && !TARGET_IPHONE_SIMULATOR // check if sdk/target supports metal
# import <Metal/Metal.h>
# import <QuartzCore/CAMetalLayer.h>
# define HAS_METAL_SDK
#endif
#import <QuartzCore/CAMetalLayer.h>
#include <bgfx/bgfx.h>
@@ -190,21 +184,7 @@ using namespace entry;
+ (Class)layerClass
{
#ifdef HAS_METAL_SDK
static id<MTLDevice> device = NULL;
Class metalClass = NSClassFromString(@"CAMetalLayer"); //is metal runtime sdk available
if ( metalClass != nil)
{
device = MTLCreateSystemDefaultDevice(); // is metal supported on this device (is there a better way to do this - without creating device ?)
if (NULL != device)
{
[device retain];
return metalClass;
}
}
#endif
return [CAEAGLLayer class];
return [CAMetalLayer class];
}
- (id)initWithFrame:(CGRect)rect

View File

@@ -139,6 +139,9 @@ function bgfxProjectBase(_kind, _defines)
"-Wno-microsoft-const-init", -- default initialization of an object of const type '' without a user-provided default constructor is a Microsoft extension
}
configuration { "ios* or tvos* or xros*" }
buildoptions { "-x objective-c++" }
configuration { "osx*" }
buildoptions { "-x objective-c++" } -- additional build option for osx
linkoptions {

View File

@@ -403,9 +403,9 @@ function exampleProjectDefaults()
kind "ConsoleApp"
linkoptions {
"-framework CoreFoundation",
"-framework CoreGraphics",
"-framework Foundation",
"-framework IOKit",
"-framework OpenGLES",
"-framework QuartzCore",
"-framework UIKit",
"-weak_framework Metal",
@@ -591,6 +591,7 @@ or _OPTIONS["with-combined-examples"] then
, "52-layered"
, "53-sky2"
, "54-s2h"
, "55-hdr10"
)

View File

@@ -149,11 +149,15 @@ project ("geometryv")
kind "ConsoleApp"
linkoptions {
"-framework CoreFoundation",
"-framework CoreGraphics",
"-framework Foundation",
"-framework IOKit",
"-framework OpenGLES",
"-framework QuartzCore",
"-framework UIKit",
"-weak_framework Metal",
"-weak_framework VideoToolbox",
"-weak_framework CoreMedia",
"-weak_framework CoreVideo",
}
configuration { "xcode4", "ios" }

View File

@@ -209,11 +209,15 @@ project "texturev"
kind "ConsoleApp"
linkoptions {
"-framework CoreFoundation",
"-framework CoreGraphics",
"-framework Foundation",
"-framework IOKit",
"-framework OpenGLES",
"-framework QuartzCore",
"-framework UIKit",
"-weak_framework Metal",
"-weak_framework VideoToolbox",
"-weak_framework CoreMedia",
"-weak_framework CoreVideo",
}
configuration { "xcode*", "ios" }

View File

@@ -187,7 +187,7 @@ namespace bgfx
, 1
);
bimg::imageWriteTga(&writer, _width, _height, _pitch, _data, false, _yflip);
bimg::imageWriteTga(&writer, _width, _height, dstPitch, dst, false, _yflip);
bx::free(g_allocator, dst);
}

View File

@@ -674,10 +674,11 @@ BX_PRAGMA_DIAGNOSTIC_POP();
if (false
|| TextureFormat::BGRA8 == _fmt
|| TextureFormat::RGB10A2 == _fmt
|| TextureFormat::RGBA16F == _fmt
)
{
// CAMetalLayer accepts BGRA8Unorm(_sRGB) and RGBA16Float as drawable pixel formats.
// CAMetalLayer accepts BGRA8Unorm(_sRGB), RGB10A2Unorm and RGBA16Float as drawable pixel formats.
caps |= BGFX_CAPS_FORMAT_TEXTURE_BACKBUFFER;
}
@@ -1011,6 +1012,19 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
| BGFX_CAPS_VERTEX_ATTRIB_UINT10
);
#if BX_PLATFORM_OSX
if (__builtin_available(macOS 11.0, *) )
{
void* screen = MtlObjAccess::send<void*>(objc_lookUpClass("NSScreen"), sel_registerName("mainScreen") );
if (NULL != screen
&& 1.0 < MtlObjAccess::send<double>(screen, sel_registerName("maximumPotentialExtendedDynamicRangeColorComponentValue") ) )
{
g_caps.supported |= BGFX_CAPS_HDR10;
}
}
#endif // BX_PLATFORM_OSX
g_caps.supported |= (m_device->supportsFamily(MTL::GPUFamilyApple7) || m_device->supportsFamily(MTL::GPUFamilyMac2) )
? BGFX_CAPS_PRIMITIVE_ID
: 0
@@ -2048,12 +2062,12 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
if (NULL != frameBuffer.m_swapChain
&& frameBuffer.m_swapChain->m_drawableTexture)
{
MTL_RELEASE_I(frameBuffer.m_swapChain->m_drawableTexture);
MTL_RELEASE_DEFERRED(frameBuffer.m_swapChain->m_drawableTexture);
if (NULL != frameBuffer.m_swapChain->m_drawable)
{
presentCommandBuffer->presentDrawable( (MTL::Drawable*)frameBuffer.m_swapChain->m_drawable);
MTL_RELEASE_I(frameBuffer.m_swapChain->m_drawable);
MTL_RELEASE_DEFERRED(frameBuffer.m_swapChain->m_drawable);
}
}
}
@@ -2143,7 +2157,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
if (NULL == m_screenshotBlitRenderPipelineState
|| m_screenshotBlitPixelFormat != _pixelFormat)
{
MTL_RELEASE_I(m_screenshotBlitRenderPipelineState);
MTL_RELEASE_DEFERRED(m_screenshotBlitRenderPipelineState);
reset(m_renderPipelineDescriptor);
m_renderPipelineDescriptor->colorAttachments()->object(0)->setPixelFormat(_pixelFormat);
@@ -2437,7 +2451,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
, MTL::Origin::Make(_rect.m_x, _rect.m_y, 0)
);
MTL_RELEASE_I(buffer);
MTL_RELEASE_DEFERRED(buffer);
}
endBlitEncoding();
@@ -3875,7 +3889,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
void ChunkedScratchBufferMtl::destroyChunk(ChunkMtl& _chunk)
{
MTL_RELEASE_W(_chunk.buffer, 0);
MTL_RELEASE_DEFERRED(_chunk.buffer);
}
void ChunkedScratchBufferMtl::flushChunk(ChunkMtl& _chunk, uint32_t _size)
@@ -4569,7 +4583,33 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
;
}
m_ptr->replaceRegion(region, lod, side, data, bytesPerRow, bytesPerImage);
if (MTL::StorageModePrivate == m_ptr->storageMode() )
{
const uint32_t size = convert
? width*height*depth*4
: mip.m_size
;
MTL::Buffer* staging = s_renderMtl->m_device->newBuffer(data, size, MTL::ResourceCPUCacheModeDefaultCache);
s_renderMtl->getBlitCommandEncoder()->copyFromBuffer(
staging
, 0
, bytesPerRow
, bytesPerImage
, MTL::Size::Make(width, height, depth)
, m_ptr
, side
, lod
, MTL::Origin::Make(0, 0, 0)
);
s_renderMtl->m_cmd.release(staging);
}
else
{
m_ptr->replaceRegion(region, lod, side, data, bytesPerRow, bytesPerImage);
}
}
width >>= 1;
@@ -4594,18 +4634,18 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
if (0 == (m_flags & BGFX_SAMPLER_INTERNAL_SHARED) )
{
MTL_RELEASE_W(m_ptr, 0);
MTL_RELEASE_DEFERRED(m_ptr);
}
MTL_RELEASE_W(m_ptrMsaa, 0);
MTL_RELEASE_W(m_ptrStencil, 0);
MTL_RELEASE_W(m_ptrAlt, 0);
MTL_RELEASE_W(m_ptrMsaaAlt, 0);
MTL_RELEASE_DEFERRED(m_ptrMsaa);
MTL_RELEASE_DEFERRED(m_ptrStencil);
MTL_RELEASE_DEFERRED(m_ptrAlt);
MTL_RELEASE_DEFERRED(m_ptrMsaaAlt);
for (stl::unordered_map<uint64_t, MTL::Texture*>::iterator it = m_ptrViews.begin(), itEnd = m_ptrViews.end(); it != itEnd; ++it)
{
MTL::Texture* view = it->second;
MTL_RELEASE_W(view, 0);
MTL_RELEASE_DEFERRED(view);
}
m_ptrViews.clear();
}
@@ -4729,7 +4769,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
);
MTL_RELEASE(desc, 0);
MTL_RELEASE(tempTexture, 1);
MTL_RELEASE_DEFERRED(tempTexture);
}
if (NULL != temp)
@@ -4958,10 +4998,10 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
SwapChainMtl::~SwapChainMtl()
{
MTL_RELEASE(m_metalLayer, 2);
MTL_RELEASE(m_drawable, 0);
MTL_RELEASE_DEFERRED(m_drawable);
MTL_RELEASE(m_drawableTexture, 0);
MTL_RELEASE(m_screenshotTarget, 0);
MTL_RELEASE_DEFERRED(m_drawableTexture);
MTL_RELEASE_DEFERRED(m_screenshotTarget);
releaseBackBuffer();
}
@@ -5130,11 +5170,11 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
}
else
{
MTL_RELEASE(m_backBufferStencil, m_backBufferDepth == m_backBufferStencil ? 1 : 0);
MTL_RELEASE_W(m_backBufferDepth, 0);
MTL_RELEASE_DEFERRED(m_backBufferStencil);
MTL_RELEASE_DEFERRED(m_backBufferDepth);
}
MTL_RELEASE_W(m_backBufferColorMsaa, 0);
MTL_RELEASE_DEFERRED(m_backBufferColorMsaa);
}
uint32_t SwapChainMtl::resize(const SwapChain& _desc)
@@ -5174,6 +5214,33 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
: s_textureFormat[formatColor].fmt) )
;
const bool hdr10 = 0 != (_desc.flags & BGFX_SWAP_CHAIN_HDR10);
if (hdr10
|| m_hdr10)
{
if (__builtin_available(macOS 11.0, iOS 16.0, tvOS 16.0, macCatalyst 16.0, VISION_OS_MINIMUM *) )
{
CFStringRef colorSpaceName = !hdr10 ? NULL
: TextureFormat::RGB10A2 == formatColor ? kCGColorSpaceITUR_2100_PQ
: TextureFormat::RGBA16F == formatColor ? kCGColorSpaceExtendedLinearSRGB
: NULL
;
CGColorSpaceRef colorSpace = NULL != colorSpaceName
? CGColorSpaceCreateWithName(colorSpaceName)
: NULL
;
MtlObjAccess::send<void>(m_metalLayer, sel_registerName("setColorspace:"), colorSpace);
MtlObjAccess::send<void>(m_metalLayer, sel_registerName("setWantsExtendedDynamicRangeContent:"), NULL != colorSpace);
CGColorSpaceRelease(colorSpace);
}
m_hdr10 = hdr10;
}
const CGSize actualSize = m_metalLayer->drawableSize();
BX_WARN(true
&& uint32_t(actualSize.width) == _desc.width
@@ -5506,18 +5573,38 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
{
NS::AutoreleasePool* pool = NS::AutoreleasePool::alloc()->init();
for (ReleaseArray::iterator it = m_release.begin(), itEnd = m_release.end(); it != itEnd; ++it)
{
MTL_RELEASE_I(it->ptr);
}
m_release.clear();
MTL_RELEASE(m_commandQueue, 0);
pool->release();
}
}
void deferredRelease(NS::Object* _obj)
{
if (NULL != s_renderMtl
&& NULL != s_renderMtl->m_cmd.m_commandQueue)
{
s_renderMtl->m_cmd.release(_obj);
}
else
{
_obj->release();
}
}
MTL::CommandBuffer* CommandQueueMtl::alloc()
{
{
NS::AutoreleasePool* pool = NS::AutoreleasePool::alloc()->init();
m_activeCommandBuffer = m_commandQueue->commandBuffer();
m_activeCommandBuffer = m_commandQueue->commandBufferWithUnretainedReferences();
retain(m_activeCommandBuffer);
pool->release();
@@ -5531,6 +5618,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
if (queue)
{
bx::atomicFetchAndAdd<uint32_t>(&queue->m_completedFrames, 1);
queue->m_framesSemaphore.post();
}
}
@@ -5541,7 +5629,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
{
if (_endFrame)
{
m_releaseWriteIndex = (m_releaseWriteIndex + 1) % BGFX_CONFIG_MAX_FRAME_LATENCY;
++m_submittedFrames;
m_activeCommandBuffer->addCompletedHandler(
MTL::HandlerFunction([this](MTL::CommandBuffer*) { commandBufferFinishedCallback(this); })
);
@@ -5588,22 +5676,23 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
void CommandQueueMtl::release(NS::Object* _ptr)
{
m_release[m_releaseWriteIndex].push_back(_ptr);
m_release.push_back({ _ptr, m_submittedFrames });
}
void CommandQueueMtl::consume()
{
m_framesSemaphore.wait();
m_releaseReadIndex = (m_releaseReadIndex + 1) % BGFX_CONFIG_MAX_FRAME_LATENCY;
ResourceArray& ra = m_release[m_releaseReadIndex];
const uint32_t completedFrames = bx::atomicLoad(&m_completedFrames);
for (ResourceArray::iterator it = ra.begin(), itEnd = ra.end(); it != itEnd; ++it)
ReleaseArray::iterator it = m_release.begin();
for (ReleaseArray::iterator itEnd = m_release.end(); it != itEnd && 0 < int32_t(completedFrames - it->frame); ++it)
{
MTL_RELEASE_I(*it);
MTL_RELEASE_I(it->ptr);
}
ra.clear();
m_release.erase(m_release.begin(), it);
}
static constexpr uint64_t kNanosecondsPerSecond = UINT64_C(1000000000);
@@ -5681,7 +5770,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
{
for (uint32_t ii = 0; ii < BX_COUNTOF(m_sampling); ++ii)
{
MTL_RELEASE_W(m_sampling[ii].m_sampleBuffer, 0);
MTL_RELEASE_DEFERRED(m_sampling[ii].m_sampleBuffer);
}
}
@@ -5877,7 +5966,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
void OcclusionQueryMTL::postReset()
{
MTL_RELEASE_W(m_buffer, 0);
MTL_RELEASE_DEFERRED(m_buffer);
}
void OcclusionQueryMTL::preReset()
@@ -5894,8 +5983,9 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
Query& query = m_query[m_control.m_current];
query.m_handle = _handle;
query.m_frame = s_renderMtl->m_cmd.m_submittedFrames;
uint32_t offset = _handle.idx * 8;
_rce->setVisibilityResultMode( (MTL::VisibilityResultMode)MTL::VisibilityResultModeBoolean, offset);
_rce->setVisibilityResultMode( (MTL::VisibilityResultMode)MTL::VisibilityResultModeCounting, offset);
}
void OcclusionQueryMTL::end(MTL::RenderCommandEncoder*& _rce)
@@ -5908,12 +5998,19 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
void OcclusionQueryMTL::resolve(Frame* _render, bool _wait)
{
BX_UNUSED(_wait);
const uint32_t completedFrames = bx::atomicLoad(&s_renderMtl->m_cmd.m_completedFrames);
while (0 != m_control.getNumUsed() )
{
Query& query = m_query[m_control.m_read];
if (!_wait
&& isValid(query.m_handle)
&& int32_t(completedFrames - query.m_frame) <= 0)
{
break;
}
if (isValid(query.m_handle) )
{
uint64_t result = ( (uint64_t*)m_buffer->contents() )[query.m_handle.idx];
@@ -6161,7 +6258,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
&& (swapChain->m_screenshotTarget->width() != width
|| swapChain->m_screenshotTarget->height() != height) )
{
MTL_RELEASE(swapChain->m_screenshotTarget, 0);
MTL_RELEASE_DEFERRED(swapChain->m_screenshotTarget);
}
if (NULL == swapChain->m_screenshotTarget)
@@ -6197,7 +6294,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
}
else
{
MTL_RELEASE(swapChain->m_screenshotTarget, 0);
MTL_RELEASE_DEFERRED(swapChain->m_screenshotTarget);
}
}

View File

@@ -72,6 +72,15 @@
#define MTL_RELEASE_W(_obj, _expected) _MTL_RELEASE(_obj, _expected, BX_WARN)
#define MTL_RELEASE_I(_obj) _MTL_RELEASE(_obj, 0, BX_NOOP)
#define MTL_RELEASE_DEFERRED(_obj) \
BX_MACRO_BLOCK_BEGIN \
if (NULL != _obj) \
{ \
bgfx::mtl::deferredRelease( (NS::Object*)(void*)_obj); \
_obj = NULL; \
} \
BX_MACRO_BLOCK_END
namespace bgfx { namespace mtl
{
// Helper to access NS::Object's protected sendMessage for label retrieval
@@ -243,6 +252,8 @@ namespace bgfx { namespace mtl
return MTL::RasterizationRateMapDescriptor::alloc()->init();
}
void deferredRelease(NS::Object* _obj);
//helper functions
template<typename T>
inline void release(T* _obj)
@@ -277,7 +288,7 @@ namespace bgfx { namespace mtl
void destroy()
{
MTL_RELEASE_W(m_ptr, 0);
MTL_RELEASE_DEFERRED(m_ptr);
if (NULL != m_dynamic)
{
@@ -324,13 +335,13 @@ namespace bgfx { namespace mtl
{
for (FunctionMap::iterator it = m_functions.begin(), itEnd = m_functions.end(); it != itEnd; ++it)
{
MTL_RELEASE_W(it->second, 0);
MTL_RELEASE_DEFERRED(it->second);
}
m_functions.clear();
m_function = NULL;
MTL_RELEASE_W(m_lib, 0);
MTL_RELEASE_DEFERRED(m_lib);
}
typedef stl::unordered_map<uint32_t, MTL::Function*> FunctionMap;
@@ -403,8 +414,8 @@ namespace bgfx { namespace mtl
m_fshConstantBuffer = NULL;
}
MTL_RELEASE_W(m_rps, 0);
MTL_RELEASE_W(m_cps, 0);
MTL_RELEASE_DEFERRED(m_rps);
MTL_RELEASE_DEFERRED(m_cps);
}
UniformBuffer* m_vshConstantBuffer;
@@ -436,6 +447,17 @@ namespace bgfx { namespace mtl
bx::deleteObject(g_allocator, _ptr);
}
template<typename T>
inline void evict(T* _ptr)
{
deferredRelease( (NS::Object*)(void*)_ptr);
}
inline void evict(PipelineStateMtl* _ptr)
{
release(_ptr);
}
struct VideoDecoderMtl;
struct TextureMtl
@@ -530,6 +552,7 @@ namespace bgfx { namespace mtl
, m_maxAnisotropy(0)
, m_colorFormat(TextureFormat::Count)
, m_borrowedDepth(false)
, m_hdr10(false)
{
}
@@ -557,6 +580,7 @@ namespace bgfx { namespace mtl
void* m_nwh;
TextureFormat::Enum m_colorFormat;
bool m_borrowedDepth;
bool m_hdr10;
};
struct FrameBufferMtl
@@ -598,9 +622,9 @@ namespace bgfx { namespace mtl
CommandQueueMtl()
: m_commandQueue(NULL)
, m_activeCommandBuffer(NULL)
, m_releaseWriteIndex(0)
, m_releaseReadIndex(0)
, m_maxFrameLatency(BGFX_CONFIG_MAX_FRAME_LATENCY)
, m_submittedFrames(0)
, m_completedFrames(0)
{
}
@@ -621,11 +645,17 @@ namespace bgfx { namespace mtl
MTL::CommandQueue* m_commandQueue;
MTL::CommandBuffer* m_activeCommandBuffer;
int m_releaseWriteIndex;
int m_releaseReadIndex;
uint32_t m_maxFrameLatency;
typedef stl::vector<NS::Object*> ResourceArray;
ResourceArray m_release[BGFX_CONFIG_MAX_FRAME_LATENCY];
uint32_t m_submittedFrames;
volatile uint32_t m_completedFrames;
struct Release
{
NS::Object* ptr;
uint32_t frame;
};
typedef stl::vector<Release> ReleaseArray;
ReleaseArray m_release;
};
struct TimerQueryMtl
@@ -720,6 +750,7 @@ namespace bgfx { namespace mtl
struct Query
{
OcclusionQueryHandle m_handle;
uint32_t m_frame;
};
MTL::Buffer* m_buffer;
@@ -736,7 +767,7 @@ namespace bgfx
{
static void evict(Ty* _ptr)
{
mtl::release(_ptr);
mtl::evict(_ptr);
}
static void validate(Ty* /*_ptr*/, uint64_t /*_key*/)