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:
commit
c6ea99e4db
@ -111,3 +111,17 @@ bool mvkGetEnvVarBool(std::string varName, bool* pWasFound = nullptr);
|
||||
int64_t val = wasFound ? ev : EV; \
|
||||
cfgVal = (int32_t)std::min(std::max(val, (int64_t)INT32_MIN), (int64_t)INT32_MAX); \
|
||||
} 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();
|
||||
|
||||
|
@ -18,7 +18,9 @@
|
||||
|
||||
|
||||
#include "MVKOSExtensions.h"
|
||||
#include <mach/mach_host.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include <mach/task.h>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@ -91,3 +93,43 @@ int64_t mvkGetEnvVarInt64(string varName, bool* pWasFound) {
|
||||
bool mvkGetEnvVarBool(std::string varName, bool* pWasFound) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,18 @@ For best results, use a Markdown reader.*
|
||||
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.
|
||||
- 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.
|
||||
@ -30,6 +40,7 @@ Released TBD
|
||||
|
||||
|
||||
|
||||
|
||||
MoltenVK 1.0.37
|
||||
---------------
|
||||
|
||||
@ -112,7 +123,7 @@ Released 2019/07/25
|
||||
- `VK_EXT_post_depth_coverage`
|
||||
- `VK_EXT_scalar_block_layout`
|
||||
- `VK_EXT_shader_stencil_export`
|
||||
- `VK_EXT_swapchain_colorspace`
|
||||
- `VK_EXT_swapchain_colorspace` (*macOS*)
|
||||
- `VK_EXT_texel_buffer_alignment`
|
||||
- `VK_AMD_shader_image_load_store_lod`
|
||||
- `VK_AMD_shader_trinary_minmax`
|
||||
|
@ -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. */
|
||||
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
|
||||
|
||||
|
@ -549,6 +549,9 @@ typedef struct {
|
||||
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 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;
|
||||
|
||||
/**
|
||||
|
@ -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 mip level 0, we can skip the copy and just decode
|
||||
// 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;
|
||||
info.srcRowStride = bytesPerRow & 0xffffffff;
|
||||
info.srcRowStrideHigh = bytesPerRow >> 32;
|
||||
|
@ -591,7 +591,7 @@ void MVKGraphicsResourcesCommandEncoderState::markDirty() {
|
||||
void MVKGraphicsResourcesCommandEncoderState::encodeImpl(uint32_t stage) {
|
||||
|
||||
MVKPipeline* pipeline = _cmdEncoder->_graphicsPipelineState.getPipeline();
|
||||
bool fullImageViewSwizzle = pipeline->fullImageViewSwizzle();
|
||||
bool fullImageViewSwizzle = pipeline->fullImageViewSwizzle() || _cmdEncoder->getDevice()->_pMetalFeatures->nativeTextureSwizzle;
|
||||
bool forTessellation = ((MVKGraphicsPipeline*)pipeline)->isTessellationPipeline();
|
||||
|
||||
if (stage == (forTessellation ? kMVKGraphicsStageVertex : kMVKGraphicsStageRasterization)) {
|
||||
|
@ -69,10 +69,10 @@ public:
|
||||
#pragma mark Metal
|
||||
|
||||
/** 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. */
|
||||
inline NSUInteger getMTLBufferOffset() { return _deviceMemoryOffset; }
|
||||
inline NSUInteger getMTLBufferOffset() { return _deviceMemory && _deviceMemory->getMTLHeap() ? 0 : _deviceMemoryOffset; }
|
||||
|
||||
|
||||
#pragma mark Construction
|
||||
@ -90,6 +90,7 @@ protected:
|
||||
VkBufferMemoryBarrier* pBufferMemoryBarrier);
|
||||
|
||||
VkBufferUsageFlags _usage;
|
||||
id<MTLBuffer> _mtlBuffer = nil;
|
||||
};
|
||||
|
||||
|
||||
|
@ -29,21 +29,28 @@ using namespace std;
|
||||
#pragma mark MVKBuffer
|
||||
|
||||
void MVKBuffer::propogateDebugName() {
|
||||
if (_debugName &&
|
||||
_deviceMemory &&
|
||||
if (!_debugName) { return; }
|
||||
if (_deviceMemory &&
|
||||
_deviceMemory->isDedicatedAllocation() &&
|
||||
_deviceMemory->_debugName.length == 0) {
|
||||
|
||||
_deviceMemory->setDebugName(_debugName.UTF8String);
|
||||
}
|
||||
setLabelIfNotNil(_mtlBuffer, _debugName);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Resource memory
|
||||
|
||||
VkResult MVKBuffer::getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements) {
|
||||
pMemoryRequirements->size = getByteCount();
|
||||
pMemoryRequirements->alignment = _byteAlignment;
|
||||
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->alignment = _byteAlignment;
|
||||
}
|
||||
pMemoryRequirements->memoryTypeBits = _device->getPhysicalDevice()->getAllMemoryTypes();
|
||||
#if MVK_MACOS
|
||||
// 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
|
||||
|
||||
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() {
|
||||
if (_deviceMemory) { _deviceMemory->removeBuffer(this); }
|
||||
if (_mtlBuffer) { [_mtlBuffer release]; }
|
||||
}
|
||||
|
||||
|
||||
|
@ -283,9 +283,15 @@ public:
|
||||
*/
|
||||
inline uint32_t getLazilyAllocatedMemoryTypes() { return _lazilyAllocatedMemoryTypes; }
|
||||
|
||||
/** Returns whether this is a unified memory device. */
|
||||
bool getHasUnifiedMemory();
|
||||
|
||||
|
||||
#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. */
|
||||
inline const MVKPhysicalDeviceMetalFeatures* getMetalFeatures() { return &_metalFeatures; }
|
||||
|
||||
@ -330,11 +336,15 @@ protected:
|
||||
void initMetalFeatures();
|
||||
void initFeatures();
|
||||
void initProperties();
|
||||
void initGPUInfoProperties();
|
||||
void initMemoryProperties();
|
||||
uint64_t getVRAMSize();
|
||||
uint64_t getRecommendedMaxWorkingSetSize();
|
||||
uint64_t getCurrentAllocatedSize();
|
||||
void initExtensions();
|
||||
MVKVector<MVKQueueFamily*>& getQueueFamilies();
|
||||
void initPipelineCacheUUID();
|
||||
MTLFeatureSet getHighestMTLFeatureSet();
|
||||
uint32_t getHighestMTLFeatureSet();
|
||||
uint64_t getSpirvCrossRevision();
|
||||
bool getImageViewIsSupported(const VkPhysicalDeviceImageFormatInfo2KHR *pImageFormatInfo);
|
||||
void logGPUInfo();
|
||||
@ -831,18 +841,5 @@ protected:
|
||||
#pragma mark -
|
||||
#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. */
|
||||
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);
|
||||
|
@ -36,7 +36,6 @@
|
||||
#include "MVKOSExtensions.h"
|
||||
#include <MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h>
|
||||
#include "vk_mvk_moltenvk.h"
|
||||
#include <mach/mach_host.h>
|
||||
|
||||
#import "CAMetalLayer+MoltenVK.h"
|
||||
|
||||
@ -134,8 +133,8 @@ void MVKPhysicalDevice::getFeatures(VkPhysicalDeviceFeatures2* features) {
|
||||
auto* portabilityFeatures = (VkPhysicalDevicePortabilitySubsetFeaturesEXTX*)next;
|
||||
portabilityFeatures->triangleFans = false;
|
||||
portabilityFeatures->separateStencilMaskRef = true;
|
||||
portabilityFeatures->events = true;
|
||||
portabilityFeatures->standardImageViews = _mvkInstance->getMoltenVKConfiguration()->fullImageViewSwizzle;
|
||||
portabilityFeatures->events = true;
|
||||
portabilityFeatures->standardImageViews = _mvkInstance->getMoltenVKConfiguration()->fullImageViewSwizzle || _metalFeatures.nativeTextureSwizzle;
|
||||
portabilityFeatures->samplerMipLodBias = false;
|
||||
break;
|
||||
}
|
||||
@ -335,7 +334,7 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format,
|
||||
}
|
||||
#if MVK_MACOS
|
||||
// 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;
|
||||
}
|
||||
#endif
|
||||
@ -491,8 +490,8 @@ VkResult MVKPhysicalDevice::getSurfaceFormats(MVKSurface* surface,
|
||||
|
||||
MVKVectorInline<VkColorSpaceKHR, 16> colorSpaces;
|
||||
colorSpaces.push_back(VK_COLOR_SPACE_SRGB_NONLINEAR_KHR);
|
||||
#if MVK_MACOS
|
||||
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.
|
||||
colorSpaces.push_back(VK_COLOR_SPACE_DISPLAY_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_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
|
||||
#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);
|
||||
if (!mvkMTLPixelFormatIsSupported(MTLPixelFormatBGR10A2Unorm)) { mtlFmtsCnt--; }
|
||||
@ -710,22 +739,21 @@ VkResult MVKPhysicalDevice::getPhysicalDeviceMemoryProperties(VkPhysicalDeviceMe
|
||||
if (pMemoryProperties) {
|
||||
pMemoryProperties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
|
||||
pMemoryProperties->memoryProperties = _memoryProperties;
|
||||
auto* next = (MVKVkAPIStructHeader*)pMemoryProperties->pNext;
|
||||
while (next) {
|
||||
switch ((uint32_t)next->sType) {
|
||||
for (auto* next = (VkBaseOutStructure*)pMemoryProperties->pNext; next; next = next->pNext) {
|
||||
switch (next->sType) {
|
||||
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT: {
|
||||
auto* budgetProps = (VkPhysicalDeviceMemoryBudgetPropertiesEXT*)next;
|
||||
memset(budgetProps->heapBudget, 0, sizeof(budgetProps->heapBudget));
|
||||
memset(budgetProps->heapUsage, 0, sizeof(budgetProps->heapUsage));
|
||||
budgetProps->heapBudget[0] = (VkDeviceSize)mvkRecommendedMaxWorkingSetSize(_mtlDevice);
|
||||
if ( [_mtlDevice respondsToSelector: @selector(currentAllocatedSize)] ) {
|
||||
budgetProps->heapUsage[0] = (VkDeviceSize)_mtlDevice.currentAllocatedSize;
|
||||
budgetProps->heapBudget[0] = (VkDeviceSize)getRecommendedMaxWorkingSetSize();
|
||||
budgetProps->heapUsage[0] = (VkDeviceSize)getCurrentAllocatedSize();
|
||||
if (!getHasUnifiedMemory()) {
|
||||
budgetProps->heapBudget[1] = (VkDeviceSize)mvkGetAvailableMemorySize();
|
||||
budgetProps->heapUsage[1] = (VkDeviceSize)mvkGetUsedMemorySize();
|
||||
}
|
||||
next = (MVKVkAPIStructHeader*)budgetProps->pNext;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
next = (MVKVkAPIStructHeader*)next->pNext;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -821,6 +849,14 @@ void MVKPhysicalDevice::initMetalFeatures() {
|
||||
_metalFeatures.stencilFeedback = true;
|
||||
}
|
||||
|
||||
if ( mvkOSVersion() >= 13.0 ) {
|
||||
_metalFeatures.mslVersionEnum = MTLLanguageVersion2_2;
|
||||
_metalFeatures.placementHeaps = true;
|
||||
if ( getSupportsGPUFamily(MTLGPUFamilyApple4) ) {
|
||||
_metalFeatures.nativeTextureSwizzle = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MVK_MACOS
|
||||
@ -866,6 +902,15 @@ void MVKPhysicalDevice::initMetalFeatures() {
|
||||
_metalFeatures.stencilFeedback = true;
|
||||
}
|
||||
|
||||
if ( mvkOSVersion() >= 10.15 ) {
|
||||
_metalFeatures.mslVersionEnum = MTLLanguageVersion2_2;
|
||||
_metalFeatures.native3DCompressedTextures = true;
|
||||
if ( getSupportsGPUFamily(MTLGPUFamilyMac2) ) {
|
||||
_metalFeatures.nativeTextureSwizzle = true;
|
||||
_metalFeatures.placementHeaps = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// 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);
|
||||
|
||||
switch (_metalFeatures.mslVersionEnum) {
|
||||
case MTLLanguageVersion2_2:
|
||||
setMSLVersion(2, 2);
|
||||
break;
|
||||
case MTLLanguageVersion2_1:
|
||||
setMSLVersion(2, 1);
|
||||
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() {
|
||||
memset(&_features, 0, sizeof(_features)); // Start with everything cleared
|
||||
|
||||
@ -999,6 +1052,9 @@ void MVKPhysicalDevice::initFeatures() {
|
||||
_features.multiViewport = true;
|
||||
}
|
||||
|
||||
if ( mvkOSVersion() >= 10.15 ) {
|
||||
_features.shaderResourceMinLod = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1049,7 +1105,7 @@ void MVKPhysicalDevice::initFeatures() {
|
||||
// VkBool32 shaderInt64;
|
||||
// VkBool32 shaderInt16; // done
|
||||
// VkBool32 shaderResourceResidency;
|
||||
// VkBool32 shaderResourceMinLod;
|
||||
// VkBool32 shaderResourceMinLod; // done
|
||||
// VkBool32 sparseBinding;
|
||||
// VkBool32 sparseResidencyBuffer;
|
||||
// VkBool32 sparseResidencyImage2D;
|
||||
@ -1070,7 +1126,7 @@ void MVKPhysicalDevice::initProperties() {
|
||||
_properties.apiVersion = MVK_VULKAN_API_VERSION;
|
||||
_properties.driverVersion = MVK_VERSION;
|
||||
|
||||
mvkPopulateGPUInfo(_properties, _mtlDevice);
|
||||
initGPUInfoProperties();
|
||||
initPipelineCacheUUID();
|
||||
|
||||
// Limits
|
||||
@ -1381,6 +1437,104 @@ void MVKPhysicalDevice::initProperties() {
|
||||
_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
|
||||
|
||||
@ -1516,7 +1670,7 @@ void MVKPhysicalDevice::initPipelineCacheUUID() {
|
||||
uuidComponentOffset += sizeof(mvkVersion);
|
||||
|
||||
// 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);
|
||||
uuidComponentOffset += sizeof(mtlFeatSet);
|
||||
|
||||
@ -1526,24 +1680,46 @@ void MVKPhysicalDevice::initPipelineCacheUUID() {
|
||||
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
|
||||
MTLFeatureSet maxFS = MTLFeatureSet_iOS_GPUFamily5_v1;
|
||||
MTLFeatureSet minFS = MTLFeatureSet_iOS_GPUFamily1_v1;
|
||||
if (mvkOSVersion() >= 13.0) { mtlVer = 0x30000; }
|
||||
#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
|
||||
|
||||
#if MVK_MACOS
|
||||
MTLFeatureSet maxFS = MTLFeatureSet_macOS_GPUFamily2_v1;
|
||||
MTLFeatureSet minFS = MTLFeatureSet_macOS_GPUFamily1_v1;
|
||||
uint32_t maxFS = (uint32_t)MTLFeatureSet_macOS_GPUFamily2_v1;
|
||||
uint32_t minFS = (uint32_t)MTLFeatureSet_macOS_GPUFamily1_v1;
|
||||
#endif
|
||||
|
||||
for (NSUInteger fs = maxFS; fs > minFS; fs--) {
|
||||
MTLFeatureSet mtlFS = (MTLFeatureSet)fs;
|
||||
if ( [_mtlDevice supportsFeatureSet: mtlFS] ) {
|
||||
return mtlFS;
|
||||
}
|
||||
for (uint32_t fs = maxFS; fs > minFS; fs--) {
|
||||
if ( [_mtlDevice supportsFeatureSet: (MTLFeatureSet)fs] ) { return fs; }
|
||||
}
|
||||
|
||||
return minFS;
|
||||
}
|
||||
|
||||
@ -1606,7 +1782,7 @@ void MVKPhysicalDevice::initMemoryProperties() {
|
||||
.memoryHeaps = {
|
||||
{
|
||||
.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)
|
||||
@ -1656,6 +1832,65 @@ void MVKPhysicalDevice::initMemoryProperties() {
|
||||
_allMemoryTypes = 0x7; // Private, shared & memoryless
|
||||
}
|
||||
#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() {
|
||||
@ -1699,7 +1934,24 @@ void MVKPhysicalDevice::logGPUInfo() {
|
||||
logMsg += "\n\t\tvendorID: %#06x";
|
||||
logMsg += "\n\t\tdeviceID: %#06x";
|
||||
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 ( [_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 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;
|
||||
}
|
||||
|
||||
@ -2645,139 +2901,6 @@ MVKDevice::~MVKDevice() {
|
||||
#pragma mark -
|
||||
#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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -104,6 +104,9 @@ public:
|
||||
/** Returns the Metal buffer underlying this memory allocation. */
|
||||
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. */
|
||||
inline MTLStorageMode getMTLStorageMode() { return _mtlStorageMode; }
|
||||
|
||||
@ -133,6 +136,7 @@ protected:
|
||||
void removeBuffer(MVKBuffer* mvkBuff);
|
||||
VkResult addImage(MVKImage* mvkImg);
|
||||
void removeImage(MVKImage* mvkImg);
|
||||
bool ensureMTLHeap();
|
||||
bool ensureMTLBuffer();
|
||||
bool ensureHostMemory();
|
||||
void freeHostMemory();
|
||||
@ -145,6 +149,7 @@ protected:
|
||||
VkDeviceSize _mapOffset = 0;
|
||||
VkDeviceSize _mapSize = 0;
|
||||
id<MTLBuffer> _mtlBuffer = nil;
|
||||
id<MTLHeap> _mtlHeap = nil;
|
||||
void* _pMemory = nullptr;
|
||||
void* _pHostMemory = nullptr;
|
||||
bool _isMapped = false;
|
||||
|
@ -32,7 +32,10 @@ using namespace std;
|
||||
|
||||
#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) {
|
||||
|
||||
@ -86,8 +89,11 @@ VkResult MVKDeviceMemory::flushToDevice(VkDeviceSize offset, VkDeviceSize size,
|
||||
}
|
||||
#endif
|
||||
|
||||
lock_guard<mutex> lock(_rezLock);
|
||||
for (auto& img : _images) { img->flushToDevice(offset, memSize); }
|
||||
// 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);
|
||||
for (auto& img : _images) { img->flushToDevice(offset, memSize); }
|
||||
}
|
||||
}
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
@ -98,7 +104,7 @@ VkResult MVKDeviceMemory::pullFromDevice(VkDeviceSize offset,
|
||||
MVKMTLBlitEncoder* pBlitEnc) {
|
||||
// Coherent memory is flushed on unmap(), so it is only flushed if forced
|
||||
VkDeviceSize memSize = adjustMemorySize(size, offset);
|
||||
if (memSize > 0 && isMemoryHostAccessible() && (evenIfCoherent || !isMemoryHostCoherent()) ) {
|
||||
if (memSize > 0 && isMemoryHostAccessible() && (evenIfCoherent || !isMemoryHostCoherent()) && !_mtlHeap) {
|
||||
lock_guard<mutex> lock(_rezLock);
|
||||
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() );
|
||||
}
|
||||
|
||||
if (!_isDedicated)
|
||||
_images.push_back(mvkImg);
|
||||
if (!_isDedicated) { _images.push_back(mvkImg); }
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
@ -164,6 +169,36 @@ void MVKDeviceMemory::removeImage(MVKImage* 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,
|
||||
// creating the MTLBuffer if needed, and returns whether it was successful.
|
||||
bool MVKDeviceMemory::ensureMTLBuffer() {
|
||||
@ -175,12 +210,20 @@ bool MVKDeviceMemory::ensureMTLBuffer() {
|
||||
if (memLen > _device->_pMetalFeatures->maxMTLBufferSize) { return false; }
|
||||
|
||||
// If host memory was already allocated, it is copied into the new MTLBuffer, and then released.
|
||||
if (_pHostMemory) {
|
||||
if (_mtlHeap) {
|
||||
_mtlBuffer = [_mtlHeap newBufferWithLength: memLen options: getMTLResourceOptions() offset: 0]; // retained
|
||||
if (_pHostMemory) {
|
||||
memcpy(_mtlBuffer.contents, _pHostMemory, memLen);
|
||||
freeHostMemory();
|
||||
}
|
||||
[_mtlBuffer makeAliasable];
|
||||
} else if (_pHostMemory) {
|
||||
_mtlBuffer = [getMTLDevice() newBufferWithBytes: _pHostMemory length: memLen options: getMTLResourceOptions()]; // retained
|
||||
freeHostMemory();
|
||||
} else {
|
||||
_mtlBuffer = [getMTLDevice() newBufferWithLength: memLen options: getMTLResourceOptions()]; // retained
|
||||
}
|
||||
if (!_mtlBuffer) { return false; }
|
||||
_pMemory = isMemoryHostAccessible() ? _mtlBuffer.contents : nullptr;
|
||||
|
||||
propogateDebugName();
|
||||
@ -267,6 +310,15 @@ MVKDeviceMemory::MVKDeviceMemory(MVKDevice* device,
|
||||
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 (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));
|
||||
|
@ -275,6 +275,7 @@ protected:
|
||||
bool _usesTexelBuffer;
|
||||
bool _isLinear;
|
||||
bool _is3DCompressed;
|
||||
bool _isAliasable;
|
||||
};
|
||||
|
||||
|
||||
|
@ -197,10 +197,8 @@ VkResult MVKImage::getMemoryRequirements(const void*, VkMemoryRequirements2* pMe
|
||||
switch (next->sType) {
|
||||
case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS: {
|
||||
auto* dedicatedReqs = (VkMemoryDedicatedRequirements*)next;
|
||||
// TODO: Maybe someday we could do something with MTLHeaps
|
||||
// and allocate non-dedicated memory from them. For now, we
|
||||
// always prefer dedicated allocations for non-buffer-backed images.
|
||||
dedicatedReqs->prefersDedicatedAllocation = !_usesTexelBuffer;
|
||||
bool writable = mvkIsAnyFlagEnabled(_usage, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
|
||||
dedicatedReqs->prefersDedicatedAllocation = !_usesTexelBuffer && (writable || !_device->_pMetalFeatures->placementHeaps);
|
||||
dedicatedReqs->requiresDedicatedAllocation = VK_FALSE;
|
||||
break;
|
||||
}
|
||||
@ -229,7 +227,7 @@ bool MVKImage::validateUseTexelBuffer() {
|
||||
bool isUncompressed = blockExt.width == 1 && blockExt.height == 1;
|
||||
|
||||
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
|
||||
|
||||
#if MVK_MACOS
|
||||
@ -350,6 +348,10 @@ id<MTLTexture> MVKImage::newMTLTexture() {
|
||||
mtlTex = [_deviceMemory->_mtlBuffer newTextureWithDescriptor: mtlTexDesc
|
||||
offset: getDeviceMemoryOffset()
|
||||
bytesPerRow: _subresources[0].layout.rowPitch];
|
||||
} else if (_deviceMemory->_mtlHeap) {
|
||||
mtlTex = [_deviceMemory->_mtlHeap newTextureWithDescriptor: mtlTexDesc
|
||||
offset: getDeviceMemoryOffset()];
|
||||
if (_isAliasable) [mtlTex makeAliasable];
|
||||
} else {
|
||||
mtlTex = [getMTLDevice() newTextureWithDescriptor: mtlTexDesc];
|
||||
}
|
||||
@ -448,7 +450,7 @@ MTLTextureDescriptor* MVKImage::newMTLTextureDescriptor() {
|
||||
MTLTextureDescriptor* mtlTexDesc = [MTLTextureDescriptor new]; // retained
|
||||
#if MVK_MACOS
|
||||
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.
|
||||
mtlTexDesc.pixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||
} else {
|
||||
@ -620,17 +622,26 @@ MVKImage::MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo) : MV
|
||||
_mtlTextureType = mvkMTLTextureTypeFromVkImageType(pCreateInfo->imageType, _arrayLayers, _samples > VK_SAMPLE_COUNT_1_BIT);
|
||||
_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) ||
|
||||
mvkAreAllFlagsEnabled(mvkVkFormatProperties(pCreateInfo->format).optimalTilingFeatures, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT));
|
||||
_canSupportMTLTextureView = !_isDepthStencilAttachment || _device->_pMetalFeatures->stencilViews;
|
||||
_hasExpectedTexelSize = (mvkMTLPixelFormatBytesPerBlock(_mtlPixelFormat) == mvkVkFormatBytesPerBlock(pCreateInfo->format));
|
||||
|
||||
// Calc _byteCount after _byteAlignment
|
||||
_byteAlignment = _isLinear ? _device->getVkFormatTexelBufferAlignment(pCreateInfo->format, this) : mvkEnsurePowerOfTwo(mvkVkFormatBytesPerBlock(pCreateInfo->format));
|
||||
for (uint32_t mipLvl = 0; mipLvl < _mipLevels; mipLvl++) {
|
||||
_byteCount += getBytesPerLayer(mipLvl) * _extent.depth * _arrayLayers;
|
||||
}
|
||||
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
|
||||
_byteAlignment = _isLinear ? _device->getVkFormatTexelBufferAlignment(pCreateInfo->format, this) : mvkEnsurePowerOfTwo(mvkVkFormatBytesPerBlock(pCreateInfo->format));
|
||||
for (uint32_t mipLvl = 0; mipLvl < _mipLevels; mipLvl++) {
|
||||
_byteCount += getBytesPerLayer(mipLvl) * _extent.depth * _arrayLayers;
|
||||
}
|
||||
}
|
||||
|
||||
initSubresources(pCreateInfo);
|
||||
}
|
||||
@ -646,8 +657,12 @@ void MVKImage::validateConfig(const VkImageCreateInfo* pCreateInfo, bool isAttac
|
||||
}
|
||||
#endif
|
||||
#if MVK_MACOS
|
||||
if (isCompressed && !is2D && !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)));
|
||||
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)));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -859,15 +874,25 @@ id<MTLTexture> MVKImageView::getMTLTexture() {
|
||||
// overlay on the Metal texture of the underlying image.
|
||||
id<MTLTexture> MVKImageView::newMTLTexture() {
|
||||
MTLTextureType mtlTextureType = _mtlTextureType;
|
||||
NSRange sliceRange = NSMakeRange(_subresourceRange.baseArrayLayer, _subresourceRange.layerCount);
|
||||
// Fake support for 2D views of 3D textures.
|
||||
if (_image->getImageType() == VK_IMAGE_TYPE_3D &&
|
||||
(mtlTextureType == MTLTextureType2D || mtlTextureType == MTLTextureType2DArray)) {
|
||||
(mtlTextureType == MTLTextureType2D || mtlTextureType == MTLTextureType2DArray)) {
|
||||
mtlTextureType = MTLTextureType3D;
|
||||
}
|
||||
return [_image->getMTLTexture() newTextureViewWithPixelFormat: _mtlPixelFormat
|
||||
textureType: mtlTextureType
|
||||
levels: NSMakeRange(_subresourceRange.baseMipLevel, _subresourceRange.levelCount)
|
||||
slices: NSMakeRange(_subresourceRange.baseArrayLayer, _subresourceRange.layerCount)]; // retained
|
||||
sliceRange = NSMakeRange(0, 1);
|
||||
}
|
||||
if (getDevice()->_pMetalFeatures->nativeTextureSwizzle && _packedSwizzle) {
|
||||
return [_image->getMTLTexture() newTextureViewWithPixelFormat: _mtlPixelFormat
|
||||
textureType: mtlTextureType
|
||||
levels: NSMakeRange(_subresourceRange.baseMipLevel, _subresourceRange.levelCount)
|
||||
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;
|
||||
}
|
||||
|
||||
bool useShaderSwizzle;
|
||||
bool useSwizzle;
|
||||
bool isMultisample = _image ? _image->getSampleCount() != VK_SAMPLE_COUNT_1_BIT : false;
|
||||
_mtlTexture = nil;
|
||||
_mtlPixelFormat = getSwizzledMTLPixelFormat(pCreateInfo->format, pCreateInfo->components, useShaderSwizzle,
|
||||
_mtlPixelFormat = getSwizzledMTLPixelFormat(pCreateInfo->format, pCreateInfo->components, useSwizzle,
|
||||
(_device ? _device->_pMVKConfig : pAltMVKConfig));
|
||||
_packedSwizzle = useShaderSwizzle ? mvkPackSwizzle(pCreateInfo->components) : 0;
|
||||
_packedSwizzle = useSwizzle ? mvkPackSwizzle(pCreateInfo->components) : 0;
|
||||
_mtlTextureType = mvkMTLTextureTypeFromVkImageViewType(pCreateInfo->viewType, isMultisample);
|
||||
|
||||
initMTLTextureViewSupport();
|
||||
@ -944,20 +969,22 @@ void MVKImageView::validateImageViewConfig(const VkImageViewCreateInfo* pCreateI
|
||||
|
||||
// Returns a MTLPixelFormat, based on the MTLPixelFormat converted from the VkFormat, but possibly
|
||||
// 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
|
||||
// function attempts to find an alternate MTLPixelFormat that coincidentally matches the swizzled format.
|
||||
// If a replacement MTLFormat was found, it is returned and useShaderSwizzle is set to false.
|
||||
// Metal prior to version 3.0 does not support general per-texture swizzles, so if the swizzle is not an
|
||||
// identity swizzle, this function attempts to find an alternate MTLPixelFormat that coincidentally
|
||||
// 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
|
||||
// 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.
|
||||
MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format,
|
||||
VkComponentMapping components,
|
||||
bool& useShaderSwizzle,
|
||||
bool& useSwizzle,
|
||||
const MVKConfiguration* pMVKConfig) {
|
||||
|
||||
// Attempt to find a valid format transformation swizzle first.
|
||||
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 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.
|
||||
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* 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"
|
||||
@ -1061,12 +1088,8 @@ void MVKImageView::initMTLTextureViewSupport() {
|
||||
(_mtlTextureType == _image->_mtlTextureType ||
|
||||
((_mtlTextureType == MTLTextureType2D || _mtlTextureType == MTLTextureType2DArray) && is3D)) &&
|
||||
_subresourceRange.levelCount == _image->_mipLevels &&
|
||||
_subresourceRange.layerCount == (is3D ? _image->_extent.depth : _image->_arrayLayers)) {
|
||||
_useMTLTextureView = false;
|
||||
}
|
||||
|
||||
// Never use views for subsets of 3D textures. Metal doesn't support them yet.
|
||||
if (is3D && _subresourceRange.layerCount != _image->_extent.depth) {
|
||||
(is3D || _subresourceRange.layerCount == _image->_arrayLayers) &&
|
||||
(!getDevice()->_pMetalFeatures->nativeTextureSwizzle || !_packedSwizzle)) {
|
||||
_useMTLTextureView = false;
|
||||
}
|
||||
}
|
||||
|
@ -628,6 +628,7 @@ void MVKInstance::initProcAddrs() {
|
||||
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(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(vkDebugMarkerSetObjectTagEXT, EXT_DEBUG_MARKER);
|
||||
ADD_DVC_EXT_ENTRY_POINT(vkDebugMarkerSetObjectNameEXT, EXT_DEBUG_MARKER);
|
||||
|
@ -1159,7 +1159,7 @@ void MVKGraphicsPipeline::initMVKShaderConverterContext(SPIRVToMSLConversionConf
|
||||
|
||||
shaderContext.options.mslOptions.enable_point_size_builtin = isRenderingPoints(pCreateInfo, reflectData);
|
||||
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.tessPatchKind = reflectData.patchKind;
|
||||
@ -1346,7 +1346,7 @@ MVKMTLFunction MVKComputePipeline::getMTLFunction(const VkComputePipelineCreateI
|
||||
shaderContext.options.entryPointStage = spv::ExecutionModelGLCompute;
|
||||
shaderContext.options.mslOptions.msl_version = _device->_pMetalFeatures->mslVersion;
|
||||
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.dispatch_base = _allowsDispatchBase;
|
||||
|
||||
|
@ -80,6 +80,9 @@ public:
|
||||
/** Returns the specified performance stats structure. */
|
||||
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.
|
||||
* This function accepts both a semaphore and a fence, and either none, one, or both may be provided.
|
||||
|
@ -30,6 +30,8 @@
|
||||
#import "CAMetalLayer+MoltenVK.h"
|
||||
#import "MVKBlockObserver.h"
|
||||
|
||||
#include <libkern/OSByteOrder.h>
|
||||
|
||||
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
|
||||
|
||||
@ -342,41 +408,59 @@ void MVKSwapchain::initCAMetalLayer(const VkSwapchainCreateInfoKHR* pCreateInfo,
|
||||
if (pCreateInfo->compositeAlpha != VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) {
|
||||
_mtlLayer.opaque = pCreateInfo->compositeAlpha == VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
}
|
||||
#if MVK_MACOS
|
||||
|
||||
switch (pCreateInfo->imageColorSpace) {
|
||||
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
|
||||
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
|
||||
break;
|
||||
case VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT:
|
||||
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3);
|
||||
_mtlLayer.wantsExtendedDynamicRangeContentMVK = YES;
|
||||
break;
|
||||
case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
|
||||
_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;
|
||||
case VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT:
|
||||
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceDCIP3);
|
||||
_mtlLayer.wantsExtendedDynamicRangeContentMVK = YES;
|
||||
break;
|
||||
case VK_COLOR_SPACE_BT709_NONLINEAR_EXT:
|
||||
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_709);
|
||||
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:
|
||||
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceAdobeRGB1998);
|
||||
break;
|
||||
case VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT:
|
||||
_mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedSRGB);
|
||||
break;
|
||||
case VK_COLOR_SPACE_PASS_THROUGH_EXT:
|
||||
default:
|
||||
// Nothing - the default is not to do color matching.
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
_mtlLayerOrigDrawSize = _mtlLayer.updatedDrawableSizeMVK;
|
||||
|
||||
// TODO: set additional CAMetalLayer properties before extracting drawables:
|
||||
// - presentsWithTransaction
|
||||
// - drawsAsynchronously
|
||||
// - wantsExtendedDynamicRangeContent (macOS only)
|
||||
|
||||
if ( [_mtlLayer.delegate isKindOfClass: [PLATFORM_VIEW_CLASS class]] ) {
|
||||
// Sometimes, the owning view can replace its CAMetalLayer. In that case, the client
|
||||
|
@ -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_utils, EXT_DEBUG_UTILS, MVK_EXTENSION_INSTANCE)
|
||||
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_memory_budget, EXT_MEMORY_BUDGET, MVK_EXTENSION_DEVICE)
|
||||
MVK_EXTENSION(EXT_metal_surface, EXT_METAL_SURFACE, MVK_EXTENSION_INSTANCE)
|
||||
|
@ -47,6 +47,9 @@ static VkExtensionProperties kVkExtProps_ ##EXT = mvkMakeExtProps(VK_ ##EXT ##_E
|
||||
// Returns whether the specified properties are valid for this platform
|
||||
static bool mvkIsSupportedOnPlatform(VkExtensionProperties* pProperties) {
|
||||
#if MVK_MACOS
|
||||
if (pProperties == &kVkExtProps_EXT_HDR_METADATA) {
|
||||
return mvkOSVersion() >= 10.15;
|
||||
}
|
||||
if (pProperties == &kVkExtProps_EXT_FRAGMENT_SHADER_INTERLOCK) {
|
||||
return mvkOSVersion() >= 10.13;
|
||||
}
|
||||
@ -69,6 +72,7 @@ static bool mvkIsSupportedOnPlatform(VkExtensionProperties* pProperties) {
|
||||
#endif
|
||||
#if MVK_IOS
|
||||
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) {
|
||||
return mvkOSVersion() >= 11.0;
|
||||
}
|
||||
@ -81,7 +85,9 @@ static bool mvkIsSupportedOnPlatform(VkExtensionProperties* pProperties) {
|
||||
if (pProperties == &kVkExtProps_EXT_SHADER_STENCIL_EXPORT) {
|
||||
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) {
|
||||
return mvkOSVersion() >= 11.0;
|
||||
}
|
||||
|
@ -56,4 +56,12 @@
|
||||
*/
|
||||
@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
|
||||
|
@ -62,4 +62,18 @@
|
||||
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
|
||||
|
@ -932,6 +932,25 @@ MVK_PUBLIC_SYMBOL VkSampleCountFlagBits mvkVkSampleCountFlagBitsFromSampleCount(
|
||||
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
|
||||
|
||||
|
@ -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 VK_EXT_host_query_reset extension
|
||||
|
||||
|
@ -35,7 +35,7 @@ ${MVK_SAN} \
|
||||
${MVK_LINK_WARN} \
|
||||
-isysroot ${SDK_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} \
|
||||
-o "${BUILT_PRODUCTS_DIR}/dynamic/${MVK_DYLIB_NAME}" \
|
||||
-force_load "${BUILT_PRODUCTS_DIR}/lib${PRODUCT_NAME}.a"
|
||||
|
Loading…
x
Reference in New Issue
Block a user