Support Metal 3.0.

Merge Metal-3.0 branch into master branch.
Update What's New document with merged features.
This commit is contained in:
Bill Hollings 2019-10-16 18:26:58 -04:00
commit c6ea99e4db
26 changed files with 703 additions and 243 deletions

View File

@ -111,3 +111,17 @@ bool mvkGetEnvVarBool(std::string varName, bool* pWasFound = nullptr);
int64_t val = wasFound ? ev : EV; \ int64_t val = wasFound ? ev : EV; \
cfgVal = (int32_t)std::min(std::max(val, (int64_t)INT32_MIN), (int64_t)INT32_MAX); \ cfgVal = (int32_t)std::min(std::max(val, (int64_t)INT32_MIN), (int64_t)INT32_MAX); \
} while(false) } while(false)
#pragma mark -
#pragma mark System memory
/** Returns the total amount of physical RAM in the system. */
uint64_t mvkGetSystemMemorySize();
/** Returns the amount of memory available to this process. */
uint64_t mvkGetAvailableMemorySize();
/** Returns the amount of memory currently used by this process. */
uint64_t mvkGetUsedMemorySize();

View File

@ -18,7 +18,9 @@
#include "MVKOSExtensions.h" #include "MVKOSExtensions.h"
#include <mach/mach_host.h>
#include <mach/mach_time.h> #include <mach/mach_time.h>
#include <mach/task.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@ -91,3 +93,43 @@ int64_t mvkGetEnvVarInt64(string varName, bool* pWasFound) {
bool mvkGetEnvVarBool(std::string varName, bool* pWasFound) { bool mvkGetEnvVarBool(std::string varName, bool* pWasFound) {
return mvkGetEnvVarInt64(varName, pWasFound) != 0; return mvkGetEnvVarInt64(varName, pWasFound) != 0;
} }
#pragma mark -
#pragma mark System memory
uint64_t mvkGetSystemMemorySize() {
mach_msg_type_number_t host_size = HOST_BASIC_INFO_COUNT;
host_basic_info_data_t info;
if (host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&info, &host_size) == KERN_SUCCESS) {
return info.max_mem;
}
return 0;
}
uint64_t mvkGetAvailableMemorySize() {
#if MVK_IOS
if (mvkOSVersion() >= 13.0) { return os_proc_available_memory(); }
#endif
mach_port_t host_port;
mach_msg_type_number_t host_size;
vm_size_t pagesize;
host_port = mach_host_self();
host_size = HOST_VM_INFO_COUNT;
host_page_size(host_port, &pagesize);
vm_statistics_data_t vm_stat;
if (host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size) == KERN_SUCCESS ) {
return vm_stat.free_count * pagesize;
}
return 0;
}
uint64_t mvkGetUsedMemorySize() {
task_vm_info_data_t task_vm_info;
mach_msg_type_number_t task_size = TASK_VM_INFO_COUNT;
if (task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&task_vm_info, &task_size) == KERN_SUCCESS) {
return task_vm_info.phys_footprint;
}
return 0;
}

View File

@ -17,8 +17,18 @@ For best results, use a Markdown reader.*
MoltenVK 1.0.38 MoltenVK 1.0.38
--------------- ---------------
Released TBD Released 2019/10/31
- Add support for Metal 3.0 capabilities.
- Add support for extensions:
- `VK_EXT_swapchain_colorspace` (*iOS*, already supported on *macOS*).
- `VK_EXT_hdr_metadata` (*macOS*)
- Use native texture swizzling when available.
- Use placement `MTLHeaps` for `VkDeviceMemory` when possible.
- Report heap sizes accurately when possible.
- Add support for additional colorspace options.
- Add support for the `VkPhysicalDeviceFeatures::shaderResourceMinLod` feature.
- Add support for compressed 3D images on *macOS*.
- Update `MoltenVK_Runtime_UserGuide.md` about embedding `libMoltenVK.dylib` in an application. - Update `MoltenVK_Runtime_UserGuide.md` about embedding `libMoltenVK.dylib` in an application.
- Clarify static linking as the recommended linking approach for *iOS* app store distribution. - Clarify static linking as the recommended linking approach for *iOS* app store distribution.
- Add request for feedback from people who reject **MoltenVK** to `README.md` document. - Add request for feedback from people who reject **MoltenVK** to `README.md` document.
@ -30,6 +40,7 @@ Released TBD
MoltenVK 1.0.37 MoltenVK 1.0.37
--------------- ---------------
@ -112,7 +123,7 @@ Released 2019/07/25
- `VK_EXT_post_depth_coverage` - `VK_EXT_post_depth_coverage`
- `VK_EXT_scalar_block_layout` - `VK_EXT_scalar_block_layout`
- `VK_EXT_shader_stencil_export` - `VK_EXT_shader_stencil_export`
- `VK_EXT_swapchain_colorspace` - `VK_EXT_swapchain_colorspace` (*macOS*)
- `VK_EXT_texel_buffer_alignment` - `VK_EXT_texel_buffer_alignment`
- `VK_AMD_shader_image_load_store_lod` - `VK_AMD_shader_image_load_store_lod`
- `VK_AMD_shader_trinary_minmax` - `VK_AMD_shader_trinary_minmax`

View File

@ -234,6 +234,12 @@ uint32_t mvkSampleCountFromVkSampleCountFlagBits(VkSampleCountFlagBits vkSampleC
/** Returns the Vulkan bit flags corresponding to the numeric sample count, which must be a PoT value. */ /** Returns the Vulkan bit flags corresponding to the numeric sample count, which must be a PoT value. */
VkSampleCountFlagBits mvkVkSampleCountFlagBitsFromSampleCount(NSUInteger sampleCount); VkSampleCountFlagBits mvkVkSampleCountFlagBitsFromSampleCount(NSUInteger sampleCount);
/** Returns the Metal texture swizzle from the Vulkan component swizzle. */
MTLTextureSwizzle mvkMTLTextureSwizzleFromVkComponentSwizzle(VkComponentSwizzle vkSwizzle);
/** Returns all four Metal texture swizzles from the Vulkan component mapping. */
MTLTextureSwizzleChannels mvkMTLTextureSwizzleChannelsFromVkComponentMapping(VkComponentMapping vkMapping);
#pragma mark Mipmaps #pragma mark Mipmaps

View File

@ -549,6 +549,9 @@ typedef struct {
VkBool32 postDepthCoverage; /**< If true, coverage masks in fragment shaders post-depth-test are supported. */ VkBool32 postDepthCoverage; /**< If true, coverage masks in fragment shaders post-depth-test are supported. */
VkBool32 fences; /**< If true, Metal synchronization fences (MTLFence) are supported. */ VkBool32 fences; /**< If true, Metal synchronization fences (MTLFence) are supported. */
VkBool32 rasterOrderGroups; /**< If true, Raster order groups in fragment shaders are supported. */ VkBool32 rasterOrderGroups; /**< If true, Raster order groups in fragment shaders are supported. */
VkBool32 native3DCompressedTextures; /**< If true, 3D compressed images are supported natively, without manual decompression. */
VkBool32 nativeTextureSwizzle; /**< If true, component swizzle is supported natively, without manual swizzling in shaders. */
VkBool32 placementHeaps; /**< If true, MTLHeap objects support placement of resources. */
} MVKPhysicalDeviceMetalFeatures; } MVKPhysicalDeviceMetalFeatures;
/** /**

View File

@ -762,7 +762,8 @@ void MVKCmdBufferImageCopy::encode(MVKCommandEncoder* cmdEncoder) {
// If we're copying to a compressed 3D image, the image data need to be decompressed. // If we're copying to a compressed 3D image, the image data need to be decompressed.
// If we're copying to mip level 0, we can skip the copy and just decode // If we're copying to mip level 0, we can skip the copy and just decode
// directly into the image. Otherwise, we need to use an intermediate buffer. // directly into the image. Otherwise, we need to use an intermediate buffer.
if (_toImage && _image->getIsCompressed() && mtlTexture.textureType == MTLTextureType3D) { if (_toImage && _image->getIsCompressed() && mtlTexture.textureType == MTLTextureType3D &&
!getDevice()->_pMetalFeatures->native3DCompressedTextures) {
MVKCmdCopyBufferToImageInfo info; MVKCmdCopyBufferToImageInfo info;
info.srcRowStride = bytesPerRow & 0xffffffff; info.srcRowStride = bytesPerRow & 0xffffffff;
info.srcRowStrideHigh = bytesPerRow >> 32; info.srcRowStrideHigh = bytesPerRow >> 32;

View File

@ -591,7 +591,7 @@ void MVKGraphicsResourcesCommandEncoderState::markDirty() {
void MVKGraphicsResourcesCommandEncoderState::encodeImpl(uint32_t stage) { void MVKGraphicsResourcesCommandEncoderState::encodeImpl(uint32_t stage) {
MVKPipeline* pipeline = _cmdEncoder->_graphicsPipelineState.getPipeline(); MVKPipeline* pipeline = _cmdEncoder->_graphicsPipelineState.getPipeline();
bool fullImageViewSwizzle = pipeline->fullImageViewSwizzle(); bool fullImageViewSwizzle = pipeline->fullImageViewSwizzle() || _cmdEncoder->getDevice()->_pMetalFeatures->nativeTextureSwizzle;
bool forTessellation = ((MVKGraphicsPipeline*)pipeline)->isTessellationPipeline(); bool forTessellation = ((MVKGraphicsPipeline*)pipeline)->isTessellationPipeline();
if (stage == (forTessellation ? kMVKGraphicsStageVertex : kMVKGraphicsStageRasterization)) { if (stage == (forTessellation ? kMVKGraphicsStageVertex : kMVKGraphicsStageRasterization)) {

View File

@ -69,10 +69,10 @@ public:
#pragma mark Metal #pragma mark Metal
/** Returns the Metal buffer underlying this memory allocation. */ /** Returns the Metal buffer underlying this memory allocation. */
inline id<MTLBuffer> getMTLBuffer() { return _deviceMemory ? _deviceMemory->getMTLBuffer() : nullptr; } id<MTLBuffer> getMTLBuffer();
/** Returns the offset at which the contents of this instance starts within the underlying Metal buffer. */ /** Returns the offset at which the contents of this instance starts within the underlying Metal buffer. */
inline NSUInteger getMTLBufferOffset() { return _deviceMemoryOffset; } inline NSUInteger getMTLBufferOffset() { return _deviceMemory && _deviceMemory->getMTLHeap() ? 0 : _deviceMemoryOffset; }
#pragma mark Construction #pragma mark Construction
@ -90,6 +90,7 @@ protected:
VkBufferMemoryBarrier* pBufferMemoryBarrier); VkBufferMemoryBarrier* pBufferMemoryBarrier);
VkBufferUsageFlags _usage; VkBufferUsageFlags _usage;
id<MTLBuffer> _mtlBuffer = nil;
}; };

View File

@ -29,21 +29,28 @@ using namespace std;
#pragma mark MVKBuffer #pragma mark MVKBuffer
void MVKBuffer::propogateDebugName() { void MVKBuffer::propogateDebugName() {
if (_debugName && if (!_debugName) { return; }
_deviceMemory && if (_deviceMemory &&
_deviceMemory->isDedicatedAllocation() && _deviceMemory->isDedicatedAllocation() &&
_deviceMemory->_debugName.length == 0) { _deviceMemory->_debugName.length == 0) {
_deviceMemory->setDebugName(_debugName.UTF8String); _deviceMemory->setDebugName(_debugName.UTF8String);
} }
setLabelIfNotNil(_mtlBuffer, _debugName);
} }
#pragma mark Resource memory #pragma mark Resource memory
VkResult MVKBuffer::getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements) { VkResult MVKBuffer::getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements) {
if (_device->_pMetalFeatures->placementHeaps) {
MTLSizeAndAlign sizeAndAlign = [_device->getMTLDevice() heapBufferSizeAndAlignWithLength: getByteCount() options: MTLResourceStorageModePrivate];
pMemoryRequirements->size = sizeAndAlign.size;
pMemoryRequirements->alignment = sizeAndAlign.align;
} else {
pMemoryRequirements->size = getByteCount(); pMemoryRequirements->size = getByteCount();
pMemoryRequirements->alignment = _byteAlignment; pMemoryRequirements->alignment = _byteAlignment;
}
pMemoryRequirements->memoryTypeBits = _device->getPhysicalDevice()->getAllMemoryTypes(); pMemoryRequirements->memoryTypeBits = _device->getPhysicalDevice()->getAllMemoryTypes();
#if MVK_MACOS #if MVK_MACOS
// Textures must not use shared memory // Textures must not use shared memory
@ -126,6 +133,25 @@ bool MVKBuffer::needsHostReadSync(VkPipelineStageFlags srcStageMask,
} }
#pragma mark Metal
id<MTLBuffer> MVKBuffer::getMTLBuffer() {
if (_mtlBuffer) { return _mtlBuffer; }
if (_deviceMemory) {
if (_deviceMemory->getMTLHeap()) {
_mtlBuffer = [_deviceMemory->getMTLHeap() newBufferWithLength: getByteCount()
options: _deviceMemory->getMTLResourceOptions()
offset: _deviceMemoryOffset]; // retained
propogateDebugName();
return _mtlBuffer;
} else {
return _deviceMemory->getMTLBuffer();
}
}
return nil;
}
#pragma mark Construction #pragma mark Construction
MVKBuffer::MVKBuffer(MVKDevice* device, const VkBufferCreateInfo* pCreateInfo) : MVKResource(device), _usage(pCreateInfo->usage) { MVKBuffer::MVKBuffer(MVKDevice* device, const VkBufferCreateInfo* pCreateInfo) : MVKResource(device), _usage(pCreateInfo->usage) {
@ -135,6 +161,7 @@ MVKBuffer::MVKBuffer(MVKDevice* device, const VkBufferCreateInfo* pCreateInfo) :
MVKBuffer::~MVKBuffer() { MVKBuffer::~MVKBuffer() {
if (_deviceMemory) { _deviceMemory->removeBuffer(this); } if (_deviceMemory) { _deviceMemory->removeBuffer(this); }
if (_mtlBuffer) { [_mtlBuffer release]; }
} }

View File

@ -283,9 +283,15 @@ public:
*/ */
inline uint32_t getLazilyAllocatedMemoryTypes() { return _lazilyAllocatedMemoryTypes; } inline uint32_t getLazilyAllocatedMemoryTypes() { return _lazilyAllocatedMemoryTypes; }
/** Returns whether this is a unified memory device. */
bool getHasUnifiedMemory();
#pragma mark Metal #pragma mark Metal
/** Returns whether the underlying MTLDevice supports the GPU family. */
bool getSupportsGPUFamily(MTLGPUFamily gpuFamily);
/** Populates the specified structure with the Metal-specific features of this device. */ /** Populates the specified structure with the Metal-specific features of this device. */
inline const MVKPhysicalDeviceMetalFeatures* getMetalFeatures() { return &_metalFeatures; } inline const MVKPhysicalDeviceMetalFeatures* getMetalFeatures() { return &_metalFeatures; }
@ -330,11 +336,15 @@ protected:
void initMetalFeatures(); void initMetalFeatures();
void initFeatures(); void initFeatures();
void initProperties(); void initProperties();
void initGPUInfoProperties();
void initMemoryProperties(); void initMemoryProperties();
uint64_t getVRAMSize();
uint64_t getRecommendedMaxWorkingSetSize();
uint64_t getCurrentAllocatedSize();
void initExtensions(); void initExtensions();
MVKVector<MVKQueueFamily*>& getQueueFamilies(); MVKVector<MVKQueueFamily*>& getQueueFamilies();
void initPipelineCacheUUID(); void initPipelineCacheUUID();
MTLFeatureSet getHighestMTLFeatureSet(); uint32_t getHighestMTLFeatureSet();
uint64_t getSpirvCrossRevision(); uint64_t getSpirvCrossRevision();
bool getImageViewIsSupported(const VkPhysicalDeviceImageFormatInfo2KHR *pImageFormatInfo); bool getImageViewIsSupported(const VkPhysicalDeviceImageFormatInfo2KHR *pImageFormatInfo);
void logGPUInfo(); void logGPUInfo();
@ -831,18 +841,5 @@ protected:
#pragma mark - #pragma mark -
#pragma mark Support functions #pragma mark Support functions
/** Returns an approximation of how much memory, in bytes, the device can use with good performance. */
uint64_t mvkRecommendedMaxWorkingSetSize(id<MTLDevice> mtlDevice);
/** Populate the propertes with info about the GPU represented by the MTLDevice. */
void mvkPopulateGPUInfo(VkPhysicalDeviceProperties& devProps, id<MTLDevice> mtlDevice);
/** Returns the registry ID of the specified device, or zero if the device does not have a registry ID. */ /** Returns the registry ID of the specified device, or zero if the device does not have a registry ID. */
uint64_t mvkGetRegistryID(id<MTLDevice> mtlDevice); uint64_t mvkGetRegistryID(id<MTLDevice> mtlDevice);
/**
* If the MTLDevice defines a texture memory alignment for the format, it is retrieved from
* the MTLDevice and returned, or returns zero if the MTLDevice does not define an alignment.
* The format must support linear texture memory (must not be depth, stencil, or compressed).
*/
VkDeviceSize mvkMTLPixelFormatLinearTextureAlignment(MTLPixelFormat mtlPixelFormat, id<MTLDevice> mtlDevice);

View File

@ -36,7 +36,6 @@
#include "MVKOSExtensions.h" #include "MVKOSExtensions.h"
#include <MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h> #include <MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h>
#include "vk_mvk_moltenvk.h" #include "vk_mvk_moltenvk.h"
#include <mach/mach_host.h>
#import "CAMetalLayer+MoltenVK.h" #import "CAMetalLayer+MoltenVK.h"
@ -135,7 +134,7 @@ void MVKPhysicalDevice::getFeatures(VkPhysicalDeviceFeatures2* features) {
portabilityFeatures->triangleFans = false; portabilityFeatures->triangleFans = false;
portabilityFeatures->separateStencilMaskRef = true; portabilityFeatures->separateStencilMaskRef = true;
portabilityFeatures->events = true; portabilityFeatures->events = true;
portabilityFeatures->standardImageViews = _mvkInstance->getMoltenVKConfiguration()->fullImageViewSwizzle; portabilityFeatures->standardImageViews = _mvkInstance->getMoltenVKConfiguration()->fullImageViewSwizzle || _metalFeatures.nativeTextureSwizzle;
portabilityFeatures->samplerMipLodBias = false; portabilityFeatures->samplerMipLodBias = false;
break; break;
} }
@ -335,7 +334,7 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format,
} }
#if MVK_MACOS #if MVK_MACOS
// If this is a compressed format and there's no codec, it isn't supported. // If this is a compressed format and there's no codec, it isn't supported.
if ((mvkFmt == kMVKFormatCompressed) && !mvkCanDecodeFormat(format)) { if ((mvkFmt == kMVKFormatCompressed) && !mvkCanDecodeFormat(format) && !_metalFeatures.native3DCompressedTextures) {
return VK_ERROR_FORMAT_NOT_SUPPORTED; return VK_ERROR_FORMAT_NOT_SUPPORTED;
} }
#endif #endif
@ -491,8 +490,8 @@ VkResult MVKPhysicalDevice::getSurfaceFormats(MVKSurface* surface,
MVKVectorInline<VkColorSpaceKHR, 16> colorSpaces; MVKVectorInline<VkColorSpaceKHR, 16> colorSpaces;
colorSpaces.push_back(VK_COLOR_SPACE_SRGB_NONLINEAR_KHR); colorSpaces.push_back(VK_COLOR_SPACE_SRGB_NONLINEAR_KHR);
#if MVK_MACOS
if (getInstance()->_enabledExtensions.vk_EXT_swapchain_colorspace.enabled) { if (getInstance()->_enabledExtensions.vk_EXT_swapchain_colorspace.enabled) {
#if MVK_MACOS
// 10.11 supports some but not all of the color spaces specified by VK_EXT_swapchain_colorspace. // 10.11 supports some but not all of the color spaces specified by VK_EXT_swapchain_colorspace.
colorSpaces.push_back(VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT); colorSpaces.push_back(VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT);
colorSpaces.push_back(VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT); colorSpaces.push_back(VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT);
@ -503,8 +502,38 @@ VkResult MVKPhysicalDevice::getSurfaceFormats(MVKSurface* surface,
colorSpaces.push_back(VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT); colorSpaces.push_back(VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT);
colorSpaces.push_back(VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT); colorSpaces.push_back(VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT);
} }
if (mvkOSVersion() >= 10.14) {
colorSpaces.push_back(VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT);
colorSpaces.push_back(VK_COLOR_SPACE_BT2020_LINEAR_EXT);
colorSpaces.push_back(VK_COLOR_SPACE_HDR10_ST2084_EXT);
colorSpaces.push_back(VK_COLOR_SPACE_HDR10_HLG_EXT);
} }
#endif #endif
#if MVK_IOS
// iOS 8 doesn't support anything but sRGB.
if (mvkOSVersion() >= 9.0) {
colorSpaces.push_back(VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT);
colorSpaces.push_back(VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT);
colorSpaces.push_back(VK_COLOR_SPACE_BT709_NONLINEAR_EXT);
colorSpaces.push_back(VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT);
colorSpaces.push_back(VK_COLOR_SPACE_PASS_THROUGH_EXT);
}
if (mvkOSVersion() >= 10.0) {
colorSpaces.push_back(VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT);
colorSpaces.push_back(VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT);
}
if (mvkOSVersion() >= 12.0) {
colorSpaces.push_back(VK_COLOR_SPACE_HDR10_ST2084_EXT);
}
if (mvkOSVersion() >= 12.3) {
colorSpaces.push_back(VK_COLOR_SPACE_DCI_P3_LINEAR_EXT);
colorSpaces.push_back(VK_COLOR_SPACE_BT2020_LINEAR_EXT);
}
if (mvkOSVersion() >= 13.0) {
colorSpaces.push_back(VK_COLOR_SPACE_HDR10_HLG_EXT);
}
#endif
}
uint mtlFmtsCnt = sizeof(mtlFormats) / sizeof(MTLPixelFormat); uint mtlFmtsCnt = sizeof(mtlFormats) / sizeof(MTLPixelFormat);
if (!mvkMTLPixelFormatIsSupported(MTLPixelFormatBGR10A2Unorm)) { mtlFmtsCnt--; } if (!mvkMTLPixelFormatIsSupported(MTLPixelFormatBGR10A2Unorm)) { mtlFmtsCnt--; }
@ -710,22 +739,21 @@ VkResult MVKPhysicalDevice::getPhysicalDeviceMemoryProperties(VkPhysicalDeviceMe
if (pMemoryProperties) { if (pMemoryProperties) {
pMemoryProperties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2; pMemoryProperties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
pMemoryProperties->memoryProperties = _memoryProperties; pMemoryProperties->memoryProperties = _memoryProperties;
auto* next = (MVKVkAPIStructHeader*)pMemoryProperties->pNext; for (auto* next = (VkBaseOutStructure*)pMemoryProperties->pNext; next; next = next->pNext) {
while (next) { switch (next->sType) {
switch ((uint32_t)next->sType) {
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT: { case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT: {
auto* budgetProps = (VkPhysicalDeviceMemoryBudgetPropertiesEXT*)next; auto* budgetProps = (VkPhysicalDeviceMemoryBudgetPropertiesEXT*)next;
memset(budgetProps->heapBudget, 0, sizeof(budgetProps->heapBudget)); memset(budgetProps->heapBudget, 0, sizeof(budgetProps->heapBudget));
memset(budgetProps->heapUsage, 0, sizeof(budgetProps->heapUsage)); memset(budgetProps->heapUsage, 0, sizeof(budgetProps->heapUsage));
budgetProps->heapBudget[0] = (VkDeviceSize)mvkRecommendedMaxWorkingSetSize(_mtlDevice); budgetProps->heapBudget[0] = (VkDeviceSize)getRecommendedMaxWorkingSetSize();
if ( [_mtlDevice respondsToSelector: @selector(currentAllocatedSize)] ) { budgetProps->heapUsage[0] = (VkDeviceSize)getCurrentAllocatedSize();
budgetProps->heapUsage[0] = (VkDeviceSize)_mtlDevice.currentAllocatedSize; if (!getHasUnifiedMemory()) {
budgetProps->heapBudget[1] = (VkDeviceSize)mvkGetAvailableMemorySize();
budgetProps->heapUsage[1] = (VkDeviceSize)mvkGetUsedMemorySize();
} }
next = (MVKVkAPIStructHeader*)budgetProps->pNext;
break; break;
} }
default: default:
next = (MVKVkAPIStructHeader*)next->pNext;
break; break;
} }
} }
@ -821,6 +849,14 @@ void MVKPhysicalDevice::initMetalFeatures() {
_metalFeatures.stencilFeedback = true; _metalFeatures.stencilFeedback = true;
} }
if ( mvkOSVersion() >= 13.0 ) {
_metalFeatures.mslVersionEnum = MTLLanguageVersion2_2;
_metalFeatures.placementHeaps = true;
if ( getSupportsGPUFamily(MTLGPUFamilyApple4) ) {
_metalFeatures.nativeTextureSwizzle = true;
}
}
#endif #endif
#if MVK_MACOS #if MVK_MACOS
@ -866,6 +902,15 @@ void MVKPhysicalDevice::initMetalFeatures() {
_metalFeatures.stencilFeedback = true; _metalFeatures.stencilFeedback = true;
} }
if ( mvkOSVersion() >= 10.15 ) {
_metalFeatures.mslVersionEnum = MTLLanguageVersion2_2;
_metalFeatures.native3DCompressedTextures = true;
if ( getSupportsGPUFamily(MTLGPUFamilyMac2) ) {
_metalFeatures.nativeTextureSwizzle = true;
_metalFeatures.placementHeaps = true;
}
}
#endif #endif
// Note the selector name, which is different from the property name. // Note the selector name, which is different from the property name.
@ -887,6 +932,9 @@ void MVKPhysicalDevice::initMetalFeatures() {
_metalFeatures.mslVersion = SPIRV_CROSS_NAMESPACE::CompilerMSL::Options::make_msl_version(maj, min); _metalFeatures.mslVersion = SPIRV_CROSS_NAMESPACE::CompilerMSL::Options::make_msl_version(maj, min);
switch (_metalFeatures.mslVersionEnum) { switch (_metalFeatures.mslVersionEnum) {
case MTLLanguageVersion2_2:
setMSLVersion(2, 2);
break;
case MTLLanguageVersion2_1: case MTLLanguageVersion2_1:
setMSLVersion(2, 1); setMSLVersion(2, 1);
break; break;
@ -915,7 +963,12 @@ void MVKPhysicalDevice::initMetalFeatures() {
} }
/** Initializes the physical device features of this instance. */ bool MVKPhysicalDevice::getSupportsGPUFamily(MTLGPUFamily gpuFamily) {
return ([_mtlDevice respondsToSelector: @selector(supportsFamily:)] &&
[_mtlDevice supportsFamily: gpuFamily]);
}
// Initializes the physical device features of this instance.
void MVKPhysicalDevice::initFeatures() { void MVKPhysicalDevice::initFeatures() {
memset(&_features, 0, sizeof(_features)); // Start with everything cleared memset(&_features, 0, sizeof(_features)); // Start with everything cleared
@ -999,6 +1052,9 @@ void MVKPhysicalDevice::initFeatures() {
_features.multiViewport = true; _features.multiViewport = true;
} }
if ( mvkOSVersion() >= 10.15 ) {
_features.shaderResourceMinLod = true;
}
#endif #endif
} }
@ -1049,7 +1105,7 @@ void MVKPhysicalDevice::initFeatures() {
// VkBool32 shaderInt64; // VkBool32 shaderInt64;
// VkBool32 shaderInt16; // done // VkBool32 shaderInt16; // done
// VkBool32 shaderResourceResidency; // VkBool32 shaderResourceResidency;
// VkBool32 shaderResourceMinLod; // VkBool32 shaderResourceMinLod; // done
// VkBool32 sparseBinding; // VkBool32 sparseBinding;
// VkBool32 sparseResidencyBuffer; // VkBool32 sparseResidencyBuffer;
// VkBool32 sparseResidencyImage2D; // VkBool32 sparseResidencyImage2D;
@ -1070,7 +1126,7 @@ void MVKPhysicalDevice::initProperties() {
_properties.apiVersion = MVK_VULKAN_API_VERSION; _properties.apiVersion = MVK_VULKAN_API_VERSION;
_properties.driverVersion = MVK_VERSION; _properties.driverVersion = MVK_VERSION;
mvkPopulateGPUInfo(_properties, _mtlDevice); initGPUInfoProperties();
initPipelineCacheUUID(); initPipelineCacheUUID();
// Limits // Limits
@ -1381,6 +1437,104 @@ void MVKPhysicalDevice::initProperties() {
_properties.limits.maxGeometryTotalOutputComponents = 0; _properties.limits.maxGeometryTotalOutputComponents = 0;
} }
#if MVK_MACOS
static uint32_t mvkGetEntryProperty(io_registry_entry_t entry, CFStringRef propertyName) {
uint32_t value = 0;
CFTypeRef cfProp = IORegistryEntrySearchCFProperty(entry,
kIOServicePlane,
propertyName,
kCFAllocatorDefault,
kIORegistryIterateRecursively |
kIORegistryIterateParents);
if (cfProp) {
const uint32_t* pValue = reinterpret_cast<const uint32_t*>(CFDataGetBytePtr((CFDataRef)cfProp));
if (pValue) { value = *pValue; }
CFRelease(cfProp);
}
return value;
}
void MVKPhysicalDevice::initGPUInfoProperties() {
static const uint32_t kIntelVendorId = 0x8086;
bool isFound = false;
bool isIntegrated = _mtlDevice.isLowPower;
_properties.deviceType = isIntegrated ? VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU : VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU;
strlcpy(_properties.deviceName, _mtlDevice.name.UTF8String, VK_MAX_PHYSICAL_DEVICE_NAME_SIZE);
// If the device has an associated registry ID, we can use that to get the associated IOKit node.
// The match dictionary is consumed by IOServiceGetMatchingServices and does not need to be released.
io_registry_entry_t entry;
uint64_t regID = mvkGetRegistryID(_mtlDevice);
if (regID) {
entry = IOServiceGetMatchingService(kIOMasterPortDefault, IORegistryEntryIDMatching(regID));
if (entry) {
// That returned the IOGraphicsAccelerator nub. Its parent, then, is the actual
// PCI device.
io_registry_entry_t parent;
if (IORegistryEntryGetParentEntry(entry, kIOServicePlane, &parent) == kIOReturnSuccess) {
isFound = true;
_properties.vendorID = mvkGetEntryProperty(parent, CFSTR("vendor-id"));
_properties.deviceID = mvkGetEntryProperty(parent, CFSTR("device-id"));
IOObjectRelease(parent);
}
IOObjectRelease(entry);
}
}
// Iterate all GPU's, looking for a match.
// The match dictionary is consumed by IOServiceGetMatchingServices and does not need to be released.
io_iterator_t entryIterator;
if (!isFound && IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching("IOPCIDevice"),
&entryIterator) == kIOReturnSuccess) {
while ( !isFound && (entry = IOIteratorNext(entryIterator)) ) {
if (mvkGetEntryProperty(entry, CFSTR("class-code")) == 0x30000) { // 0x30000 : DISPLAY_VGA
// The Intel GPU will always be marked as integrated.
// Return on a match of either Intel && low power, or non-Intel and non-low-power.
uint32_t vendorID = mvkGetEntryProperty(entry, CFSTR("vendor-id"));
if ( (vendorID == kIntelVendorId) == isIntegrated) {
isFound = true;
_properties.vendorID = vendorID;
_properties.deviceID = mvkGetEntryProperty(entry, CFSTR("device-id"));
}
}
}
IOObjectRelease(entryIterator);
}
}
#endif //MVK_MACOS
#if MVK_IOS
// For iOS devices, the Device ID is the SoC model (A8, A10X...), in the hex form 0xaMMX, where
//"a" is the Apple brand, MM is the SoC model number (8, 10...) and X is 1 for X version, 0 for other.
void MVKPhysicalDevice::initGPUInfoProperties() {
NSUInteger coreCnt = NSProcessInfo.processInfo.processorCount;
uint32_t devID = 0xa070;
if ([_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily5_v1]) {
devID = 0xa120;
} else if ([_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily4_v1]) {
devID = 0xa110;
} else if ([_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v1]) {
devID = coreCnt > 2 ? 0xa101 : 0xa100;
} else if ([_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v1]) {
devID = coreCnt > 2 ? 0xa081 : 0xa080;
}
_properties.vendorID = 0x0000106b; // Apple's PCI ID
_properties.deviceID = devID;
_properties.deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
strlcpy(_properties.deviceName, _mtlDevice.name.UTF8String, VK_MAX_PHYSICAL_DEVICE_NAME_SIZE);
}
#endif //MVK_IOS
#pragma mark VkPhysicalDeviceLimits - List of feature limits available on the device #pragma mark VkPhysicalDeviceLimits - List of feature limits available on the device
@ -1516,7 +1670,7 @@ void MVKPhysicalDevice::initPipelineCacheUUID() {
uuidComponentOffset += sizeof(mvkVersion); uuidComponentOffset += sizeof(mvkVersion);
// Next 4 bytes contains hightest Metal feature set supported by this device // Next 4 bytes contains hightest Metal feature set supported by this device
uint32_t mtlFeatSet = (uint32_t)getHighestMTLFeatureSet(); uint32_t mtlFeatSet = getHighestMTLFeatureSet();
*(uint32_t*)&_properties.pipelineCacheUUID[uuidComponentOffset] = NSSwapHostIntToBig(mtlFeatSet); *(uint32_t*)&_properties.pipelineCacheUUID[uuidComponentOffset] = NSSwapHostIntToBig(mtlFeatSet);
uuidComponentOffset += sizeof(mtlFeatSet); uuidComponentOffset += sizeof(mtlFeatSet);
@ -1526,24 +1680,46 @@ void MVKPhysicalDevice::initPipelineCacheUUID() {
uuidComponentOffset += sizeof(spvxRev); uuidComponentOffset += sizeof(spvxRev);
} }
MTLFeatureSet MVKPhysicalDevice::getHighestMTLFeatureSet() { uint32_t MVKPhysicalDevice::getHighestMTLFeatureSet() {
// On newer OS's, combine highest Metal version with highest GPU family
// (Mac & Apple GPU lists should be mutex on platform)
uint32_t mtlVer = 0;
#if MVK_IOS #if MVK_IOS
MTLFeatureSet maxFS = MTLFeatureSet_iOS_GPUFamily5_v1; if (mvkOSVersion() >= 13.0) { mtlVer = 0x30000; }
MTLFeatureSet minFS = MTLFeatureSet_iOS_GPUFamily1_v1; #endif
#if MVK_MACOS
if (mvkOSVersion() >= 10.15) { mtlVer = 0x30000; }
#endif
MTLGPUFamily mtlFam = MTLGPUFamily(0);
if (getSupportsGPUFamily(MTLGPUFamilyMac1)) { mtlFam = MTLGPUFamilyMac1; }
if (getSupportsGPUFamily(MTLGPUFamilyMac2)) { mtlFam = MTLGPUFamilyMac2; }
if (getSupportsGPUFamily(MTLGPUFamilyApple1)) { mtlFam = MTLGPUFamilyApple1; }
if (getSupportsGPUFamily(MTLGPUFamilyApple2)) { mtlFam = MTLGPUFamilyApple2; }
if (getSupportsGPUFamily(MTLGPUFamilyApple3)) { mtlFam = MTLGPUFamilyApple3; }
if (getSupportsGPUFamily(MTLGPUFamilyApple4)) { mtlFam = MTLGPUFamilyApple4; }
if (getSupportsGPUFamily(MTLGPUFamilyApple5)) { mtlFam = MTLGPUFamilyApple5; }
// Not explicitly guaranteed to be unique...but close enough without spilling over
uint32_t mtlFS = (mtlVer << 8) + (uint32_t)mtlFam;
if (mtlFS) { return mtlFS; }
// Fall back to legacy feature sets on older OS's
#if MVK_IOS
uint32_t maxFS = (uint32_t)MTLFeatureSet_iOS_GPUFamily5_v1;
uint32_t minFS = (uint32_t)MTLFeatureSet_iOS_GPUFamily1_v1;
#endif #endif
#if MVK_MACOS #if MVK_MACOS
MTLFeatureSet maxFS = MTLFeatureSet_macOS_GPUFamily2_v1; uint32_t maxFS = (uint32_t)MTLFeatureSet_macOS_GPUFamily2_v1;
MTLFeatureSet minFS = MTLFeatureSet_macOS_GPUFamily1_v1; uint32_t minFS = (uint32_t)MTLFeatureSet_macOS_GPUFamily1_v1;
#endif #endif
for (NSUInteger fs = maxFS; fs > minFS; fs--) { for (uint32_t fs = maxFS; fs > minFS; fs--) {
MTLFeatureSet mtlFS = (MTLFeatureSet)fs; if ( [_mtlDevice supportsFeatureSet: (MTLFeatureSet)fs] ) { return fs; }
if ( [_mtlDevice supportsFeatureSet: mtlFS] ) {
return mtlFS;
} }
}
return minFS; return minFS;
} }
@ -1606,7 +1782,7 @@ void MVKPhysicalDevice::initMemoryProperties() {
.memoryHeaps = { .memoryHeaps = {
{ {
.flags = (VK_MEMORY_HEAP_DEVICE_LOCAL_BIT), .flags = (VK_MEMORY_HEAP_DEVICE_LOCAL_BIT),
.size = (VkDeviceSize)mvkRecommendedMaxWorkingSetSize(_mtlDevice), .size = (VkDeviceSize)getVRAMSize(),
}, },
}, },
// NB this list needs to stay sorted by propertyFlags (as bit sets) // NB this list needs to stay sorted by propertyFlags (as bit sets)
@ -1656,6 +1832,65 @@ void MVKPhysicalDevice::initMemoryProperties() {
_allMemoryTypes = 0x7; // Private, shared & memoryless _allMemoryTypes = 0x7; // Private, shared & memoryless
} }
#endif #endif
#if MVK_MACOS
if (!getHasUnifiedMemory()) {
// This means we really have two heaps. The second heap is system memory.
_memoryProperties.memoryHeapCount = 2;
_memoryProperties.memoryHeaps[1].size = mvkGetSystemMemorySize();
_memoryProperties.memoryHeaps[1].flags = 0;
_memoryProperties.memoryTypes[2].heapIndex = 1; // Shared memory in the shared heap
}
#endif
}
bool MVKPhysicalDevice::getHasUnifiedMemory() {
#if MVK_IOS
return true;
#endif
#if MVK_MACOS
return [_mtlDevice respondsToSelector: @selector(hasUnifiedMemory)] && _mtlDevice.hasUnifiedMemory;
#endif
}
uint64_t MVKPhysicalDevice::getVRAMSize() {
#if MVK_IOS
// All iOS devices are UMA, so return the system memory size.
return mvkGetSystemMemorySize();
#endif
#if MVK_MACOS
if (getHasUnifiedMemory()) { return mvkGetSystemMemorySize(); }
// There's actually no way to query the total physical VRAM on the device in Metal.
// Just default to using the recommended max working set size (i.e. the budget).
return getRecommendedMaxWorkingSetSize();
#endif
}
uint64_t MVKPhysicalDevice::getRecommendedMaxWorkingSetSize() {
#if MVK_MACOS
if ( [_mtlDevice respondsToSelector: @selector(recommendedMaxWorkingSetSize)]) {
return _mtlDevice.recommendedMaxWorkingSetSize;
}
#endif
#if MVK_IOS
// GPU and CPU use shared memory. Estimate the current free memory in the system.
uint64_t freeMem = mvkGetAvailableMemorySize();
if (freeMem) { return freeMem; }
#endif
return 128 * MEBI; // Conservative minimum for macOS GPU's & iOS shared memory
}
uint64_t MVKPhysicalDevice::getCurrentAllocatedSize() {
if ( [_mtlDevice respondsToSelector: @selector(currentAllocatedSize)] ) {
return _mtlDevice.currentAllocatedSize;
}
#if MVK_IOS
// We can use the current memory used by this process as a reasonable approximation.
return mvkGetUsedMemorySize();
#endif
#if MVK_MACOS
return 0;
#endif
} }
void MVKPhysicalDevice::initExtensions() { void MVKPhysicalDevice::initExtensions() {
@ -1699,7 +1934,24 @@ void MVKPhysicalDevice::logGPUInfo() {
logMsg += "\n\t\tvendorID: %#06x"; logMsg += "\n\t\tvendorID: %#06x";
logMsg += "\n\t\tdeviceID: %#06x"; logMsg += "\n\t\tdeviceID: %#06x";
logMsg += "\n\t\tpipelineCacheUUID: %s"; logMsg += "\n\t\tpipelineCacheUUID: %s";
logMsg += "\n\tsupports Metal Shading Language version %s and the following Metal Feature Sets:"; logMsg += "\n\tsupports the following Metal Versions, GPU's and Feature Sets:";
logMsg += "\n\t\tMetal Shading Language %s";
if (getSupportsGPUFamily(MTLGPUFamilyApple5)) { logMsg += "\n\t\tGPU Family Apple 5"; }
if (getSupportsGPUFamily(MTLGPUFamilyApple4)) { logMsg += "\n\t\tGPU Family Apple 4"; }
if (getSupportsGPUFamily(MTLGPUFamilyApple3)) { logMsg += "\n\t\tGPU Family Apple 3"; }
if (getSupportsGPUFamily(MTLGPUFamilyApple2)) { logMsg += "\n\t\tGPU Family Apple 2"; }
if (getSupportsGPUFamily(MTLGPUFamilyApple1)) { logMsg += "\n\t\tGPU Family Apple 1"; }
if (getSupportsGPUFamily(MTLGPUFamilyMac2)) { logMsg += "\n\t\tGPU Family Mac 2"; }
if (getSupportsGPUFamily(MTLGPUFamilyMac1)) { logMsg += "\n\t\tGPU Family Mac 1"; }
if (getSupportsGPUFamily(MTLGPUFamilyCommon3)) { logMsg += "\n\t\tGPU Family Common 3"; }
if (getSupportsGPUFamily(MTLGPUFamilyCommon2)) { logMsg += "\n\t\tGPU Family Common 2"; }
if (getSupportsGPUFamily(MTLGPUFamilyCommon1)) { logMsg += "\n\t\tGPU Family Common 1"; }
if (getSupportsGPUFamily(MTLGPUFamilyMacCatalyst2)) { logMsg += "\n\t\tGPU Family Mac Catalyst 2"; }
if (getSupportsGPUFamily(MTLGPUFamilyMacCatalyst1)) { logMsg += "\n\t\tGPU Family Mac Catalyst 1"; }
#if MVK_IOS #if MVK_IOS
if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily5_v1] ) { logMsg += "\n\t\tiOS GPU Family 5 v1"; } if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily5_v1] ) { logMsg += "\n\t\tiOS GPU Family 5 v1"; }
@ -2267,7 +2519,11 @@ MTLPixelFormat MVKDevice::getMTLPixelFormatFromVkFormat(VkFormat vkFormat, MVKBa
} }
VkDeviceSize MVKDevice::getVkFormatTexelBufferAlignment(VkFormat format, MVKBaseObject* mvkObj) { VkDeviceSize MVKDevice::getVkFormatTexelBufferAlignment(VkFormat format, MVKBaseObject* mvkObj) {
VkDeviceSize deviceAlignment = mvkMTLPixelFormatLinearTextureAlignment(getMTLPixelFormatFromVkFormat(format, mvkObj), getMTLDevice()); VkDeviceSize deviceAlignment = 0;
id<MTLDevice> mtlDev = getMTLDevice();
if ([mtlDev respondsToSelector: @selector(minimumLinearTextureAlignmentForPixelFormat:)]) {
deviceAlignment = [mtlDev minimumLinearTextureAlignmentForPixelFormat: getMTLPixelFormatFromVkFormat(format, mvkObj)];
}
return deviceAlignment ? deviceAlignment : _pProperties->limits.minTexelBufferOffsetAlignment; return deviceAlignment ? deviceAlignment : _pProperties->limits.minTexelBufferOffsetAlignment;
} }
@ -2645,139 +2901,6 @@ MVKDevice::~MVKDevice() {
#pragma mark - #pragma mark -
#pragma mark Support functions #pragma mark Support functions
uint64_t mvkRecommendedMaxWorkingSetSize(id<MTLDevice> mtlDevice) {
#if MVK_MACOS
if ( [mtlDevice respondsToSelector: @selector(recommendedMaxWorkingSetSize)]) {
return mtlDevice.recommendedMaxWorkingSetSize;
}
#endif
#if MVK_IOS
// GPU and CPU use shared memory. Estimate the current free memory in the system.
mach_port_t host_port;
mach_msg_type_number_t host_size;
vm_size_t pagesize;
host_port = mach_host_self();
host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
host_page_size(host_port, &pagesize);
vm_statistics_data_t vm_stat;
if (host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size) == KERN_SUCCESS ) {
return vm_stat.free_count * pagesize;
}
#endif
return 128 * MEBI; // Conservative minimum for macOS GPU's & iOS shared memory
}
#if MVK_MACOS
static uint32_t mvkGetEntryProperty(io_registry_entry_t entry, CFStringRef propertyName) {
uint32_t value = 0;
CFTypeRef cfProp = IORegistryEntrySearchCFProperty(entry,
kIOServicePlane,
propertyName,
kCFAllocatorDefault,
kIORegistryIterateRecursively |
kIORegistryIterateParents);
if (cfProp) {
const uint32_t* pValue = reinterpret_cast<const uint32_t*>(CFDataGetBytePtr((CFDataRef)cfProp));
if (pValue) { value = *pValue; }
CFRelease(cfProp);
}
return value;
}
void mvkPopulateGPUInfo(VkPhysicalDeviceProperties& devProps, id<MTLDevice> mtlDevice) {
static const uint32_t kIntelVendorId = 0x8086;
bool isFound = false;
bool isIntegrated = mtlDevice.isLowPower;
devProps.deviceType = isIntegrated ? VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU : VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU;
strlcpy(devProps.deviceName, mtlDevice.name.UTF8String, VK_MAX_PHYSICAL_DEVICE_NAME_SIZE);
// If the device has an associated registry ID, we can use that to get the associated IOKit node.
// The match dictionary is consumed by IOServiceGetMatchingServices and does not need to be released.
io_registry_entry_t entry;
uint64_t regID = mvkGetRegistryID(mtlDevice);
if (regID) {
entry = IOServiceGetMatchingService(kIOMasterPortDefault, IORegistryEntryIDMatching(regID));
if (entry) {
// That returned the IOGraphicsAccelerator nub. Its parent, then, is the actual
// PCI device.
io_registry_entry_t parent;
if (IORegistryEntryGetParentEntry(entry, kIOServicePlane, &parent) == kIOReturnSuccess) {
isFound = true;
devProps.vendorID = mvkGetEntryProperty(parent, CFSTR("vendor-id"));
devProps.deviceID = mvkGetEntryProperty(parent, CFSTR("device-id"));
IOObjectRelease(parent);
}
IOObjectRelease(entry);
}
}
// Iterate all GPU's, looking for a match.
// The match dictionary is consumed by IOServiceGetMatchingServices and does not need to be released.
io_iterator_t entryIterator;
if (!isFound && IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching("IOPCIDevice"),
&entryIterator) == kIOReturnSuccess) {
while ( !isFound && (entry = IOIteratorNext(entryIterator)) ) {
if (mvkGetEntryProperty(entry, CFSTR("class-code")) == 0x30000) { // 0x30000 : DISPLAY_VGA
// The Intel GPU will always be marked as integrated.
// Return on a match of either Intel && low power, or non-Intel and non-low-power.
uint32_t vendorID = mvkGetEntryProperty(entry, CFSTR("vendor-id"));
if ( (vendorID == kIntelVendorId) == isIntegrated) {
isFound = true;
devProps.vendorID = vendorID;
devProps.deviceID = mvkGetEntryProperty(entry, CFSTR("device-id"));
}
}
}
IOObjectRelease(entryIterator);
}
}
#endif //MVK_MACOS
#if MVK_IOS
void mvkPopulateGPUInfo(VkPhysicalDeviceProperties& devProps, id<MTLDevice> mtlDevice) {
// For iOS devices, the Device ID is the SoC model (A8, A10X...), in the hex form 0xaMMX, where
//"a" is the Apple brand, MM is the SoC model number (8, 10...) and X is 1 for X version, 0 for other.
NSUInteger coreCnt = NSProcessInfo.processInfo.processorCount;
uint32_t devID = 0xa070;
if ([mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily5_v1]) {
devID = 0xa120;
} else if ([mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily4_v1]) {
devID = 0xa110;
} else if ([mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v1]) {
devID = coreCnt > 2 ? 0xa101 : 0xa100;
} else if ([mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v1]) {
devID = coreCnt > 2 ? 0xa081 : 0xa080;
}
devProps.vendorID = 0x0000106b; // Apple's PCI ID
devProps.deviceID = devID;
devProps.deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
strlcpy(devProps.deviceName, mtlDevice.name.UTF8String, VK_MAX_PHYSICAL_DEVICE_NAME_SIZE);
}
#endif //MVK_IOS
uint64_t mvkGetRegistryID(id<MTLDevice> mtlDevice) { uint64_t mvkGetRegistryID(id<MTLDevice> mtlDevice) {
return [mtlDevice respondsToSelector: @selector(registryID)] ? mtlDevice.registryID : 0; return [mtlDevice respondsToSelector: @selector(registryID)] ? mtlDevice.registryID : 0;
} }
VkDeviceSize mvkMTLPixelFormatLinearTextureAlignment(MTLPixelFormat mtlPixelFormat,
id<MTLDevice> mtlDevice) {
if ([mtlDevice respondsToSelector: @selector(minimumLinearTextureAlignmentForPixelFormat:)]) {
return [mtlDevice minimumLinearTextureAlignmentForPixelFormat: mtlPixelFormat];
} else {
return 0;
}
}

View File

@ -104,6 +104,9 @@ public:
/** Returns the Metal buffer underlying this memory allocation. */ /** Returns the Metal buffer underlying this memory allocation. */
inline id<MTLBuffer> getMTLBuffer() { return _mtlBuffer; } inline id<MTLBuffer> getMTLBuffer() { return _mtlBuffer; }
/** Returns the Metal heap underlying this memory allocation. */
inline id<MTLHeap> getMTLHeap() { return _mtlHeap; }
/** Returns the Metal storage mode used by this memory allocation. */ /** Returns the Metal storage mode used by this memory allocation. */
inline MTLStorageMode getMTLStorageMode() { return _mtlStorageMode; } inline MTLStorageMode getMTLStorageMode() { return _mtlStorageMode; }
@ -133,6 +136,7 @@ protected:
void removeBuffer(MVKBuffer* mvkBuff); void removeBuffer(MVKBuffer* mvkBuff);
VkResult addImage(MVKImage* mvkImg); VkResult addImage(MVKImage* mvkImg);
void removeImage(MVKImage* mvkImg); void removeImage(MVKImage* mvkImg);
bool ensureMTLHeap();
bool ensureMTLBuffer(); bool ensureMTLBuffer();
bool ensureHostMemory(); bool ensureHostMemory();
void freeHostMemory(); void freeHostMemory();
@ -145,6 +149,7 @@ protected:
VkDeviceSize _mapOffset = 0; VkDeviceSize _mapOffset = 0;
VkDeviceSize _mapSize = 0; VkDeviceSize _mapSize = 0;
id<MTLBuffer> _mtlBuffer = nil; id<MTLBuffer> _mtlBuffer = nil;
id<MTLHeap> _mtlHeap = nil;
void* _pMemory = nullptr; void* _pMemory = nullptr;
void* _pHostMemory = nullptr; void* _pHostMemory = nullptr;
bool _isMapped = false; bool _isMapped = false;

View File

@ -32,7 +32,10 @@ using namespace std;
#pragma mark MVKDeviceMemory #pragma mark MVKDeviceMemory
void MVKDeviceMemory::propogateDebugName() { setLabelIfNotNil(_mtlBuffer, _debugName); } void MVKDeviceMemory::propogateDebugName() {
setLabelIfNotNil(_mtlHeap, _debugName);
setLabelIfNotNil(_mtlBuffer, _debugName);
}
VkResult MVKDeviceMemory::map(VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData) { VkResult MVKDeviceMemory::map(VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData) {
@ -86,9 +89,12 @@ VkResult MVKDeviceMemory::flushToDevice(VkDeviceSize offset, VkDeviceSize size,
} }
#endif #endif
// If we have an MTLHeap object, there's no need to sync memory manually between images and the buffer.
if (!_mtlHeap) {
lock_guard<mutex> lock(_rezLock); lock_guard<mutex> lock(_rezLock);
for (auto& img : _images) { img->flushToDevice(offset, memSize); } for (auto& img : _images) { img->flushToDevice(offset, memSize); }
} }
}
return VK_SUCCESS; return VK_SUCCESS;
} }
@ -98,7 +104,7 @@ VkResult MVKDeviceMemory::pullFromDevice(VkDeviceSize offset,
MVKMTLBlitEncoder* pBlitEnc) { MVKMTLBlitEncoder* pBlitEnc) {
// Coherent memory is flushed on unmap(), so it is only flushed if forced // Coherent memory is flushed on unmap(), so it is only flushed if forced
VkDeviceSize memSize = adjustMemorySize(size, offset); VkDeviceSize memSize = adjustMemorySize(size, offset);
if (memSize > 0 && isMemoryHostAccessible() && (evenIfCoherent || !isMemoryHostCoherent()) ) { if (memSize > 0 && isMemoryHostAccessible() && (evenIfCoherent || !isMemoryHostCoherent()) && !_mtlHeap) {
lock_guard<mutex> lock(_rezLock); lock_guard<mutex> lock(_rezLock);
for (auto& img : _images) { img->pullFromDevice(offset, memSize); } for (auto& img : _images) { img->pullFromDevice(offset, memSize); }
@ -153,8 +159,7 @@ VkResult MVKDeviceMemory::addImage(MVKImage* mvkImg) {
return reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "Could not bind VkImage %p to a VkDeviceMemory dedicated to resource %p. A dedicated allocation may only be used with the resource it was dedicated to.", mvkImg, getDedicatedResource() ); return reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "Could not bind VkImage %p to a VkDeviceMemory dedicated to resource %p. A dedicated allocation may only be used with the resource it was dedicated to.", mvkImg, getDedicatedResource() );
} }
if (!_isDedicated) if (!_isDedicated) { _images.push_back(mvkImg); }
_images.push_back(mvkImg);
return VK_SUCCESS; return VK_SUCCESS;
} }
@ -164,6 +169,36 @@ void MVKDeviceMemory::removeImage(MVKImage* mvkImg) {
mvkRemoveAllOccurances(_images, mvkImg); mvkRemoveAllOccurances(_images, mvkImg);
} }
// Ensures that this instance is backed by a MTLHeap object,
// creating the MTLHeap if needed, and returns whether it was successful.
bool MVKDeviceMemory::ensureMTLHeap() {
if (_mtlHeap) { return true; }
// Don't bother if we don't have placement heaps.
if (!getDevice()->_pMetalFeatures->placementHeaps) { return true; }
#if MVK_MACOS
// MTLHeaps on Mac must use private storage for now.
if (_mtlStorageMode != MTLStorageModePrivate) { return true; }
#endif
MTLHeapDescriptor* heapDesc = [MTLHeapDescriptor new];
heapDesc.type = MTLHeapTypePlacement;
heapDesc.resourceOptions = getMTLResourceOptions();
// For now, use tracked resources. Later, we should probably default
// to untracked, since Vulkan uses explicit barriers anyway.
heapDesc.hazardTrackingMode = MTLHazardTrackingModeTracked;
heapDesc.size = _allocationSize;
_mtlHeap = [_device->getMTLDevice() newHeapWithDescriptor: heapDesc]; // retained
[heapDesc release];
if (!_mtlHeap) { return false; }
propogateDebugName();
return true;
}
// Ensures that this instance is backed by a MTLBuffer object, // Ensures that this instance is backed by a MTLBuffer object,
// creating the MTLBuffer if needed, and returns whether it was successful. // creating the MTLBuffer if needed, and returns whether it was successful.
bool MVKDeviceMemory::ensureMTLBuffer() { bool MVKDeviceMemory::ensureMTLBuffer() {
@ -175,12 +210,20 @@ bool MVKDeviceMemory::ensureMTLBuffer() {
if (memLen > _device->_pMetalFeatures->maxMTLBufferSize) { return false; } if (memLen > _device->_pMetalFeatures->maxMTLBufferSize) { return false; }
// If host memory was already allocated, it is copied into the new MTLBuffer, and then released. // If host memory was already allocated, it is copied into the new MTLBuffer, and then released.
if (_mtlHeap) {
_mtlBuffer = [_mtlHeap newBufferWithLength: memLen options: getMTLResourceOptions() offset: 0]; // retained
if (_pHostMemory) { if (_pHostMemory) {
memcpy(_mtlBuffer.contents, _pHostMemory, memLen);
freeHostMemory();
}
[_mtlBuffer makeAliasable];
} else if (_pHostMemory) {
_mtlBuffer = [getMTLDevice() newBufferWithBytes: _pHostMemory length: memLen options: getMTLResourceOptions()]; // retained _mtlBuffer = [getMTLDevice() newBufferWithBytes: _pHostMemory length: memLen options: getMTLResourceOptions()]; // retained
freeHostMemory(); freeHostMemory();
} else { } else {
_mtlBuffer = [getMTLDevice() newBufferWithLength: memLen options: getMTLResourceOptions()]; // retained _mtlBuffer = [getMTLDevice() newBufferWithLength: memLen options: getMTLResourceOptions()]; // retained
} }
if (!_mtlBuffer) { return false; }
_pMemory = isMemoryHostAccessible() ? _mtlBuffer.contents : nullptr; _pMemory = isMemoryHostAccessible() ? _mtlBuffer.contents : nullptr;
propogateDebugName(); propogateDebugName();
@ -267,6 +310,15 @@ MVKDeviceMemory::MVKDeviceMemory(MVKDevice* device,
return; return;
} }
// If we can, create a MTLHeap. This should happen before creating the buffer
// allowing us to map its contents.
if (!dedicatedImage && !dedicatedBuffer) {
if (!ensureMTLHeap()) {
setConfigurationResult(reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "Could not allocate VkDeviceMemory of size %llu bytes.", _allocationSize));
return;
}
}
// If memory needs to be coherent it must reside in an MTLBuffer, since an open-ended map() must work. // If memory needs to be coherent it must reside in an MTLBuffer, since an open-ended map() must work.
if (isMemoryHostCoherent() && !ensureMTLBuffer() ) { if (isMemoryHostCoherent() && !ensureMTLBuffer() ) {
setConfigurationResult(reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "Could not allocate a host-coherent VkDeviceMemory of size %llu bytes. The maximum memory-aligned size of a host-coherent VkDeviceMemory is %llu bytes.", _allocationSize, _device->_pMetalFeatures->maxMTLBufferSize)); setConfigurationResult(reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "Could not allocate a host-coherent VkDeviceMemory of size %llu bytes. The maximum memory-aligned size of a host-coherent VkDeviceMemory is %llu bytes.", _allocationSize, _device->_pMetalFeatures->maxMTLBufferSize));

View File

@ -275,6 +275,7 @@ protected:
bool _usesTexelBuffer; bool _usesTexelBuffer;
bool _isLinear; bool _isLinear;
bool _is3DCompressed; bool _is3DCompressed;
bool _isAliasable;
}; };

View File

@ -197,10 +197,8 @@ VkResult MVKImage::getMemoryRequirements(const void*, VkMemoryRequirements2* pMe
switch (next->sType) { switch (next->sType) {
case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS: { case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS: {
auto* dedicatedReqs = (VkMemoryDedicatedRequirements*)next; auto* dedicatedReqs = (VkMemoryDedicatedRequirements*)next;
// TODO: Maybe someday we could do something with MTLHeaps bool writable = mvkIsAnyFlagEnabled(_usage, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
// and allocate non-dedicated memory from them. For now, we dedicatedReqs->prefersDedicatedAllocation = !_usesTexelBuffer && (writable || !_device->_pMetalFeatures->placementHeaps);
// always prefer dedicated allocations for non-buffer-backed images.
dedicatedReqs->prefersDedicatedAllocation = !_usesTexelBuffer;
dedicatedReqs->requiresDedicatedAllocation = VK_FALSE; dedicatedReqs->requiresDedicatedAllocation = VK_FALSE;
break; break;
} }
@ -229,7 +227,7 @@ bool MVKImage::validateUseTexelBuffer() {
bool isUncompressed = blockExt.width == 1 && blockExt.height == 1; bool isUncompressed = blockExt.width == 1 && blockExt.height == 1;
bool useTexelBuffer = _device->_pMetalFeatures->texelBuffers; // Texel buffers available bool useTexelBuffer = _device->_pMetalFeatures->texelBuffers; // Texel buffers available
useTexelBuffer = useTexelBuffer && isMemoryHostAccessible() && _isLinear && isUncompressed; // Applicable memory layout useTexelBuffer = useTexelBuffer && (isMemoryHostAccessible() || _device->_pMetalFeatures->placementHeaps) && _isLinear && isUncompressed; // Applicable memory layout
useTexelBuffer = useTexelBuffer && _deviceMemory && _deviceMemory->_mtlBuffer; // Buffer is available to overlay useTexelBuffer = useTexelBuffer && _deviceMemory && _deviceMemory->_mtlBuffer; // Buffer is available to overlay
#if MVK_MACOS #if MVK_MACOS
@ -350,6 +348,10 @@ id<MTLTexture> MVKImage::newMTLTexture() {
mtlTex = [_deviceMemory->_mtlBuffer newTextureWithDescriptor: mtlTexDesc mtlTex = [_deviceMemory->_mtlBuffer newTextureWithDescriptor: mtlTexDesc
offset: getDeviceMemoryOffset() offset: getDeviceMemoryOffset()
bytesPerRow: _subresources[0].layout.rowPitch]; bytesPerRow: _subresources[0].layout.rowPitch];
} else if (_deviceMemory->_mtlHeap) {
mtlTex = [_deviceMemory->_mtlHeap newTextureWithDescriptor: mtlTexDesc
offset: getDeviceMemoryOffset()];
if (_isAliasable) [mtlTex makeAliasable];
} else { } else {
mtlTex = [getMTLDevice() newTextureWithDescriptor: mtlTexDesc]; mtlTex = [getMTLDevice() newTextureWithDescriptor: mtlTexDesc];
} }
@ -448,7 +450,7 @@ MTLTextureDescriptor* MVKImage::newMTLTextureDescriptor() {
MTLTextureDescriptor* mtlTexDesc = [MTLTextureDescriptor new]; // retained MTLTextureDescriptor* mtlTexDesc = [MTLTextureDescriptor new]; // retained
#if MVK_MACOS #if MVK_MACOS
if (_is3DCompressed) { if (_is3DCompressed) {
// Metal doesn't yet support 3D compressed textures, so we'll decompress // Metal before 3.0 doesn't support 3D compressed textures, so we'll decompress
// the texture ourselves. This, then, is the *uncompressed* format. // the texture ourselves. This, then, is the *uncompressed* format.
mtlTexDesc.pixelFormat = MTLPixelFormatBGRA8Unorm; mtlTexDesc.pixelFormat = MTLPixelFormatBGRA8Unorm;
} else { } else {
@ -620,17 +622,26 @@ MVKImage::MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo) : MV
_mtlTextureType = mvkMTLTextureTypeFromVkImageType(pCreateInfo->imageType, _arrayLayers, _samples > VK_SAMPLE_COUNT_1_BIT); _mtlTextureType = mvkMTLTextureTypeFromVkImageType(pCreateInfo->imageType, _arrayLayers, _samples > VK_SAMPLE_COUNT_1_BIT);
_usage = pCreateInfo->usage; _usage = pCreateInfo->usage;
_is3DCompressed = (pCreateInfo->imageType == VK_IMAGE_TYPE_3D) && (mvkFormatTypeFromVkFormat(pCreateInfo->format) == kMVKFormatCompressed); _is3DCompressed = (pCreateInfo->imageType == VK_IMAGE_TYPE_3D) && (mvkFormatTypeFromVkFormat(pCreateInfo->format) == kMVKFormatCompressed) && !getDevice()->_pMetalFeatures->native3DCompressedTextures;
_isDepthStencilAttachment = (mvkAreAllFlagsEnabled(pCreateInfo->usage, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) || _isDepthStencilAttachment = (mvkAreAllFlagsEnabled(pCreateInfo->usage, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ||
mvkAreAllFlagsEnabled(mvkVkFormatProperties(pCreateInfo->format).optimalTilingFeatures, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)); mvkAreAllFlagsEnabled(mvkVkFormatProperties(pCreateInfo->format).optimalTilingFeatures, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT));
_canSupportMTLTextureView = !_isDepthStencilAttachment || _device->_pMetalFeatures->stencilViews; _canSupportMTLTextureView = !_isDepthStencilAttachment || _device->_pMetalFeatures->stencilViews;
_hasExpectedTexelSize = (mvkMTLPixelFormatBytesPerBlock(_mtlPixelFormat) == mvkVkFormatBytesPerBlock(pCreateInfo->format)); _hasExpectedTexelSize = (mvkMTLPixelFormatBytesPerBlock(_mtlPixelFormat) == mvkVkFormatBytesPerBlock(pCreateInfo->format));
if (!_isLinear && _device->_pMetalFeatures->placementHeaps) {
MTLTextureDescriptor *mtlTexDesc = newMTLTextureDescriptor(); // temp retain
MTLSizeAndAlign sizeAndAlign = [_device->getMTLDevice() heapTextureSizeAndAlignWithDescriptor: mtlTexDesc];
[mtlTexDesc release];
_byteCount = sizeAndAlign.size;
_byteAlignment = sizeAndAlign.align;
_isAliasable = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_ALIAS_BIT);
} else {
// Calc _byteCount after _byteAlignment // Calc _byteCount after _byteAlignment
_byteAlignment = _isLinear ? _device->getVkFormatTexelBufferAlignment(pCreateInfo->format, this) : mvkEnsurePowerOfTwo(mvkVkFormatBytesPerBlock(pCreateInfo->format)); _byteAlignment = _isLinear ? _device->getVkFormatTexelBufferAlignment(pCreateInfo->format, this) : mvkEnsurePowerOfTwo(mvkVkFormatBytesPerBlock(pCreateInfo->format));
for (uint32_t mipLvl = 0; mipLvl < _mipLevels; mipLvl++) { for (uint32_t mipLvl = 0; mipLvl < _mipLevels; mipLvl++) {
_byteCount += getBytesPerLayer(mipLvl) * _extent.depth * _arrayLayers; _byteCount += getBytesPerLayer(mipLvl) * _extent.depth * _arrayLayers;
} }
}
initSubresources(pCreateInfo); initSubresources(pCreateInfo);
} }
@ -646,9 +657,13 @@ void MVKImage::validateConfig(const VkImageCreateInfo* pCreateInfo, bool isAttac
} }
#endif #endif
#if MVK_MACOS #if MVK_MACOS
if (isCompressed && !is2D && !mvkCanDecodeFormat(pCreateInfo->format)) { if (isCompressed && !is2D) {
if (pCreateInfo->imageType != VK_IMAGE_TYPE_3D) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, compressed formats may only be used with 2D or 3D images."));
} else if (!getDevice()->_pMetalFeatures->native3DCompressedTextures && !mvkCanDecodeFormat(pCreateInfo->format)) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, the %s compressed format may only be used with 2D images.", mvkVkFormatName(pCreateInfo->format))); setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, the %s compressed format may only be used with 2D images.", mvkVkFormatName(pCreateInfo->format)));
} }
}
#endif #endif
if ((mvkFormatTypeFromVkFormat(pCreateInfo->format) == kMVKFormatDepthStencil) && !is2D ) { if ((mvkFormatTypeFromVkFormat(pCreateInfo->format) == kMVKFormatDepthStencil) && !is2D ) {
@ -859,15 +874,25 @@ id<MTLTexture> MVKImageView::getMTLTexture() {
// overlay on the Metal texture of the underlying image. // overlay on the Metal texture of the underlying image.
id<MTLTexture> MVKImageView::newMTLTexture() { id<MTLTexture> MVKImageView::newMTLTexture() {
MTLTextureType mtlTextureType = _mtlTextureType; MTLTextureType mtlTextureType = _mtlTextureType;
NSRange sliceRange = NSMakeRange(_subresourceRange.baseArrayLayer, _subresourceRange.layerCount);
// Fake support for 2D views of 3D textures. // Fake support for 2D views of 3D textures.
if (_image->getImageType() == VK_IMAGE_TYPE_3D && if (_image->getImageType() == VK_IMAGE_TYPE_3D &&
(mtlTextureType == MTLTextureType2D || mtlTextureType == MTLTextureType2DArray)) { (mtlTextureType == MTLTextureType2D || mtlTextureType == MTLTextureType2DArray)) {
mtlTextureType = MTLTextureType3D; mtlTextureType = MTLTextureType3D;
sliceRange = NSMakeRange(0, 1);
} }
if (getDevice()->_pMetalFeatures->nativeTextureSwizzle && _packedSwizzle) {
return [_image->getMTLTexture() newTextureViewWithPixelFormat: _mtlPixelFormat return [_image->getMTLTexture() newTextureViewWithPixelFormat: _mtlPixelFormat
textureType: mtlTextureType textureType: mtlTextureType
levels: NSMakeRange(_subresourceRange.baseMipLevel, _subresourceRange.levelCount) levels: NSMakeRange(_subresourceRange.baseMipLevel, _subresourceRange.levelCount)
slices: NSMakeRange(_subresourceRange.baseArrayLayer, _subresourceRange.layerCount)]; // retained slices: sliceRange
swizzle: mvkMTLTextureSwizzleChannelsFromVkComponentMapping(mvkUnpackSwizzle(_packedSwizzle))]; // retained
} else {
return [_image->getMTLTexture() newTextureViewWithPixelFormat: _mtlPixelFormat
textureType: mtlTextureType
levels: NSMakeRange(_subresourceRange.baseMipLevel, _subresourceRange.levelCount)
slices: sliceRange]; // retained
}
} }
@ -908,12 +933,12 @@ MVKImageView::MVKImageView(MVKDevice* device,
_subresourceRange.layerCount = _image ? (_image->getLayerCount() - _subresourceRange.baseArrayLayer) : 1; _subresourceRange.layerCount = _image ? (_image->getLayerCount() - _subresourceRange.baseArrayLayer) : 1;
} }
bool useShaderSwizzle; bool useSwizzle;
bool isMultisample = _image ? _image->getSampleCount() != VK_SAMPLE_COUNT_1_BIT : false; bool isMultisample = _image ? _image->getSampleCount() != VK_SAMPLE_COUNT_1_BIT : false;
_mtlTexture = nil; _mtlTexture = nil;
_mtlPixelFormat = getSwizzledMTLPixelFormat(pCreateInfo->format, pCreateInfo->components, useShaderSwizzle, _mtlPixelFormat = getSwizzledMTLPixelFormat(pCreateInfo->format, pCreateInfo->components, useSwizzle,
(_device ? _device->_pMVKConfig : pAltMVKConfig)); (_device ? _device->_pMVKConfig : pAltMVKConfig));
_packedSwizzle = useShaderSwizzle ? mvkPackSwizzle(pCreateInfo->components) : 0; _packedSwizzle = useSwizzle ? mvkPackSwizzle(pCreateInfo->components) : 0;
_mtlTextureType = mvkMTLTextureTypeFromVkImageViewType(pCreateInfo->viewType, isMultisample); _mtlTextureType = mvkMTLTextureTypeFromVkImageViewType(pCreateInfo->viewType, isMultisample);
initMTLTextureViewSupport(); initMTLTextureViewSupport();
@ -944,20 +969,22 @@ void MVKImageView::validateImageViewConfig(const VkImageViewCreateInfo* pCreateI
// Returns a MTLPixelFormat, based on the MTLPixelFormat converted from the VkFormat, but possibly // Returns a MTLPixelFormat, based on the MTLPixelFormat converted from the VkFormat, but possibly
// modified by the swizzles defined in the VkComponentMapping of the VkImageViewCreateInfo. // modified by the swizzles defined in the VkComponentMapping of the VkImageViewCreateInfo.
// Metal does not support general per-texture swizzles, so if the swizzle is not an identity swizzle, this // Metal prior to version 3.0 does not support general per-texture swizzles, so if the swizzle is not an
// function attempts to find an alternate MTLPixelFormat that coincidentally matches the swizzled format. // identity swizzle, this function attempts to find an alternate MTLPixelFormat that coincidentally
// If a replacement MTLFormat was found, it is returned and useShaderSwizzle is set to false. // matches the swizzled format.
// If a replacement MTLFormat was found, it is returned and useSwizzle is set to false.
// If a replacement MTLFormat could not be found, the original MTLPixelFormat is returned, and the // If a replacement MTLFormat could not be found, the original MTLPixelFormat is returned, and the
// useShaderSwizzle is set to true, indicating that shader swizzling should be used for this image view. // useSwizzle is set to true, indicating that either native or shader swizzling should be used for
// this image view.
// The config is used to test whether full shader swizzle support is available, and to report an error if not. // The config is used to test whether full shader swizzle support is available, and to report an error if not.
MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format, MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format,
VkComponentMapping components, VkComponentMapping components,
bool& useShaderSwizzle, bool& useSwizzle,
const MVKConfiguration* pMVKConfig) { const MVKConfiguration* pMVKConfig) {
// Attempt to find a valid format transformation swizzle first. // Attempt to find a valid format transformation swizzle first.
MTLPixelFormat mtlPF = getMTLPixelFormatFromVkFormat(format); MTLPixelFormat mtlPF = getMTLPixelFormatFromVkFormat(format);
useShaderSwizzle = false; useSwizzle = false;
#define SWIZZLE_MATCHES(R, G, B, A) mvkVkComponentMappingsMatch(components, {VK_COMPONENT_SWIZZLE_ ##R, VK_COMPONENT_SWIZZLE_ ##G, VK_COMPONENT_SWIZZLE_ ##B, VK_COMPONENT_SWIZZLE_ ##A} ) #define SWIZZLE_MATCHES(R, G, B, A) mvkVkComponentMappingsMatch(components, {VK_COMPONENT_SWIZZLE_ ##R, VK_COMPONENT_SWIZZLE_ ##G, VK_COMPONENT_SWIZZLE_ ##B, VK_COMPONENT_SWIZZLE_ ##A} )
#define VK_COMPONENT_SWIZZLE_ANY VK_COMPONENT_SWIZZLE_MAX_ENUM #define VK_COMPONENT_SWIZZLE_ANY VK_COMPONENT_SWIZZLE_MAX_ENUM
@ -1027,9 +1054,9 @@ MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format,
// No format transformation swizzles were found, so unless we have an identity swizzle, we'll need to use shader swizzling. // No format transformation swizzles were found, so unless we have an identity swizzle, we'll need to use shader swizzling.
if ( !SWIZZLE_MATCHES(R, G, B, A)) { if ( !SWIZZLE_MATCHES(R, G, B, A)) {
useShaderSwizzle = true; useSwizzle = true;
if ( !pMVKConfig->fullImageViewSwizzle ) { if ( !pMVKConfig->fullImageViewSwizzle && !getDevice()->_pMetalFeatures->nativeTextureSwizzle ) {
const char* vkCmd = _image ? "vkCreateImageView(VkImageViewCreateInfo" : "vkGetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDeviceImageViewSupportEXTX"; const char* vkCmd = _image ? "vkCreateImageView(VkImageViewCreateInfo" : "vkGetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDeviceImageViewSupportEXTX";
const char* errMsg = ("The value of %s::components) (%s, %s, %s, %s), when applied to a VkImageView, requires full component swizzling to be enabled both at the" const char* errMsg = ("The value of %s::components) (%s, %s, %s, %s), when applied to a VkImageView, requires full component swizzling to be enabled both at the"
" time when the VkImageView is created and at the time any pipeline that uses that VkImageView is compiled. Full component swizzling can" " time when the VkImageView is created and at the time any pipeline that uses that VkImageView is compiled. Full component swizzling can"
@ -1061,12 +1088,8 @@ void MVKImageView::initMTLTextureViewSupport() {
(_mtlTextureType == _image->_mtlTextureType || (_mtlTextureType == _image->_mtlTextureType ||
((_mtlTextureType == MTLTextureType2D || _mtlTextureType == MTLTextureType2DArray) && is3D)) && ((_mtlTextureType == MTLTextureType2D || _mtlTextureType == MTLTextureType2DArray) && is3D)) &&
_subresourceRange.levelCount == _image->_mipLevels && _subresourceRange.levelCount == _image->_mipLevels &&
_subresourceRange.layerCount == (is3D ? _image->_extent.depth : _image->_arrayLayers)) { (is3D || _subresourceRange.layerCount == _image->_arrayLayers) &&
_useMTLTextureView = false; (!getDevice()->_pMetalFeatures->nativeTextureSwizzle || !_packedSwizzle)) {
}
// Never use views for subsets of 3D textures. Metal doesn't support them yet.
if (is3D && _subresourceRange.layerCount != _image->_extent.depth) {
_useMTLTextureView = false; _useMTLTextureView = false;
} }
} }

View File

@ -628,6 +628,7 @@ void MVKInstance::initProcAddrs() {
ADD_DVC_EXT2_ENTRY_POINT(vkGetDeviceGroupSurfacePresentModesKHR, KHR_SWAPCHAIN, KHR_DEVICE_GROUP); ADD_DVC_EXT2_ENTRY_POINT(vkGetDeviceGroupSurfacePresentModesKHR, KHR_SWAPCHAIN, KHR_DEVICE_GROUP);
ADD_DVC_EXT2_ENTRY_POINT(vkGetPhysicalDevicePresentRectanglesKHR, KHR_SWAPCHAIN, KHR_DEVICE_GROUP); ADD_DVC_EXT2_ENTRY_POINT(vkGetPhysicalDevicePresentRectanglesKHR, KHR_SWAPCHAIN, KHR_DEVICE_GROUP);
ADD_DVC_EXT2_ENTRY_POINT(vkAcquireNextImage2KHR, KHR_SWAPCHAIN, KHR_DEVICE_GROUP); ADD_DVC_EXT2_ENTRY_POINT(vkAcquireNextImage2KHR, KHR_SWAPCHAIN, KHR_DEVICE_GROUP);
ADD_DVC_EXT_ENTRY_POINT(vkSetHdrMetadataEXT, EXT_HDR_METADATA);
ADD_DVC_EXT_ENTRY_POINT(vkResetQueryPoolEXT, EXT_HOST_QUERY_RESET); ADD_DVC_EXT_ENTRY_POINT(vkResetQueryPoolEXT, EXT_HOST_QUERY_RESET);
ADD_DVC_EXT_ENTRY_POINT(vkDebugMarkerSetObjectTagEXT, EXT_DEBUG_MARKER); ADD_DVC_EXT_ENTRY_POINT(vkDebugMarkerSetObjectTagEXT, EXT_DEBUG_MARKER);
ADD_DVC_EXT_ENTRY_POINT(vkDebugMarkerSetObjectNameEXT, EXT_DEBUG_MARKER); ADD_DVC_EXT_ENTRY_POINT(vkDebugMarkerSetObjectNameEXT, EXT_DEBUG_MARKER);

View File

@ -1159,7 +1159,7 @@ void MVKGraphicsPipeline::initMVKShaderConverterContext(SPIRVToMSLConversionConf
shaderContext.options.mslOptions.enable_point_size_builtin = isRenderingPoints(pCreateInfo, reflectData); shaderContext.options.mslOptions.enable_point_size_builtin = isRenderingPoints(pCreateInfo, reflectData);
shaderContext.options.shouldFlipVertexY = _device->_pMVKConfig->shaderConversionFlipVertexY; shaderContext.options.shouldFlipVertexY = _device->_pMVKConfig->shaderConversionFlipVertexY;
shaderContext.options.mslOptions.swizzle_texture_samples = _fullImageViewSwizzle; shaderContext.options.mslOptions.swizzle_texture_samples = _fullImageViewSwizzle && !getDevice()->_pMetalFeatures->nativeTextureSwizzle;
shaderContext.options.mslOptions.tess_domain_origin_lower_left = pTessDomainOriginState && pTessDomainOriginState->domainOrigin == VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT; shaderContext.options.mslOptions.tess_domain_origin_lower_left = pTessDomainOriginState && pTessDomainOriginState->domainOrigin == VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT;
shaderContext.options.tessPatchKind = reflectData.patchKind; shaderContext.options.tessPatchKind = reflectData.patchKind;
@ -1346,7 +1346,7 @@ MVKMTLFunction MVKComputePipeline::getMTLFunction(const VkComputePipelineCreateI
shaderContext.options.entryPointStage = spv::ExecutionModelGLCompute; shaderContext.options.entryPointStage = spv::ExecutionModelGLCompute;
shaderContext.options.mslOptions.msl_version = _device->_pMetalFeatures->mslVersion; shaderContext.options.mslOptions.msl_version = _device->_pMetalFeatures->mslVersion;
shaderContext.options.mslOptions.texel_buffer_texture_width = _device->_pMetalFeatures->maxTextureDimension; shaderContext.options.mslOptions.texel_buffer_texture_width = _device->_pMetalFeatures->maxTextureDimension;
shaderContext.options.mslOptions.swizzle_texture_samples = _fullImageViewSwizzle; shaderContext.options.mslOptions.swizzle_texture_samples = _fullImageViewSwizzle && !getDevice()->_pMetalFeatures->nativeTextureSwizzle;
shaderContext.options.mslOptions.texture_buffer_native = _device->_pMetalFeatures->textureBuffers; shaderContext.options.mslOptions.texture_buffer_native = _device->_pMetalFeatures->textureBuffers;
shaderContext.options.mslOptions.dispatch_base = _allowsDispatchBase; shaderContext.options.mslOptions.dispatch_base = _allowsDispatchBase;

View File

@ -80,6 +80,9 @@ public:
/** Returns the specified performance stats structure. */ /** Returns the specified performance stats structure. */
const MVKSwapchainPerformance* getPerformanceStatistics() { return &_performanceStatistics; } const MVKSwapchainPerformance* getPerformanceStatistics() { return &_performanceStatistics; }
/** Adds HDR metadata to this swapchain. */
void setHDRMetadataEXT(const VkHdrMetadataEXT& metadata);
/** /**
* Registers a semaphore and/or fence that will be signaled when the image at the given index becomes available. * Registers a semaphore and/or fence that will be signaled when the image at the given index becomes available.
* This function accepts both a semaphore and a fence, and either none, one, or both may be provided. * This function accepts both a semaphore and a fence, and either none, one, or both may be provided.

View File

@ -30,6 +30,8 @@
#import "CAMetalLayer+MoltenVK.h" #import "CAMetalLayer+MoltenVK.h"
#import "MVKBlockObserver.h" #import "MVKBlockObserver.h"
#include <libkern/OSByteOrder.h>
using namespace std; using namespace std;
@ -276,6 +278,70 @@ void MVKSwapchain::markFrameInterval() {
} }
} }
#if MVK_MACOS
struct CIE1931XY {
uint16_t x;
uint16_t y;
} __attribute__((packed));
// According to D.3.28:
// "[x and y] specify the normalized x and y chromaticity coordinates, respectively...
// in normalized increments of 0.00002."
static inline uint16_t FloatToCIE1931Unorm(float x) { return OSSwapHostToBigInt16((uint16_t)(x * 100000 / 2)); }
static inline CIE1931XY VkXYColorEXTToCIE1931XY(VkXYColorEXT xy) {
return { FloatToCIE1931Unorm(xy.x), FloatToCIE1931Unorm(xy.y) };
}
#endif
void MVKSwapchain::setHDRMetadataEXT(const VkHdrMetadataEXT& metadata) {
#if MVK_MACOS
// We were given metadata as floats, but CA wants it as specified in H.265.
// More specifically, it wants "Mastering display colour volume" (D.2.28) and
// "Content light level information" (D.2.35) SEI messages, with big-endian
// integers. We have to convert.
struct ColorVolumeSEI {
CIE1931XY display_primaries[3]; // Green, blue, red
CIE1931XY white_point;
uint32_t max_display_mastering_luminance;
uint32_t min_display_mastering_luminance;
} __attribute__((packed));
struct LightLevelSEI {
uint16_t max_content_light_level;
uint16_t max_pic_average_light_level;
} __attribute__((packed));
ColorVolumeSEI colorVol;
LightLevelSEI lightLevel;
// According to D.3.28:
// "For describing mastering displays that use red, green, and blue colour
// primaries, it is suggested that index value c equal to 0 should correspond
// to the green primary, c equal to 1 should correspond to the blue primary
// and c equal to 2 should correspond to the red colour primary."
colorVol.display_primaries[0] = VkXYColorEXTToCIE1931XY(metadata.displayPrimaryGreen);
colorVol.display_primaries[1] = VkXYColorEXTToCIE1931XY(metadata.displayPrimaryBlue);
colorVol.display_primaries[2] = VkXYColorEXTToCIE1931XY(metadata.displayPrimaryRed);
colorVol.white_point = VkXYColorEXTToCIE1931XY(metadata.whitePoint);
// Later in D.3.28:
// "max_display_mastering_luminance and min_display_mastering_luminance specify
// the nominal maximum and minimum display luminance, respectively, of the mastering
// display in units of 0.0001 candelas [sic] per square metre."
// N.B. 1 nit = 1 cd/m^2
colorVol.max_display_mastering_luminance = OSSwapHostToBigInt32((uint32_t)(metadata.maxLuminance * 10000));
colorVol.min_display_mastering_luminance = OSSwapHostToBigInt32((uint32_t)(metadata.minLuminance * 10000));
lightLevel.max_content_light_level = OSSwapHostToBigInt16((uint16_t)metadata.maxContentLightLevel);
lightLevel.max_pic_average_light_level = OSSwapHostToBigInt16((uint16_t)metadata.maxFrameAverageLightLevel);
NSData* colorVolData = [NSData dataWithBytes: &colorVol length: sizeof(colorVol)];
NSData* lightLevelData = [NSData dataWithBytes: &lightLevel length: sizeof(lightLevel)];
CAEDRMetadata* caMetadata = [CAEDRMetadata HDR10MetadataWithDisplayInfo: colorVolData
contentInfo: lightLevelData
opticalOutputScale: 1];
_mtlLayer.EDRMetadata = caMetadata;
[caMetadata release];
[colorVolData release];
[lightLevelData release];
_mtlLayer.wantsExtendedDynamicRangeContent = YES;
#endif
}
#pragma mark Metal #pragma mark Metal
@ -342,41 +408,59 @@ void MVKSwapchain::initCAMetalLayer(const VkSwapchainCreateInfoKHR* pCreateInfo,
if (pCreateInfo->compositeAlpha != VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) { if (pCreateInfo->compositeAlpha != VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) {
_mtlLayer.opaque = pCreateInfo->compositeAlpha == VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; _mtlLayer.opaque = pCreateInfo->compositeAlpha == VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
} }
#if MVK_MACOS
switch (pCreateInfo->imageColorSpace) { switch (pCreateInfo->imageColorSpace) {
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); _mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
break; break;
case VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT: case VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT:
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3); _mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3);
_mtlLayer.wantsExtendedDynamicRangeContentMVK = YES;
break; break;
case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT: case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB); _mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
_mtlLayer.wantsExtendedDynamicRangeContentMVK = YES;
break;
case VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT:
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedSRGB);
_mtlLayer.wantsExtendedDynamicRangeContentMVK = YES;
break;
case VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT:
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
_mtlLayer.wantsExtendedDynamicRangeContentMVK = YES;
break; break;
case VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT: case VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT:
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceDCIP3); _mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceDCIP3);
_mtlLayer.wantsExtendedDynamicRangeContentMVK = YES;
break; break;
case VK_COLOR_SPACE_BT709_NONLINEAR_EXT: case VK_COLOR_SPACE_BT709_NONLINEAR_EXT:
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_709); _mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_709);
break; break;
case VK_COLOR_SPACE_BT2020_LINEAR_EXT:
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearITUR_2020);
_mtlLayer.wantsExtendedDynamicRangeContentMVK = YES;
break;
case VK_COLOR_SPACE_HDR10_ST2084_EXT:
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020_PQ_EOTF);
_mtlLayer.wantsExtendedDynamicRangeContentMVK = YES;
break;
case VK_COLOR_SPACE_HDR10_HLG_EXT:
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020_HLG);
_mtlLayer.wantsExtendedDynamicRangeContentMVK = YES;
break;
case VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT: case VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT:
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceAdobeRGB1998); _mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceAdobeRGB1998);
break; break;
case VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT:
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedSRGB);
break;
case VK_COLOR_SPACE_PASS_THROUGH_EXT: case VK_COLOR_SPACE_PASS_THROUGH_EXT:
default: default:
// Nothing - the default is not to do color matching. // Nothing - the default is not to do color matching.
break; break;
} }
#endif
_mtlLayerOrigDrawSize = _mtlLayer.updatedDrawableSizeMVK; _mtlLayerOrigDrawSize = _mtlLayer.updatedDrawableSizeMVK;
// TODO: set additional CAMetalLayer properties before extracting drawables: // TODO: set additional CAMetalLayer properties before extracting drawables:
// - presentsWithTransaction // - presentsWithTransaction
// - drawsAsynchronously // - drawsAsynchronously
// - wantsExtendedDynamicRangeContent (macOS only)
if ( [_mtlLayer.delegate isKindOfClass: [PLATFORM_VIEW_CLASS class]] ) { if ( [_mtlLayer.delegate isKindOfClass: [PLATFORM_VIEW_CLASS class]] ) {
// Sometimes, the owning view can replace its CAMetalLayer. In that case, the client // Sometimes, the owning view can replace its CAMetalLayer. In that case, the client

View File

@ -68,6 +68,7 @@ MVK_EXTENSION(EXT_debug_marker, EXT_DEBUG_MARKER, MVK_EXTENSION_DEVICE)
MVK_EXTENSION(EXT_debug_report, EXT_DEBUG_REPORT, MVK_EXTENSION_INSTANCE) MVK_EXTENSION(EXT_debug_report, EXT_DEBUG_REPORT, MVK_EXTENSION_INSTANCE)
MVK_EXTENSION(EXT_debug_utils, EXT_DEBUG_UTILS, MVK_EXTENSION_INSTANCE) MVK_EXTENSION(EXT_debug_utils, EXT_DEBUG_UTILS, MVK_EXTENSION_INSTANCE)
MVK_EXTENSION(EXT_fragment_shader_interlock, EXT_FRAGMENT_SHADER_INTERLOCK, MVK_EXTENSION_DEVICE) MVK_EXTENSION(EXT_fragment_shader_interlock, EXT_FRAGMENT_SHADER_INTERLOCK, MVK_EXTENSION_DEVICE)
MVK_EXTENSION(EXT_hdr_metadata, EXT_HDR_METADATA, MVK_EXTENSION_DEVICE)
MVK_EXTENSION(EXT_host_query_reset, EXT_HOST_QUERY_RESET, MVK_EXTENSION_DEVICE) MVK_EXTENSION(EXT_host_query_reset, EXT_HOST_QUERY_RESET, MVK_EXTENSION_DEVICE)
MVK_EXTENSION(EXT_memory_budget, EXT_MEMORY_BUDGET, MVK_EXTENSION_DEVICE) MVK_EXTENSION(EXT_memory_budget, EXT_MEMORY_BUDGET, MVK_EXTENSION_DEVICE)
MVK_EXTENSION(EXT_metal_surface, EXT_METAL_SURFACE, MVK_EXTENSION_INSTANCE) MVK_EXTENSION(EXT_metal_surface, EXT_METAL_SURFACE, MVK_EXTENSION_INSTANCE)

View File

@ -47,6 +47,9 @@ static VkExtensionProperties kVkExtProps_ ##EXT = mvkMakeExtProps(VK_ ##EXT ##_E
// Returns whether the specified properties are valid for this platform // Returns whether the specified properties are valid for this platform
static bool mvkIsSupportedOnPlatform(VkExtensionProperties* pProperties) { static bool mvkIsSupportedOnPlatform(VkExtensionProperties* pProperties) {
#if MVK_MACOS #if MVK_MACOS
if (pProperties == &kVkExtProps_EXT_HDR_METADATA) {
return mvkOSVersion() >= 10.15;
}
if (pProperties == &kVkExtProps_EXT_FRAGMENT_SHADER_INTERLOCK) { if (pProperties == &kVkExtProps_EXT_FRAGMENT_SHADER_INTERLOCK) {
return mvkOSVersion() >= 10.13; return mvkOSVersion() >= 10.13;
} }
@ -69,6 +72,7 @@ static bool mvkIsSupportedOnPlatform(VkExtensionProperties* pProperties) {
#endif #endif
#if MVK_IOS #if MVK_IOS
if (pProperties == &kVkExtProps_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE) { return false; } if (pProperties == &kVkExtProps_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE) { return false; }
if (pProperties == &kVkExtProps_EXT_HDR_METADATA) { return false; }
if (pProperties == &kVkExtProps_EXT_FRAGMENT_SHADER_INTERLOCK) { if (pProperties == &kVkExtProps_EXT_FRAGMENT_SHADER_INTERLOCK) {
return mvkOSVersion() >= 11.0; return mvkOSVersion() >= 11.0;
} }
@ -81,7 +85,9 @@ static bool mvkIsSupportedOnPlatform(VkExtensionProperties* pProperties) {
if (pProperties == &kVkExtProps_EXT_SHADER_STENCIL_EXPORT) { if (pProperties == &kVkExtProps_EXT_SHADER_STENCIL_EXPORT) {
return mvkOSVersion() >= 12.0; return mvkOSVersion() >= 12.0;
} }
if (pProperties == &kVkExtProps_EXT_SWAPCHAIN_COLOR_SPACE) { return false; } if (pProperties == &kVkExtProps_EXT_SWAPCHAIN_COLOR_SPACE) {
return mvkOSVersion() >= 9.0;
}
if (pProperties == &kVkExtProps_EXT_TEXEL_BUFFER_ALIGNMENT) { if (pProperties == &kVkExtProps_EXT_TEXEL_BUFFER_ALIGNMENT) {
return mvkOSVersion() >= 11.0; return mvkOSVersion() >= 11.0;
} }

View File

@ -56,4 +56,12 @@
*/ */
@property(nonatomic, readwrite) NSUInteger maximumDrawableCountMVK; @property(nonatomic, readwrite) NSUInteger maximumDrawableCountMVK;
/**
* Replacement for the wantsExtendedDynamicRangeContent property.
*
* This property allows support under all OS versions. Delegates to the wantsExtendedDynamicRangeContent
* property if it is available. Otherwise, returns NO when read and does nothing when set.
*/
@property(nonatomic, readwrite) BOOL wantsExtendedDynamicRangeContentMVK;
@end @end

View File

@ -62,4 +62,18 @@
if ( [self respondsToSelector: @selector(setMaximumDrawableCount:)] ) { self.maximumDrawableCount = count; } if ( [self respondsToSelector: @selector(setMaximumDrawableCount:)] ) { self.maximumDrawableCount = count; }
} }
-(BOOL) wantsExtendedDynamicRangeContentMVK {
#if MVK_MACOS
return self.wantsExtendedDynamicRangeContent;
#else
return NO;
#endif
}
-(void) setWantsExtendedDynamicRangeContentMVK: (BOOL) edr {
#if MVK_MACOS
self.wantsExtendedDynamicRangeContent = edr;
#endif
}
@end @end

View File

@ -932,6 +932,25 @@ MVK_PUBLIC_SYMBOL VkSampleCountFlagBits mvkVkSampleCountFlagBitsFromSampleCount(
return VkSampleCountFlagBits(sampleCount); return VkSampleCountFlagBits(sampleCount);
} }
MVK_PUBLIC_SYMBOL MTLTextureSwizzle mvkMTLTextureSwizzleFromVkComponentSwizzle(VkComponentSwizzle vkSwizzle) {
switch (vkSwizzle) {
case VK_COMPONENT_SWIZZLE_ZERO: return MTLTextureSwizzleZero;
case VK_COMPONENT_SWIZZLE_ONE: return MTLTextureSwizzleOne;
case VK_COMPONENT_SWIZZLE_R: return MTLTextureSwizzleRed;
case VK_COMPONENT_SWIZZLE_G: return MTLTextureSwizzleGreen;
case VK_COMPONENT_SWIZZLE_B: return MTLTextureSwizzleBlue;
case VK_COMPONENT_SWIZZLE_A: return MTLTextureSwizzleAlpha;
default: return MTLTextureSwizzleRed;
}
}
MVK_PUBLIC_SYMBOL MTLTextureSwizzleChannels mvkMTLTextureSwizzleChannelsFromVkComponentMapping(VkComponentMapping vkMapping) {
#define convert(v, d) \
v == VK_COMPONENT_SWIZZLE_IDENTITY ? MTLTextureSwizzle##d : mvkMTLTextureSwizzleFromVkComponentSwizzle(v)
return MTLTextureSwizzleChannelsMake(convert(vkMapping.r, Red), convert(vkMapping.g, Green), convert(vkMapping.b, Blue), convert(vkMapping.a, Alpha));
#undef convert
}
#pragma mark Mipmaps #pragma mark Mipmaps

View File

@ -2650,6 +2650,24 @@ MVK_PUBLIC_SYMBOL void vkSubmitDebugUtilsMessageEXT(
} }
#pragma mark -
#pragma mark VK_EXT_hdr_metadata extension
MVK_PUBLIC_SYMBOL void vkSetHdrMetadataEXT(
VkDevice device,
uint32_t swapchainCount,
const VkSwapchainKHR* pSwapchains,
const VkHdrMetadataEXT* pMetadata) {
MVKTraceVulkanCallStart();
for (uint32_t i = 0; i < swapchainCount; i++) {
auto* mvkSwpChn = (MVKSwapchain*)pSwapchains[i];
mvkSwpChn->setHDRMetadataEXT(pMetadata[i]);
}
MVKTraceVulkanCallEnd();
}
#pragma mark - #pragma mark -
#pragma mark VK_EXT_host_query_reset extension #pragma mark VK_EXT_host_query_reset extension

View File

@ -35,7 +35,7 @@ ${MVK_SAN} \
${MVK_LINK_WARN} \ ${MVK_LINK_WARN} \
-isysroot ${SDK_DIR} \ -isysroot ${SDK_DIR} \
-iframework ${MVK_SYS_FWK_DIR} \ -iframework ${MVK_SYS_FWK_DIR} \
-framework Metal ${MVK_IOSURFACE_FWK} -framework ${MVK_UX_FWK} -framework QuartzCore -framework IOKit -framework Foundation \ -framework Metal ${MVK_IOSURFACE_FWK} -framework ${MVK_UX_FWK} -framework QuartzCore -framework CoreGraphics -framework IOKit -framework Foundation \
--library-directory ${MVK_USR_LIB_DIR} \ --library-directory ${MVK_USR_LIB_DIR} \
-o "${BUILT_PRODUCTS_DIR}/dynamic/${MVK_DYLIB_NAME}" \ -o "${BUILT_PRODUCTS_DIR}/dynamic/${MVK_DYLIB_NAME}" \
-force_load "${BUILT_PRODUCTS_DIR}/lib${PRODUCT_NAME}.a" -force_load "${BUILT_PRODUCTS_DIR}/lib${PRODUCT_NAME}.a"