Merge pull request #1835 from billhollings/swapchain-mtce1
Add support for VK_EXT_swapchain_maintenance1 and VK_EXT_surface_maintenance1
This commit is contained in:
commit
0ad6d72bbc
4
.github/workflows/CI.yml
vendored
4
.github/workflows/CI.yml
vendored
@ -15,9 +15,9 @@ jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
xcode: [ "14.1" ]
|
||||
xcode: [ "14.2" ]
|
||||
platform: [ "macos", "maccat", "ios", "tvos" ]
|
||||
os: [ "macos-12" ]
|
||||
os: [ "macos-latest" ]
|
||||
upload_artifacts: [ true ]
|
||||
# additional specific configurations
|
||||
include:
|
||||
|
@ -338,7 +338,9 @@ In addition to core *Vulkan* functionality, **MoltenVK** also supports the foll
|
||||
- `VK_EXT_shader_stencil_export` *(requires Mac GPU family 2 or iOS GPU family 5)*
|
||||
- `VK_EXT_shader_viewport_index_layer`
|
||||
- `VK_EXT_subgroup_size_control` *(requires Metal 2.1 on Mac or Metal 2.2 and Apple family 4 on iOS)*
|
||||
- `VK_EXT_surface_maintenance1`
|
||||
- `VK_EXT_swapchain_colorspace`
|
||||
- `VK_EXT_swapchain_maintenance1`
|
||||
- `VK_EXT_vertex_attribute_divisor`
|
||||
- `VK_EXT_texel_buffer_alignment` *(requires Metal 2.0)*
|
||||
- `VK_EXT_texture_compression_astc_hdr` *(iOS and macOS, requires family 6 (A13) or better Apple GPU)*
|
||||
|
@ -18,10 +18,16 @@ MoltenVK 1.2.3
|
||||
|
||||
Released TBA
|
||||
|
||||
- Add support for extensions:
|
||||
- `VK_EXT_swapchain_maintenance1`
|
||||
- `VK_EXT_surface_maintenance1`
|
||||
- Fix issue where extension `VK_KHR_fragment_shader_barycentric`
|
||||
was sometimes incorrectly disabled due to a Metal driver bug.
|
||||
- Work around problems with using explicit LoD with arrayed depth images
|
||||
on Apple Silicon.
|
||||
- Detect when size of surface has changed under the covers.
|
||||
- Change rounding of surface size provided by Metal from truncation to rounding-with-half-to-even.
|
||||
- Queue submissions retain wait semaphores until `MTLCommandBuffer` finishes.
|
||||
- Work around problems with using explicit LoD with arrayed depth images on Apple Silicon.
|
||||
- Update `VK_MVK_MOLTENVK_SPEC_VERSION` to version `37`.
|
||||
|
||||
|
||||
|
||||
@ -49,6 +55,7 @@ Released 2023/01/23
|
||||
- MSL: Fix potentially uninitialized warnings.
|
||||
|
||||
|
||||
|
||||
MoltenVK 1.2.1
|
||||
--------------
|
||||
|
||||
|
@ -416,21 +416,14 @@ MTLBarrierScope mvkMTLBarrierScopeFromVkAccessFlags(VkAccessFlags vkAccess);
|
||||
#pragma mark -
|
||||
#pragma mark Geometry conversions
|
||||
|
||||
/** Returns a VkExtent2D that corresponds to the specified CGSize. */
|
||||
static inline VkExtent2D mvkVkExtent2DFromCGSize(CGSize cgSize) {
|
||||
VkExtent2D vkExt;
|
||||
vkExt.width = cgSize.width;
|
||||
vkExt.height = cgSize.height;
|
||||
return vkExt;
|
||||
}
|
||||
/**
|
||||
* Returns a VkExtent2D that corresponds to the specified CGSize.
|
||||
* Rounds to nearest integer using half-to-even rounding.
|
||||
*/
|
||||
VkExtent2D mvkVkExtent2DFromCGSize(CGSize cgSize);
|
||||
|
||||
/** Returns a CGSize that corresponds to the specified VkExtent2D. */
|
||||
static inline CGSize mvkCGSizeFromVkExtent2D(VkExtent2D vkExtent) {
|
||||
CGSize cgSize;
|
||||
cgSize.width = vkExtent.width;
|
||||
cgSize.height = vkExtent.height;
|
||||
return cgSize;
|
||||
}
|
||||
CGSize mvkCGSizeFromVkExtent2D(VkExtent2D vkExtent);
|
||||
|
||||
/** Returns a Metal MTLOrigin constructed from a VkOffset3D. */
|
||||
static inline MTLOrigin mvkMTLOriginFromVkOffset3D(VkOffset3D vkOffset) {
|
||||
|
@ -56,7 +56,7 @@ typedef unsigned long MTLArgumentBuffersTier;
|
||||
#define MVK_MAKE_VERSION(major, minor, patch) (((major) * 10000) + ((minor) * 100) + (patch))
|
||||
#define MVK_VERSION MVK_MAKE_VERSION(MVK_VERSION_MAJOR, MVK_VERSION_MINOR, MVK_VERSION_PATCH)
|
||||
|
||||
#define VK_MVK_MOLTENVK_SPEC_VERSION 36
|
||||
#define VK_MVK_MOLTENVK_SPEC_VERSION 37
|
||||
#define VK_MVK_MOLTENVK_EXTENSION_NAME "VK_MVK_moltenvk"
|
||||
|
||||
/** Identifies the level of logging MoltenVK should be limited to outputting. */
|
||||
@ -322,22 +322,23 @@ typedef struct {
|
||||
VkBool32 presentWithCommandBuffer;
|
||||
|
||||
/**
|
||||
* If enabled, swapchain images will use simple Nearest sampling when magnifying the
|
||||
* swapchain image to fit a physical display surface. If disabled, swapchain images will
|
||||
* If enabled, swapchain images will use simple Nearest sampling when minifying or magnifying
|
||||
* the swapchain image to fit a physical display surface. If disabled, swapchain images will
|
||||
* use Linear sampling when magnifying the swapchain image to fit a physical display surface.
|
||||
* Enabling this setting avoids smearing effects when swapchain images are simple interger
|
||||
* multiples of display pixels (eg- macOS Retina, and typical of graphics apps and games),
|
||||
* but may cause aliasing effects when using non-integer display scaling.
|
||||
*
|
||||
* The value of this parameter may be changed before creating a VkSwapchain,
|
||||
* The value of this parameter must be changed before creating a VkSwapchain,
|
||||
* for the change to take effect.
|
||||
*
|
||||
* The initial value or this parameter is set by the
|
||||
* MVK_CONFIG_SWAPCHAIN_MAG_FILTER_USE_NEAREST
|
||||
* MVK_CONFIG_SWAPCHAIN_MIN_MAG_FILTER_USE_NEAREST
|
||||
* runtime environment variable or MoltenVK compile-time build setting.
|
||||
* If neither is set, the value of this parameter defaults to true.
|
||||
*/
|
||||
VkBool32 swapchainMagFilterUseNearest;
|
||||
VkBool32 swapchainMinMagFilterUseNearest;
|
||||
#define swapchainMagFilterUseNearest swapchainMinMagFilterUseNearest
|
||||
|
||||
/**
|
||||
* The maximum amount of time, in nanoseconds, to wait for a Metal library, function, or
|
||||
|
@ -109,7 +109,7 @@ id<MTLComputePipelineState> MVKCommandEncodingPool::getCmdFillBufferMTLComputePi
|
||||
MVK_ENC_REZ_ACCESS(_mtlFillBufferComputePipelineState, newCmdFillBufferMTLComputePipelineState(_commandPool));
|
||||
}
|
||||
|
||||
static inline uint32_t getRenderpassLoadStoreStateIndex(MVKFormatType type) {
|
||||
static constexpr uint32_t getRenderpassLoadStoreStateIndex(MVKFormatType type) {
|
||||
switch (type) {
|
||||
case kMVKFormatColorHalf:
|
||||
case kMVKFormatColorFloat:
|
||||
|
@ -181,8 +181,12 @@ public:
|
||||
*/
|
||||
VkResult getSurfaceSupport(uint32_t queueFamilyIndex, MVKSurface* surface, VkBool32* pSupported);
|
||||
|
||||
/** Returns the capabilities of the specified surface. */
|
||||
VkResult getSurfaceCapabilities(MVKSurface* surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities);
|
||||
/** Returns the capabilities of the surface. */
|
||||
VkResult getSurfaceCapabilities(VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities);
|
||||
|
||||
/** Returns the capabilities of the surface. */
|
||||
VkResult getSurfaceCapabilities(const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
|
||||
VkSurfaceCapabilities2KHR* pSurfaceCapabilities);
|
||||
|
||||
/**
|
||||
* Returns the pixel formats supported by the surface.
|
||||
|
@ -385,6 +385,11 @@ void MVKPhysicalDevice::getFeatures(VkPhysicalDeviceFeatures2* features) {
|
||||
robustness2Features->nullDescriptor = false;
|
||||
break;
|
||||
}
|
||||
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT: {
|
||||
auto* swapchainMaintenance1Features = (VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT*)next;
|
||||
swapchainMaintenance1Features->swapchainMaintenance1 = true;
|
||||
break;
|
||||
}
|
||||
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT: {
|
||||
auto* texelBuffAlignFeatures = (VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT*)next;
|
||||
texelBuffAlignFeatures->texelBufferAlignment = _metalFeatures.texelBuffers && [_mtlDevice respondsToSelector: @selector(minimumLinearTextureAlignmentForPixelFormat:)];
|
||||
@ -1075,32 +1080,139 @@ VkResult MVKPhysicalDevice::getSurfaceSupport(uint32_t queueFamilyIndex,
|
||||
return *pSupported ? VK_SUCCESS : surface->getConfigurationResult();
|
||||
}
|
||||
|
||||
VkResult MVKPhysicalDevice::getSurfaceCapabilities(MVKSurface* surface,
|
||||
VkResult MVKPhysicalDevice::getSurfaceCapabilities(VkSurfaceKHR surface,
|
||||
VkSurfaceCapabilitiesKHR* pSurfaceCapabilities) {
|
||||
VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo;
|
||||
surfaceInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR;
|
||||
surfaceInfo.pNext = nullptr;
|
||||
surfaceInfo.surface = surface;
|
||||
|
||||
// The layer underlying the surface view must be a CAMetalLayer.
|
||||
VkSurfaceCapabilities2KHR surfaceCaps;
|
||||
surfaceCaps.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR;
|
||||
surfaceCaps.pNext = nullptr;
|
||||
surfaceCaps.surfaceCapabilities = *pSurfaceCapabilities;
|
||||
|
||||
VkResult rslt = getSurfaceCapabilities(&surfaceInfo, &surfaceCaps);
|
||||
|
||||
*pSurfaceCapabilities = surfaceCaps.surfaceCapabilities;
|
||||
|
||||
return rslt;
|
||||
}
|
||||
|
||||
VkResult MVKPhysicalDevice::getSurfaceCapabilities( const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
|
||||
VkSurfaceCapabilities2KHR* pSurfaceCapabilities) {
|
||||
|
||||
// Retrieve the present mode if it is supplied in this query.
|
||||
VkPresentModeKHR presentMode = VK_PRESENT_MODE_MAX_ENUM_KHR;
|
||||
for (auto* next = (const VkBaseInStructure*)pSurfaceInfo->pNext; next; next = next->pNext) {
|
||||
switch (next->sType) {
|
||||
case VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT: {
|
||||
presentMode = ((VkSurfacePresentModeEXT*)next)->presentMode;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the scaling and present mode compatibility structs if they are supplied in this query.
|
||||
VkSurfacePresentScalingCapabilitiesEXT* pScalingCaps = nullptr;
|
||||
VkSurfacePresentModeCompatibilityEXT* pCompatibility = nullptr;
|
||||
for (auto* next = (VkBaseOutStructure*)pSurfaceCapabilities->pNext; next; next = next->pNext) {
|
||||
switch (next->sType) {
|
||||
case VK_STRUCTURE_TYPE_SURFACE_PRESENT_SCALING_CAPABILITIES_EXT: {
|
||||
pScalingCaps = (VkSurfacePresentScalingCapabilitiesEXT*)next;
|
||||
break;
|
||||
}
|
||||
case VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT: {
|
||||
pCompatibility = (VkSurfacePresentModeCompatibilityEXT*)next;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The CAlayer underlying the surface must be a CAMetalLayer.
|
||||
MVKSurface* surface = (MVKSurface*)pSurfaceInfo->surface;
|
||||
CAMetalLayer* mtlLayer = surface->getCAMetalLayer();
|
||||
if ( !mtlLayer ) { return surface->getConfigurationResult(); }
|
||||
|
||||
VkExtent2D surfExtnt = mvkVkExtent2DFromCGSize(mtlLayer.naturalDrawableSizeMVK);
|
||||
VkSurfaceCapabilitiesKHR& surfCaps = pSurfaceCapabilities->surfaceCapabilities;
|
||||
surfCaps.minImageCount = _metalFeatures.minSwapchainImageCount;
|
||||
surfCaps.maxImageCount = _metalFeatures.maxSwapchainImageCount;
|
||||
surfCaps.currentExtent = mvkGetNaturalExtent(mtlLayer);
|
||||
surfCaps.minImageExtent = { 1, 1 };
|
||||
surfCaps.maxImageExtent = { _properties.limits.maxImageDimension2D, _properties.limits.maxImageDimension2D };
|
||||
surfCaps.maxImageArrayLayers = 1;
|
||||
surfCaps.supportedTransforms = (VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR);
|
||||
surfCaps.currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
surfCaps.supportedCompositeAlpha = (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR |
|
||||
VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR |
|
||||
VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR);
|
||||
surfCaps.supportedUsageFlags = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_STORAGE_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||
|
||||
pSurfaceCapabilities->minImageCount = _metalFeatures.minSwapchainImageCount;
|
||||
pSurfaceCapabilities->maxImageCount = _metalFeatures.maxSwapchainImageCount;
|
||||
// Swapchain-to-surface scaling capabilities.
|
||||
if (pScalingCaps) {
|
||||
pScalingCaps->supportedPresentScaling = (VK_PRESENT_SCALING_ONE_TO_ONE_BIT_EXT |
|
||||
VK_PRESENT_SCALING_ASPECT_RATIO_STRETCH_BIT_EXT |
|
||||
VK_PRESENT_SCALING_STRETCH_BIT_EXT);
|
||||
pScalingCaps->supportedPresentGravityX = (VK_PRESENT_GRAVITY_MIN_BIT_EXT |
|
||||
VK_PRESENT_GRAVITY_MAX_BIT_EXT |
|
||||
VK_PRESENT_GRAVITY_CENTERED_BIT_EXT);
|
||||
pScalingCaps->supportedPresentGravityY = (VK_PRESENT_GRAVITY_MIN_BIT_EXT |
|
||||
VK_PRESENT_GRAVITY_MAX_BIT_EXT |
|
||||
VK_PRESENT_GRAVITY_CENTERED_BIT_EXT);
|
||||
pScalingCaps->minScaledImageExtent = surfCaps.minImageExtent;
|
||||
pScalingCaps->maxScaledImageExtent = surfCaps.maxImageExtent;
|
||||
}
|
||||
|
||||
|
||||
// Per spec, always include the queried present mode in returned compatibility results.
|
||||
MVKSmallVector<VkPresentModeKHR> compatiblePresentModes;
|
||||
if (presentMode != VK_PRESENT_MODE_MAX_ENUM_KHR) { compatiblePresentModes.push_back(presentMode); }
|
||||
|
||||
// Customize results based on the provided present mode.
|
||||
switch (presentMode) {
|
||||
case VK_PRESENT_MODE_FIFO_KHR:
|
||||
// This could be dodgy, because Metal may not match the Vulkan spec's tight
|
||||
// requirements for transitioning from FIFO to IMMEDIATE, because the change to
|
||||
// IMMEDIATE may occur while some FIFO items are still on the GPU queue.
|
||||
if (_metalFeatures.presentModeImmediate) {
|
||||
compatiblePresentModes.push_back(VK_PRESENT_MODE_IMMEDIATE_KHR);
|
||||
}
|
||||
break;
|
||||
case VK_PRESENT_MODE_IMMEDIATE_KHR:
|
||||
compatiblePresentModes.push_back(VK_PRESENT_MODE_FIFO_KHR);
|
||||
surfCaps.minImageCount = surfCaps.maxImageCount; // Recommend always using max count to avoid visual tearing.
|
||||
break;
|
||||
case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR:
|
||||
case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR:
|
||||
// Although these are not advertised as supported, per spec, counts must be 1.
|
||||
surfCaps.minImageCount = 1;
|
||||
surfCaps.maxImageCount = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// If compatible present modes are requested, return them, otherwise return the count of them.
|
||||
if (pCompatibility) {
|
||||
if (pCompatibility->pPresentModes) {
|
||||
pCompatibility->presentModeCount = min(pCompatibility->presentModeCount, (uint32_t)compatiblePresentModes.size());
|
||||
for (uint32_t pmIdx = 0; pmIdx < pCompatibility->presentModeCount; pmIdx++) {
|
||||
pCompatibility->pPresentModes[pmIdx] = compatiblePresentModes[pmIdx];
|
||||
}
|
||||
} else {
|
||||
pCompatibility->presentModeCount = (uint32_t)compatiblePresentModes.size();
|
||||
}
|
||||
}
|
||||
|
||||
pSurfaceCapabilities->currentExtent = surfExtnt;
|
||||
pSurfaceCapabilities->minImageExtent = { 1, 1 };
|
||||
pSurfaceCapabilities->maxImageExtent = { _properties.limits.maxImageDimension2D, _properties.limits.maxImageDimension2D };
|
||||
pSurfaceCapabilities->maxImageArrayLayers = 1;
|
||||
pSurfaceCapabilities->supportedTransforms = (VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR);
|
||||
pSurfaceCapabilities->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
pSurfaceCapabilities->supportedCompositeAlpha = (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR |
|
||||
VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR |
|
||||
VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR);
|
||||
pSurfaceCapabilities->supportedUsageFlags = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_STORAGE_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
@ -1277,7 +1389,7 @@ VkResult MVKPhysicalDevice::getPresentRectangles(MVKSurface* surface,
|
||||
*pRectCount = 1;
|
||||
|
||||
pRects[0].offset = { 0, 0 };
|
||||
pRects[0].extent = mvkVkExtent2DFromCGSize(mtlLayer.naturalDrawableSizeMVK);
|
||||
pRects[0].extent = mvkGetNaturalExtent(mtlLayer);
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
@ -3462,7 +3574,8 @@ MVKPresentableSwapchainImage* MVKDevice::createPresentableSwapchainImage(const V
|
||||
MVKSwapchain* swapchain,
|
||||
uint32_t swapchainIndex,
|
||||
const VkAllocationCallbacks* pAllocator) {
|
||||
MVKPresentableSwapchainImage* mvkImg = new MVKPresentableSwapchainImage(this, pCreateInfo, swapchain, swapchainIndex);
|
||||
MVKPresentableSwapchainImage* mvkImg = new MVKPresentableSwapchainImage(this, pCreateInfo,
|
||||
swapchain, swapchainIndex);
|
||||
for (auto& memoryBinding : mvkImg->_memoryBindings) {
|
||||
addResource(memoryBinding);
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ MVK_DEVICE_FEATURE_EXTN(FragmentShaderBarycentric, FRAGMENT_SHADER_BARYCENTRIC,
|
||||
MVK_DEVICE_FEATURE_EXTN(PortabilitySubset, PORTABILITY_SUBSET, KHR, 15)
|
||||
MVK_DEVICE_FEATURE_EXTN(FragmentShaderInterlock, FRAGMENT_SHADER_INTERLOCK, EXT, 3)
|
||||
MVK_DEVICE_FEATURE_EXTN(Robustness2, ROBUSTNESS_2, EXT, 3)
|
||||
MVK_DEVICE_FEATURE_EXTN(SwapchainMaintenance1, SWAPCHAIN_MAINTENANCE_1, EXT, 1)
|
||||
MVK_DEVICE_FEATURE_EXTN(TexelBufferAlignment, TEXEL_BUFFER_ALIGNMENT, EXT, 1)
|
||||
MVK_DEVICE_FEATURE_EXTN(VertexAttributeDivisor, VERTEX_ATTRIBUTE_DIVISOR, EXT, 2)
|
||||
MVK_DEVICE_FEATURE_EXTN(ShaderIntegerFunctions2, SHADER_INTEGER_FUNCTIONS_2, INTEL, 1)
|
||||
|
@ -423,13 +423,15 @@ typedef struct MVKSwapchainImageAvailability {
|
||||
bool operator< (const MVKSwapchainImageAvailability& rhs) const;
|
||||
} MVKSwapchainImageAvailability;
|
||||
|
||||
/** VK_GOOGLE_display_timing extension info */
|
||||
/** Presentation info. */
|
||||
typedef struct {
|
||||
MVKPresentableSwapchainImage* presentableImage;
|
||||
bool hasPresentTime; // Keep track of whether presentation included VK_GOOGLE_display_timing
|
||||
uint32_t presentID; // VK_GOOGLE_display_timing presentID
|
||||
MVKFence* fence; // VK_EXT_swapchain_maintenance1 fence signaled when resources can be destroyed
|
||||
uint64_t desiredPresentTime; // VK_GOOGLE_display_timing desired presentation time in nanoseconds
|
||||
} MVKPresentTimingInfo;
|
||||
uint32_t presentID; // VK_GOOGLE_display_timing presentID
|
||||
VkPresentModeKHR presentMode; // VK_EXT_swapchain_maintenance1 present mode specialization
|
||||
bool hasPresentTime; // Keep track of whether presentation included VK_GOOGLE_display_timing
|
||||
} MVKImagePresentInfo;
|
||||
|
||||
/** Tracks a semaphore and fence for later signaling. */
|
||||
struct MVKSwapchainSignaler {
|
||||
@ -447,16 +449,13 @@ public:
|
||||
#pragma mark Metal
|
||||
|
||||
/** Presents the contained drawable to the OS. */
|
||||
void presentCAMetalDrawable(id<MTLCommandBuffer> mtlCmdBuff,
|
||||
MVKPresentTimingInfo presentTimingInfo);
|
||||
void presentCAMetalDrawable(id<MTLCommandBuffer> mtlCmdBuff, MVKImagePresentInfo& presentInfo);
|
||||
|
||||
|
||||
#pragma mark Construction
|
||||
|
||||
MVKPresentableSwapchainImage(MVKDevice* device,
|
||||
const VkImageCreateInfo* pCreateInfo,
|
||||
MVKSwapchain* swapchain,
|
||||
uint32_t swapchainIndex);
|
||||
MVKPresentableSwapchainImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo,
|
||||
MVKSwapchain* swapchain, uint32_t swapchainIndex);
|
||||
|
||||
~MVKPresentableSwapchainImage() override;
|
||||
|
||||
@ -464,16 +463,12 @@ protected:
|
||||
friend MVKSwapchain;
|
||||
|
||||
id<CAMetalDrawable> getCAMetalDrawable() override;
|
||||
void addPresentedHandler(id<CAMetalDrawable> mtlDrawable, MVKPresentTimingInfo presentTimingInfo);
|
||||
void addPresentedHandler(id<CAMetalDrawable> mtlDrawable, MVKImagePresentInfo& presentInfo);
|
||||
void releaseMetalDrawable();
|
||||
MVKSwapchainImageAvailability getAvailability();
|
||||
void makeAvailable(const MVKSwapchainSignaler& signaler);
|
||||
void makeAvailable();
|
||||
void acquireAndSignalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence);
|
||||
void signal(const MVKSwapchainSignaler& signaler, id<MTLCommandBuffer> mtlCmdBuff);
|
||||
void signalPresentationSemaphore(const MVKSwapchainSignaler& signaler, id<MTLCommandBuffer> mtlCmdBuff);
|
||||
static void markAsTracked(const MVKSwapchainSignaler& signaler);
|
||||
static void unmarkAsTracked(const MVKSwapchainSignaler& signaler);
|
||||
void untrackAllSignalers();
|
||||
void renderWatermark(id<MTLCommandBuffer> mtlCmdBuff);
|
||||
|
||||
id<CAMetalDrawable> _mtlDrawable;
|
||||
|
@ -1218,13 +1218,30 @@ MVKSwapchainImageAvailability MVKPresentableSwapchainImage::getAvailability() {
|
||||
return _availability;
|
||||
}
|
||||
|
||||
// Makes an image available for acquisition by the app.
|
||||
// If any semaphores are waiting to be signaled when this image becomes available, the
|
||||
// earliest semaphore is signaled, and this image remains unavailable for other uses.
|
||||
void MVKPresentableSwapchainImage::makeAvailable(const MVKSwapchainSignaler& signaler) {
|
||||
lock_guard<mutex> lock(_availabilityLock);
|
||||
// If present, signal the semaphore for the first waiter for the given image.
|
||||
static void signalPresentationSemaphore(const MVKSwapchainSignaler& signaler, id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
if (signaler.semaphore) { signaler.semaphore->encodeDeferredSignal(mtlCmdBuff, signaler.semaphoreSignalToken); }
|
||||
}
|
||||
|
||||
// Signal the semaphore and fence, and let them know they are no longer being tracked.
|
||||
// Signal either or both of the semaphore and fence in the specified tracker pair.
|
||||
static void signal(const MVKSwapchainSignaler& signaler, id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
if (signaler.semaphore) { signaler.semaphore->encodeDeferredSignal(mtlCmdBuff, signaler.semaphoreSignalToken); }
|
||||
if (signaler.fence) { signaler.fence->signal(); }
|
||||
}
|
||||
|
||||
// Tell the semaphore and fence that they are being tracked for future signaling.
|
||||
static void markAsTracked(const MVKSwapchainSignaler& signaler) {
|
||||
if (signaler.semaphore) { signaler.semaphore->retain(); }
|
||||
if (signaler.fence) { signaler.fence->retain(); }
|
||||
}
|
||||
|
||||
// Tell the semaphore and fence that they are no longer being tracked for future signaling.
|
||||
static void unmarkAsTracked(const MVKSwapchainSignaler& signaler) {
|
||||
if (signaler.semaphore) { signaler.semaphore->release(); }
|
||||
if (signaler.fence) { signaler.fence->release(); }
|
||||
}
|
||||
|
||||
static void signalAndUnmarkAsTracked(const MVKSwapchainSignaler& signaler) {
|
||||
signal(signaler, nil);
|
||||
unmarkAsTracked(signaler);
|
||||
}
|
||||
@ -1263,45 +1280,6 @@ void MVKPresentableSwapchainImage::acquireAndSignalWhenAvailable(MVKSemaphore* s
|
||||
markAsTracked(signaler);
|
||||
}
|
||||
|
||||
// If present, signal the semaphore for the first waiter for the given image.
|
||||
void MVKPresentableSwapchainImage::signalPresentationSemaphore(const MVKSwapchainSignaler& signaler, id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
MVKSemaphore* mvkSem = signaler.semaphore;
|
||||
if (mvkSem) { mvkSem->encodeDeferredSignal(mtlCmdBuff, signaler.semaphoreSignalToken); }
|
||||
}
|
||||
|
||||
// Signal either or both of the semaphore and fence in the specified tracker pair.
|
||||
void MVKPresentableSwapchainImage::signal(const MVKSwapchainSignaler& signaler, id<MTLCommandBuffer> mtlCmdBuff) {
|
||||
if (signaler.semaphore) { signaler.semaphore->encodeDeferredSignal(mtlCmdBuff, signaler.semaphoreSignalToken); }
|
||||
if (signaler.fence) { signaler.fence->signal(); }
|
||||
}
|
||||
|
||||
// Tell the semaphore and fence that they are being tracked for future signaling.
|
||||
void MVKPresentableSwapchainImage::markAsTracked(const MVKSwapchainSignaler& signaler) {
|
||||
if (signaler.semaphore) { signaler.semaphore->retain(); }
|
||||
if (signaler.fence) { signaler.fence->retain(); }
|
||||
}
|
||||
|
||||
// Tell the semaphore and fence that they are no longer being tracked for future signaling.
|
||||
void MVKPresentableSwapchainImage::unmarkAsTracked(const MVKSwapchainSignaler& signaler) {
|
||||
if (signaler.semaphore) { signaler.semaphore->release(); }
|
||||
if (signaler.fence) { signaler.fence->release(); }
|
||||
}
|
||||
|
||||
// Untrack any signalers that are still tracking, releasing the fences and semaphores.
|
||||
void MVKPresentableSwapchainImage::untrackAllSignalers() {
|
||||
lock_guard<mutex> lock(_availabilityLock);
|
||||
|
||||
if ( !_availability.isAvailable ) {
|
||||
unmarkAsTracked(_preSignaler);
|
||||
for (auto& sig : _availabilitySignalers) {
|
||||
unmarkAsTracked(sig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Metal
|
||||
|
||||
id<CAMetalDrawable> MVKPresentableSwapchainImage::getCAMetalDrawable() {
|
||||
while ( !_mtlDrawable ) {
|
||||
@autoreleasepool { // Reclaim auto-released drawable object before end of loop
|
||||
@ -1318,8 +1296,7 @@ id<CAMetalDrawable> MVKPresentableSwapchainImage::getCAMetalDrawable() {
|
||||
|
||||
// Present the drawable and make myself available only once the command buffer has completed.
|
||||
void MVKPresentableSwapchainImage::presentCAMetalDrawable(id<MTLCommandBuffer> mtlCmdBuff,
|
||||
MVKPresentTimingInfo presentTimingInfo) {
|
||||
|
||||
MVKImagePresentInfo& presentInfo) {
|
||||
lock_guard<mutex> lock(_availabilityLock);
|
||||
|
||||
_swapchain->willPresentSurface(getMTLTexture(0), mtlCmdBuff);
|
||||
@ -1330,10 +1307,15 @@ void MVKPresentableSwapchainImage::presentCAMetalDrawable(id<MTLCommandBuffer> m
|
||||
// Attach present handler before presenting to avoid race condition.
|
||||
id<CAMetalDrawable> mtlDrwbl = getCAMetalDrawable();
|
||||
[mtlCmdBuff addScheduledHandler: ^(id<MTLCommandBuffer> mcb) {
|
||||
if (presentTimingInfo.hasPresentTime) {
|
||||
// Try to do any present mode transitions as late as possible in an attempt
|
||||
// to avoid visual disruptions on any presents already on the queue.
|
||||
if (presentInfo.presentMode != VK_PRESENT_MODE_MAX_ENUM_KHR) {
|
||||
mtlDrwbl.layer.displaySyncEnabledMVK = (presentInfo.presentMode != VK_PRESENT_MODE_IMMEDIATE_KHR);
|
||||
}
|
||||
if (presentInfo.hasPresentTime) {
|
||||
// Convert from nsecs to seconds for Metal
|
||||
addPresentedHandler(mtlDrwbl, presentTimingInfo);
|
||||
[mtlDrwbl presentAtTime: (double)presentTimingInfo.desiredPresentTime * 1.0e-9];
|
||||
addPresentedHandler(mtlDrwbl, presentInfo);
|
||||
[mtlDrwbl presentAtTime: (double)presentInfo.desiredPresentTime * 1.0e-9];
|
||||
} else {
|
||||
[mtlDrwbl present];
|
||||
}
|
||||
@ -1349,7 +1331,6 @@ void MVKPresentableSwapchainImage::presentCAMetalDrawable(id<MTLCommandBuffer> m
|
||||
// Because the semaphore or fence will be signaled by more than one image, it will
|
||||
// get out of sync, and the final use of the image would not be signaled as a result.
|
||||
signaler = _preSignaler;
|
||||
// Save the command buffer in case this image is acquired before presentation is finished.
|
||||
} else {
|
||||
// If this image is not yet available, extract and signal the first semaphore and fence.
|
||||
auto sigIter = _availabilitySignalers.begin();
|
||||
@ -1357,21 +1338,28 @@ void MVKPresentableSwapchainImage::presentCAMetalDrawable(id<MTLCommandBuffer> m
|
||||
_availabilitySignalers.erase(sigIter);
|
||||
}
|
||||
|
||||
// Ensure this image and the drawable are not destroyed while awaiting MTLCommandBuffer completion.
|
||||
// We retain the drawable separately because new drawable might be acquired by this image by then.
|
||||
// Ensure this image, the drawable, and the present fence are not destroyed while
|
||||
// awaiting MTLCommandBuffer completion. We retain the drawable separately because
|
||||
// a new drawable might be acquired by this image by then.
|
||||
retain();
|
||||
[mtlDrwbl retain];
|
||||
auto* fence = presentInfo.fence;
|
||||
if (fence) { fence->retain(); }
|
||||
[mtlCmdBuff addCompletedHandler: ^(id<MTLCommandBuffer> mcb) {
|
||||
[mtlDrwbl release];
|
||||
makeAvailable(signaler);
|
||||
release();
|
||||
if (fence) {
|
||||
fence->signal();
|
||||
fence->release();
|
||||
}
|
||||
}];
|
||||
|
||||
signalPresentationSemaphore(signaler, mtlCmdBuff);
|
||||
}
|
||||
|
||||
void MVKPresentableSwapchainImage::addPresentedHandler(id<CAMetalDrawable> mtlDrawable,
|
||||
MVKPresentTimingInfo presentTimingInfo) {
|
||||
MVKImagePresentInfo& presentInfo) {
|
||||
#if !MVK_OS_SIMULATOR
|
||||
if ([mtlDrawable respondsToSelector: @selector(addPresentedHandler:)]) {
|
||||
retain(); // Ensure this image is not destroyed while awaiting presentation
|
||||
@ -1379,7 +1367,7 @@ void MVKPresentableSwapchainImage::addPresentedHandler(id<CAMetalDrawable> mtlDr
|
||||
// Since we're in a callback, it's possible that the swapchain has been released by now.
|
||||
// Lock the swapchain, and test if it is present before doing anything with it.
|
||||
lock_guard<mutex> cblock(_swapchainLock);
|
||||
if (_swapchain) { _swapchain->recordPresentTime(presentTimingInfo, drawable.presentedTime * 1.0e9); }
|
||||
if (_swapchain) { _swapchain->recordPresentTime(presentInfo, drawable.presentedTime * 1.0e9); }
|
||||
release();
|
||||
}];
|
||||
return;
|
||||
@ -1392,7 +1380,7 @@ void MVKPresentableSwapchainImage::addPresentedHandler(id<CAMetalDrawable> mtlDr
|
||||
// the swapchain has been released by the time this function runs.
|
||||
// Lock the swapchain, and test if it is present before doing anything with it.
|
||||
lock_guard<mutex> lock(_swapchainLock);
|
||||
if (_swapchain) {_swapchain->recordPresentTime(presentTimingInfo); }
|
||||
if (_swapchain) {_swapchain->recordPresentTime(presentInfo); }
|
||||
}
|
||||
|
||||
// Resets the MTLTexture and CAMetalDrawable underlying this image.
|
||||
@ -1404,6 +1392,29 @@ void MVKPresentableSwapchainImage::releaseMetalDrawable() {
|
||||
_mtlDrawable = nil;
|
||||
}
|
||||
|
||||
// Makes an image available for acquisition by the app.
|
||||
// If any semaphores are waiting to be signaled when this image becomes available, the
|
||||
// earliest semaphore is signaled, and this image remains unavailable for other uses.
|
||||
void MVKPresentableSwapchainImage::makeAvailable(const MVKSwapchainSignaler& signaler) {
|
||||
lock_guard<mutex> lock(_availabilityLock);
|
||||
|
||||
signalAndUnmarkAsTracked(signaler);
|
||||
}
|
||||
|
||||
// Signal, untrack, and release any signalers that are tracking.
|
||||
void MVKPresentableSwapchainImage::makeAvailable() {
|
||||
lock_guard<mutex> lock(_availabilityLock);
|
||||
|
||||
if ( !_availability.isAvailable ) {
|
||||
signalAndUnmarkAsTracked(_preSignaler);
|
||||
for (auto& sig : _availabilitySignalers) {
|
||||
signalAndUnmarkAsTracked(sig);
|
||||
}
|
||||
_availabilitySignalers.clear();
|
||||
_availability.isAvailable = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Construction
|
||||
|
||||
@ -1421,10 +1432,10 @@ MVKPresentableSwapchainImage::MVKPresentableSwapchainImage(MVKDevice* device,
|
||||
}
|
||||
|
||||
// Unsignaled signalers will exist if this image is acquired more than it is presented.
|
||||
// Ensure they are untracked so the fences and semaphores will be released.
|
||||
// Ensure they are signaled and untracked so the fences and semaphores will be released.
|
||||
MVKPresentableSwapchainImage::~MVKPresentableSwapchainImage() {
|
||||
releaseMetalDrawable();
|
||||
untrackAllSignalers();
|
||||
makeAvailable();
|
||||
}
|
||||
|
||||
|
||||
|
@ -729,9 +729,9 @@ void MVKInstance::initProcAddrs() {
|
||||
ADD_DVC_EXT_ENTRY_POINT(vkSetPrivateDataEXT, EXT_PRIVATE_DATA);
|
||||
ADD_DVC_EXT_ENTRY_POINT(vkGetPhysicalDeviceMultisamplePropertiesEXT, EXT_SAMPLE_LOCATIONS);
|
||||
ADD_DVC_EXT_ENTRY_POINT(vkCmdSetSampleLocationsEXT, EXT_SAMPLE_LOCATIONS);
|
||||
ADD_DVC_EXT_ENTRY_POINT(vkReleaseSwapchainImagesEXT, EXT_SWAPCHAIN_MAINTENANCE_1);
|
||||
ADD_DVC_EXT_ENTRY_POINT(vkGetRefreshCycleDurationGOOGLE, GOOGLE_DISPLAY_TIMING);
|
||||
ADD_DVC_EXT_ENTRY_POINT(vkGetPastPresentationTimingGOOGLE, GOOGLE_DISPLAY_TIMING);
|
||||
|
||||
}
|
||||
|
||||
void MVKInstance::logVersions() {
|
||||
|
@ -189,6 +189,8 @@ public:
|
||||
protected:
|
||||
friend class MVKQueue;
|
||||
|
||||
virtual void finish() = 0;
|
||||
|
||||
MVKQueue* _queue;
|
||||
MVKSmallVector<std::pair<MVKSemaphore*, uint64_t>> _waitSemaphores;
|
||||
};
|
||||
@ -213,7 +215,7 @@ protected:
|
||||
id<MTLCommandBuffer> getActiveMTLCommandBuffer();
|
||||
void setActiveMTLCommandBuffer(id<MTLCommandBuffer> mtlCmdBuff);
|
||||
void commitActiveMTLCommandBuffer(bool signalCompletion = false);
|
||||
virtual void finish();
|
||||
void finish() override;
|
||||
virtual void submitCommandBuffers() {}
|
||||
|
||||
MVKSmallVector<std::pair<MVKSemaphore*, uint64_t>> _signalSemaphores;
|
||||
@ -270,8 +272,8 @@ public:
|
||||
const VkPresentInfoKHR* pPresentInfo);
|
||||
|
||||
protected:
|
||||
void stopAutoGPUCapture();
|
||||
void finish() override;
|
||||
|
||||
MVKSmallVector<MVKPresentTimingInfo, 4> _presentInfo;
|
||||
MVKSmallVector<MVKImagePresentInfo, 4> _presentInfo;
|
||||
};
|
||||
|
||||
|
@ -290,11 +290,15 @@ MVKQueueSubmission::MVKQueueSubmission(MVKQueue* queue,
|
||||
|
||||
_waitSemaphores.reserve(waitSemaphoreCount);
|
||||
for (uint32_t i = 0; i < waitSemaphoreCount; i++) {
|
||||
_waitSemaphores.push_back(make_pair((MVKSemaphore*)pWaitSemaphores[i], (uint64_t)0));
|
||||
auto* sem4 = (MVKSemaphore*)pWaitSemaphores[i];
|
||||
sem4->retain();
|
||||
uint64_t sem4Val = 0;
|
||||
_waitSemaphores.emplace_back(sem4, sem4Val);
|
||||
}
|
||||
}
|
||||
|
||||
MVKQueueSubmission::~MVKQueueSubmission() {
|
||||
for (auto s : _waitSemaphores) { s.first->release(); }
|
||||
_queue->release();
|
||||
}
|
||||
|
||||
@ -530,47 +534,61 @@ void MVKQueueFullCommandBufferSubmission<N>::finish() {
|
||||
#pragma mark -
|
||||
#pragma mark MVKQueuePresentSurfaceSubmission
|
||||
|
||||
// If the semaphores are encodable, wait on them by encoding them on the MTLCommandBuffer before presenting.
|
||||
// If the semaphores are not encodable, wait on them inline after presenting.
|
||||
// The semaphores know what to do.
|
||||
void MVKQueuePresentSurfaceSubmission::execute() {
|
||||
// If the semaphores are encodable, wait on them by encoding them on the MTLCommandBuffer before presenting.
|
||||
// If the semaphores are not encodable, wait on them inline after presenting.
|
||||
// The semaphores know what to do.
|
||||
id<MTLCommandBuffer> mtlCmdBuff = _queue->getMTLCommandBuffer(kMVKCommandUseQueuePresent);
|
||||
[mtlCmdBuff enqueue];
|
||||
for (auto& ws : _waitSemaphores) { ws.first->encodeWait(mtlCmdBuff, 0); }
|
||||
|
||||
// Add completion handler that will destroy this submission only once the MTLCommandBuffer
|
||||
// is finished with the resources retained here, including the wait semaphores.
|
||||
// Completion handlers are also added in presentCAMetalDrawable() to retain the swapchain images.
|
||||
[mtlCmdBuff addCompletedHandler: ^(id<MTLCommandBuffer> mcb) {
|
||||
this->finish();
|
||||
}];
|
||||
|
||||
for (int i = 0; i < _presentInfo.size(); i++ ) {
|
||||
MVKPresentableSwapchainImage *img = _presentInfo[i].presentableImage;
|
||||
img->presentCAMetalDrawable(mtlCmdBuff, _presentInfo[i]);
|
||||
_presentInfo[i].presentableImage->presentCAMetalDrawable(mtlCmdBuff, _presentInfo[i]);
|
||||
}
|
||||
|
||||
for (auto& ws : _waitSemaphores) { ws.first->encodeWait(nil, 0); }
|
||||
[mtlCmdBuff commit];
|
||||
}
|
||||
|
||||
// Let Xcode know the current frame is done, then start a new frame
|
||||
void MVKQueuePresentSurfaceSubmission::finish() {
|
||||
|
||||
// Let Xcode know the current frame is done, then start a new frame,
|
||||
// and if auto GPU capture is active, and it's time to stop it, do so.
|
||||
auto cs = _queue->_submissionCaptureScope;
|
||||
cs->endScope();
|
||||
cs->beginScope();
|
||||
stopAutoGPUCapture();
|
||||
|
||||
this->destroy();
|
||||
}
|
||||
|
||||
void MVKQueuePresentSurfaceSubmission::stopAutoGPUCapture() {
|
||||
if (_queue->_queueFamily->getIndex() == mvkConfig().defaultGPUCaptureScopeQueueFamilyIndex &&
|
||||
_queue->_index == mvkConfig().defaultGPUCaptureScopeQueueIndex) {
|
||||
_queue->getDevice()->stopAutoGPUCapture(MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_FRAME);
|
||||
}
|
||||
|
||||
this->destroy();
|
||||
}
|
||||
|
||||
MVKQueuePresentSurfaceSubmission::MVKQueuePresentSurfaceSubmission(MVKQueue* queue,
|
||||
const VkPresentInfoKHR* pPresentInfo)
|
||||
: MVKQueueSubmission(queue, pPresentInfo->waitSemaphoreCount, pPresentInfo->pWaitSemaphores) {
|
||||
|
||||
const VkPresentTimesInfoGOOGLE *pPresentTimesInfoGOOGLE = nullptr;
|
||||
for ( const auto *next = ( VkBaseInStructure* ) pPresentInfo->pNext; next; next = next->pNext )
|
||||
{
|
||||
switch ( next->sType )
|
||||
{
|
||||
const VkPresentTimesInfoGOOGLE* pPresentTimesInfo = nullptr;
|
||||
const VkSwapchainPresentFenceInfoEXT* pPresentFenceInfo = nullptr;
|
||||
const VkSwapchainPresentModeInfoEXT* pPresentModeInfo = nullptr;
|
||||
for (auto* next = (const VkBaseInStructure*)pPresentInfo->pNext; next; next = next->pNext) {
|
||||
switch (next->sType) {
|
||||
case VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT:
|
||||
pPresentFenceInfo = (const VkSwapchainPresentFenceInfoEXT*) next;
|
||||
break;
|
||||
case VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT:
|
||||
pPresentModeInfo = (const VkSwapchainPresentModeInfoEXT*) next;
|
||||
break;
|
||||
case VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE:
|
||||
pPresentTimesInfoGOOGLE = ( const VkPresentTimesInfoGOOGLE * ) next;
|
||||
pPresentTimesInfo = (const VkPresentTimesInfoGOOGLE*) next;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -579,21 +597,34 @@ MVKQueuePresentSurfaceSubmission::MVKQueuePresentSurfaceSubmission(MVKQueue* que
|
||||
|
||||
// Populate the array of swapchain images, testing each one for status
|
||||
uint32_t scCnt = pPresentInfo->swapchainCount;
|
||||
const VkPresentTimeGOOGLE *pPresentTimesGOOGLE = nullptr;
|
||||
if ( pPresentTimesInfoGOOGLE && pPresentTimesInfoGOOGLE->pTimes ) {
|
||||
pPresentTimesGOOGLE = pPresentTimesInfoGOOGLE->pTimes;
|
||||
MVKAssert( pPresentTimesInfoGOOGLE->swapchainCount == pPresentInfo->swapchainCount, "VkPresentTimesInfoGOOGLE swapchainCount must match VkPresentInfo swapchainCount" );
|
||||
const VkPresentTimeGOOGLE* pPresentTimes = nullptr;
|
||||
if (pPresentTimesInfo && pPresentTimesInfo->pTimes) {
|
||||
pPresentTimes = pPresentTimesInfo->pTimes;
|
||||
MVKAssert(pPresentTimesInfo->swapchainCount == scCnt, "VkPresentTimesInfoGOOGLE swapchainCount must match VkPresentInfo swapchainCount.");
|
||||
}
|
||||
const VkPresentModeKHR* pPresentModes = nullptr;
|
||||
if (pPresentModeInfo) {
|
||||
pPresentModes = pPresentModeInfo->pPresentModes;
|
||||
MVKAssert(pPresentModeInfo->swapchainCount == scCnt, "VkSwapchainPresentModeInfoEXT swapchainCount must match VkPresentInfo swapchainCount.");
|
||||
}
|
||||
const VkFence* pFences = nullptr;
|
||||
if (pPresentFenceInfo) {
|
||||
pFences = pPresentFenceInfo->pFences;
|
||||
MVKAssert(pPresentFenceInfo->swapchainCount == scCnt, "VkSwapchainPresentFenceInfoEXT swapchainCount must match VkPresentInfo swapchainCount.");
|
||||
}
|
||||
|
||||
VkResult* pSCRslts = pPresentInfo->pResults;
|
||||
_presentInfo.reserve(scCnt);
|
||||
for (uint32_t scIdx = 0; scIdx < scCnt; scIdx++) {
|
||||
MVKSwapchain* mvkSC = (MVKSwapchain*)pPresentInfo->pSwapchains[scIdx];
|
||||
MVKPresentTimingInfo presentInfo = {};
|
||||
MVKImagePresentInfo presentInfo = {};
|
||||
presentInfo.presentableImage = mvkSC->getPresentableImage(pPresentInfo->pImageIndices[scIdx]);
|
||||
if ( pPresentTimesGOOGLE ) {
|
||||
presentInfo.presentMode = pPresentModes ? pPresentModes[scIdx] : VK_PRESENT_MODE_MAX_ENUM_KHR;
|
||||
presentInfo.fence = pFences ? (MVKFence*)pFences[scIdx] : nullptr;
|
||||
if (pPresentTimes) {
|
||||
presentInfo.hasPresentTime = true;
|
||||
presentInfo.presentID = pPresentTimesGOOGLE[scIdx].presentID;
|
||||
presentInfo.desiredPresentTime = pPresentTimesGOOGLE[scIdx].desiredPresentTime;
|
||||
presentInfo.presentID = pPresentTimes[scIdx].presentID;
|
||||
presentInfo.desiredPresentTime = pPresentTimes[scIdx].desiredPresentTime;
|
||||
} else {
|
||||
presentInfo.hasPresentTime = false;
|
||||
}
|
||||
|
@ -314,7 +314,7 @@ uint32_t mvkGetAttachments(const VkRenderingInfo* pRenderingInfo,
|
||||
VkClearValue clearValues[]);
|
||||
|
||||
/** Returns whether the view mask uses multiview. */
|
||||
static inline bool mvkIsMultiview(uint32_t viewMask) { return viewMask != 0; }
|
||||
static constexpr bool mvkIsMultiview(uint32_t viewMask) { return viewMask != 0; }
|
||||
|
||||
/** Returns whether the attachment is being used. */
|
||||
bool mvkIsColorAttachmentUsed(const VkPipelineRenderingCreateInfo* pRendInfo, uint32_t colorAttIdx);
|
||||
|
@ -42,8 +42,6 @@ MVKSurface::MVKSurface(MVKInstance* mvkInstance,
|
||||
const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator) : _mvkInstance(mvkInstance) {
|
||||
|
||||
// MVKLogInfo("%s(): This function is obsolete. Consider using the vkCreateMetalSurfaceEXT() function from the VK_EXT_metal_surface extension instead.", STR(vkCreate_PLATFORM_SurfaceMVK));
|
||||
|
||||
// Get the platform object contained in pView
|
||||
id<NSObject> obj = (id<NSObject>)pCreateInfo->pView;
|
||||
|
||||
|
@ -66,26 +66,27 @@ public:
|
||||
*/
|
||||
VkResult getImages(uint32_t* pCount, VkImage* pSwapchainImages);
|
||||
|
||||
/** Returns the index of the next swapchain image. */
|
||||
VkResult acquireNextImageKHR(uint64_t timeout,
|
||||
VkSemaphore semaphore,
|
||||
VkFence fence,
|
||||
uint32_t deviceMask,
|
||||
uint32_t* pImageIndex);
|
||||
/** Returns the index of the next acquireable image. */
|
||||
VkResult acquireNextImage(uint64_t timeout,
|
||||
VkSemaphore semaphore,
|
||||
VkFence fence,
|
||||
uint32_t deviceMask,
|
||||
uint32_t* pImageIndex);
|
||||
|
||||
/** Releases swapchain images. */
|
||||
VkResult releaseImages(const VkReleaseSwapchainImagesInfoEXT* pReleaseInfo);
|
||||
|
||||
/** Returns whether the parent surface is now lost and this swapchain must be recreated. */
|
||||
bool getIsSurfaceLost() { return _surfaceLost; }
|
||||
|
||||
/** Returns whether the surface size or resolution scale has changed since the last time this function was called. */
|
||||
bool getHasSurfaceSizeChanged() {
|
||||
return !CGSizeEqualToSize(_mtlLayer.naturalDrawableSizeMVK, _mtlLayer.drawableSize);
|
||||
}
|
||||
/** Returns whether this swapchain is optimally sized for the surface. */
|
||||
bool hasOptimalSurface();
|
||||
|
||||
/** Returns the status of the surface. Surface loss takes precedence over out-of-date errors. */
|
||||
/** Returns the status of the surface. Surface loss takes precedence over sub-optimal errors. */
|
||||
VkResult getSurfaceStatus() {
|
||||
if (_device->getConfigurationResult() != VK_SUCCESS) { return _device->getConfigurationResult(); }
|
||||
if (getIsSurfaceLost()) { return VK_ERROR_SURFACE_LOST_KHR; }
|
||||
if (getHasSurfaceSizeChanged()) { return VK_SUBOPTIMAL_KHR; }
|
||||
if ( !hasOptimalSurface() ) { return VK_SUBOPTIMAL_KHR; }
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
@ -110,7 +111,9 @@ protected:
|
||||
friend class MVKPresentableSwapchainImage;
|
||||
|
||||
void propagateDebugName() override;
|
||||
void initCAMetalLayer(const VkSwapchainCreateInfoKHR* pCreateInfo, uint32_t imgCnt);
|
||||
void initCAMetalLayer(const VkSwapchainCreateInfoKHR* pCreateInfo,
|
||||
VkSwapchainPresentScalingCreateInfoEXT* pScalingInfo,
|
||||
uint32_t imgCnt);
|
||||
void initSurfaceImages(const VkSwapchainCreateInfoKHR* pCreateInfo, uint32_t imgCnt);
|
||||
void releaseLayer();
|
||||
void releaseUndisplayedSurfaces();
|
||||
@ -118,22 +121,39 @@ protected:
|
||||
void willPresentSurface(id<MTLTexture> mtlTexture, id<MTLCommandBuffer> mtlCmdBuff);
|
||||
void renderWatermark(id<MTLTexture> mtlTexture, id<MTLCommandBuffer> mtlCmdBuff);
|
||||
void markFrameInterval();
|
||||
void recordPresentTime(MVKPresentTimingInfo presentTimingInfo, uint64_t actualPresentTime = 0);
|
||||
void recordPresentTime(MVKImagePresentInfo& presentInfo, uint64_t actualPresentTime = 0);
|
||||
|
||||
CAMetalLayer* _mtlLayer;
|
||||
MVKWatermark* _licenseWatermark;
|
||||
CAMetalLayer* _mtlLayer = nil;
|
||||
MVKWatermark* _licenseWatermark = nullptr;
|
||||
MVKSmallVector<MVKPresentableSwapchainImage*, kMVKMaxSwapchainImageCount> _presentableImages;
|
||||
std::atomic<uint64_t> _currentAcquisitionID;
|
||||
uint64_t _lastFrameTime;
|
||||
uint32_t _currentPerfLogFrameCount;
|
||||
std::atomic<bool> _surfaceLost;
|
||||
MVKBlockObserver* _layerObserver;
|
||||
std::mutex _layerLock;
|
||||
MVKSmallVector<VkPresentModeKHR, 2> _compatiblePresentModes;
|
||||
static const int kMaxPresentationHistory = 60;
|
||||
VkPastPresentationTimingGOOGLE _presentTimingHistory[kMaxPresentationHistory];
|
||||
uint32_t _presentHistoryCount;
|
||||
uint32_t _presentHistoryIndex;
|
||||
uint32_t _presentHistoryHeadIndex;
|
||||
std::atomic<uint64_t> _currentAcquisitionID = 0;
|
||||
MVKBlockObserver* _layerObserver = nil;
|
||||
std::mutex _presentHistoryLock;
|
||||
std::mutex _layerLock;
|
||||
uint64_t _lastFrameTime = 0;
|
||||
VkExtent2D _mtlLayerDrawableExtent = {0, 0};
|
||||
uint32_t _currentPerfLogFrameCount = 0;
|
||||
uint32_t _presentHistoryCount = 0;
|
||||
uint32_t _presentHistoryIndex = 0;
|
||||
uint32_t _presentHistoryHeadIndex = 0;
|
||||
std::atomic<bool> _surfaceLost = false;
|
||||
bool _isDeliberatelyScaled = false;
|
||||
};
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Support functions
|
||||
|
||||
/**
|
||||
* Returns the natural extent of the CAMetalLayer.
|
||||
*
|
||||
* The natural extent is the size of the bounds property of the layer,
|
||||
* multiplied by the contentsScale property of the layer, rounded
|
||||
* to nearest integer using half-to-even rounding.
|
||||
*/
|
||||
static inline VkExtent2D mvkGetNaturalExtent(CAMetalLayer* mtlLayer) {
|
||||
return mvkVkExtent2DFromCGSize(mtlLayer.naturalDrawableSizeMVK);
|
||||
}
|
||||
|
@ -70,11 +70,11 @@ VkResult MVKSwapchain::getImages(uint32_t* pCount, VkImage* pSwapchainImages) {
|
||||
return result;
|
||||
}
|
||||
|
||||
VkResult MVKSwapchain::acquireNextImageKHR(uint64_t timeout,
|
||||
VkSemaphore semaphore,
|
||||
VkFence fence,
|
||||
uint32_t deviceMask,
|
||||
uint32_t* pImageIndex) {
|
||||
VkResult MVKSwapchain::acquireNextImage(uint64_t timeout,
|
||||
VkSemaphore semaphore,
|
||||
VkFence fence,
|
||||
uint32_t deviceMask,
|
||||
uint32_t* pImageIndex) {
|
||||
|
||||
if ( _device->getConfigurationResult() != VK_SUCCESS ) { return _device->getConfigurationResult(); }
|
||||
if ( getIsSurfaceLost() ) { return VK_ERROR_SURFACE_LOST_KHR; }
|
||||
@ -100,6 +100,14 @@ VkResult MVKSwapchain::acquireNextImageKHR(uint64_t timeout,
|
||||
return getSurfaceStatus();
|
||||
}
|
||||
|
||||
VkResult MVKSwapchain::releaseImages(const VkReleaseSwapchainImagesInfoEXT* pReleaseInfo) {
|
||||
for (uint32_t imgIdxIdx = 0; imgIdxIdx < pReleaseInfo->imageIndexCount; imgIdxIdx++) {
|
||||
getPresentableImage(pReleaseInfo->pImageIndices[imgIdxIdx])->makeAvailable();
|
||||
}
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
uint64_t MVKSwapchain::getNextAcquisitionID() { return ++_currentAcquisitionID; }
|
||||
|
||||
// Releases any surfaces that are not currently being displayed,
|
||||
@ -107,6 +115,18 @@ uint64_t MVKSwapchain::getNextAcquisitionID() { return ++_currentAcquisitionID;
|
||||
void MVKSwapchain::releaseUndisplayedSurfaces() {}
|
||||
|
||||
|
||||
// This swapchain is optimally sized for the surface if the app has specified deliberate
|
||||
// swapchain scaling, or the CAMetalLayer drawableSize has not changed since the swapchain
|
||||
// was created, and the CAMetalLayer will not need to be scaled when composited.
|
||||
bool MVKSwapchain::hasOptimalSurface() {
|
||||
if (_isDeliberatelyScaled) { return true; }
|
||||
|
||||
VkExtent2D drawExtent = mvkVkExtent2DFromCGSize(_mtlLayer.drawableSize);
|
||||
return (mvkVkExtent2DsAreEqual(drawExtent, _mtlLayerDrawableExtent) &&
|
||||
mvkVkExtent2DsAreEqual(drawExtent, mvkGetNaturalExtent(_mtlLayer)));
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Rendering
|
||||
|
||||
// Called automatically when a swapchain image is about to be presented to the surface by the queue.
|
||||
@ -168,7 +188,7 @@ struct CIE1931XY {
|
||||
// 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 constexpr 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) };
|
||||
}
|
||||
@ -227,19 +247,37 @@ void MVKSwapchain::setHDRMetadataEXT(const VkHdrMetadataEXT& metadata) {
|
||||
#pragma mark Construction
|
||||
|
||||
MVKSwapchain::MVKSwapchain(MVKDevice* device,
|
||||
const VkSwapchainCreateInfoKHR* pCreateInfo) :
|
||||
MVKVulkanAPIDeviceObject(device),
|
||||
_licenseWatermark(nil),
|
||||
_currentAcquisitionID(0),
|
||||
_lastFrameTime(0),
|
||||
_currentPerfLogFrameCount(0),
|
||||
_surfaceLost(false),
|
||||
_layerObserver(nil),
|
||||
_presentHistoryCount(0),
|
||||
_presentHistoryIndex(0),
|
||||
_presentHistoryHeadIndex(0) {
|
||||
|
||||
const VkSwapchainCreateInfoKHR* pCreateInfo) : MVKVulkanAPIDeviceObject(device) {
|
||||
memset(_presentTimingHistory, 0, sizeof(_presentTimingHistory));
|
||||
|
||||
// Retrieve the scaling and present mode structs if they are supplied.
|
||||
VkSwapchainPresentScalingCreateInfoEXT* pScalingInfo = nullptr;
|
||||
VkSwapchainPresentModesCreateInfoEXT* pPresentModesInfo = nullptr;
|
||||
for (auto* next = (const VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) {
|
||||
switch (next->sType) {
|
||||
case VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_SCALING_CREATE_INFO_EXT: {
|
||||
pScalingInfo = (VkSwapchainPresentScalingCreateInfoEXT*)next;
|
||||
break;
|
||||
}
|
||||
case VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODES_CREATE_INFO_EXT: {
|
||||
pPresentModesInfo = (VkSwapchainPresentModesCreateInfoEXT*)next;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_isDeliberatelyScaled = pScalingInfo && pScalingInfo->scalingBehavior;
|
||||
|
||||
// Set the list of present modes that can be specified in a queue
|
||||
// present submission without causing the swapchain to be rebuilt.
|
||||
if (pPresentModesInfo) {
|
||||
for (uint32_t pmIdx = 0; pmIdx < pPresentModesInfo->presentModeCount; pmIdx++) {
|
||||
_compatiblePresentModes.push_back(pPresentModesInfo->pPresentModes[pmIdx]);
|
||||
}
|
||||
}
|
||||
|
||||
// If applicable, release any surfaces (not currently being displayed) from the old swapchain.
|
||||
MVKSwapchain* oldSwapchain = (MVKSwapchain*)pCreateInfo->oldSwapchain;
|
||||
if (oldSwapchain) { oldSwapchain->releaseUndisplayedSurfaces(); }
|
||||
@ -247,12 +285,51 @@ MVKSwapchain::MVKSwapchain(MVKDevice* device,
|
||||
uint32_t imgCnt = mvkClamp(pCreateInfo->minImageCount,
|
||||
_device->_pMetalFeatures->minSwapchainImageCount,
|
||||
_device->_pMetalFeatures->maxSwapchainImageCount);
|
||||
initCAMetalLayer(pCreateInfo, imgCnt);
|
||||
initCAMetalLayer(pCreateInfo, pScalingInfo, imgCnt);
|
||||
initSurfaceImages(pCreateInfo, imgCnt); // After initCAMetalLayer()
|
||||
}
|
||||
|
||||
// kCAGravityResize is the Metal default
|
||||
static CALayerContentsGravity getCALayerContentsGravity(VkSwapchainPresentScalingCreateInfoEXT* pScalingInfo) {
|
||||
|
||||
if( !pScalingInfo ) { return kCAGravityResize; }
|
||||
|
||||
switch (pScalingInfo->scalingBehavior) {
|
||||
case VK_PRESENT_SCALING_STRETCH_BIT_EXT: return kCAGravityResize;
|
||||
case VK_PRESENT_SCALING_ASPECT_RATIO_STRETCH_BIT_EXT: return kCAGravityResizeAspect;
|
||||
case VK_PRESENT_SCALING_ONE_TO_ONE_BIT_EXT:
|
||||
switch (pScalingInfo->presentGravityY) {
|
||||
case VK_PRESENT_GRAVITY_MIN_BIT_EXT:
|
||||
switch (pScalingInfo->presentGravityX) {
|
||||
case VK_PRESENT_GRAVITY_MIN_BIT_EXT: return kCAGravityTopLeft;
|
||||
case VK_PRESENT_GRAVITY_CENTERED_BIT_EXT: return kCAGravityTop;
|
||||
case VK_PRESENT_GRAVITY_MAX_BIT_EXT: return kCAGravityTopRight;
|
||||
default: return kCAGravityTop;
|
||||
}
|
||||
case VK_PRESENT_GRAVITY_CENTERED_BIT_EXT:
|
||||
switch (pScalingInfo->presentGravityX) {
|
||||
case VK_PRESENT_GRAVITY_MIN_BIT_EXT: return kCAGravityLeft;
|
||||
case VK_PRESENT_GRAVITY_CENTERED_BIT_EXT: return kCAGravityCenter;
|
||||
case VK_PRESENT_GRAVITY_MAX_BIT_EXT: return kCAGravityRight;
|
||||
default: return kCAGravityCenter;
|
||||
}
|
||||
case VK_PRESENT_GRAVITY_MAX_BIT_EXT:
|
||||
switch (pScalingInfo->presentGravityX) {
|
||||
case VK_PRESENT_GRAVITY_MIN_BIT_EXT: return kCAGravityBottomLeft;
|
||||
case VK_PRESENT_GRAVITY_CENTERED_BIT_EXT: return kCAGravityBottom;
|
||||
case VK_PRESENT_GRAVITY_MAX_BIT_EXT: return kCAGravityBottomRight;
|
||||
default: return kCAGravityBottom;
|
||||
}
|
||||
default: return kCAGravityCenter;
|
||||
}
|
||||
default: return kCAGravityResize;
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes the CAMetalLayer underlying the surface of this swapchain.
|
||||
void MVKSwapchain::initCAMetalLayer(const VkSwapchainCreateInfoKHR* pCreateInfo, uint32_t imgCnt) {
|
||||
void MVKSwapchain::initCAMetalLayer(const VkSwapchainCreateInfoKHR* pCreateInfo,
|
||||
VkSwapchainPresentScalingCreateInfoEXT* pScalingInfo,
|
||||
uint32_t imgCnt) {
|
||||
|
||||
MVKSurface* mvkSrfc = (MVKSurface*)pCreateInfo->surface;
|
||||
_mtlLayer = mvkSrfc->getCAMetalLayer();
|
||||
@ -262,15 +339,23 @@ void MVKSwapchain::initCAMetalLayer(const VkSwapchainCreateInfoKHR* pCreateInfo,
|
||||
return;
|
||||
}
|
||||
|
||||
auto minMagFilter = mvkConfig().swapchainMinMagFilterUseNearest ? kCAFilterNearest : kCAFilterLinear;
|
||||
_mtlLayer.device = getMTLDevice();
|
||||
_mtlLayer.pixelFormat = getPixelFormats()->getMTLPixelFormat(pCreateInfo->imageFormat);
|
||||
_mtlLayer.maximumDrawableCountMVK = imgCnt;
|
||||
_mtlLayer.displaySyncEnabledMVK = (pCreateInfo->presentMode != VK_PRESENT_MODE_IMMEDIATE_KHR);
|
||||
_mtlLayer.magnificationFilter = mvkConfig().swapchainMagFilterUseNearest ? kCAFilterNearest : kCAFilterLinear;
|
||||
_mtlLayer.minificationFilter = minMagFilter;
|
||||
_mtlLayer.magnificationFilter = minMagFilter;
|
||||
_mtlLayer.contentsGravity = getCALayerContentsGravity(pScalingInfo);
|
||||
_mtlLayer.framebufferOnly = !mvkIsAnyFlagEnabled(pCreateInfo->imageUsage, (VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
VK_IMAGE_USAGE_STORAGE_BIT));
|
||||
// Remember the extent to later detect if it has changed under the covers,
|
||||
// and set the drawable size of the CAMetalLayer from the extent.
|
||||
_mtlLayerDrawableExtent = pCreateInfo->imageExtent;
|
||||
_mtlLayer.drawableSize = mvkCGSizeFromVkExtent2D(_mtlLayerDrawableExtent);
|
||||
|
||||
if (pCreateInfo->compositeAlpha != VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) {
|
||||
_mtlLayer.opaque = pCreateInfo->compositeAlpha == VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
}
|
||||
@ -330,7 +415,6 @@ void MVKSwapchain::initCAMetalLayer(const VkSwapchainCreateInfoKHR* pCreateInfo,
|
||||
setConfigurationResult(reportError(VK_ERROR_FORMAT_NOT_SUPPORTED, "vkCreateSwapchainKHR(): Metal does not support VkColorSpaceKHR value %d.", pCreateInfo->imageColorSpace));
|
||||
break;
|
||||
}
|
||||
_mtlLayer.drawableSize = mvkCGSizeFromVkExtent2D(pCreateInfo->imageExtent);
|
||||
|
||||
// TODO: set additional CAMetalLayer properties before extracting drawables:
|
||||
// - presentsWithTransaction
|
||||
@ -398,8 +482,12 @@ void MVKSwapchain::initSurfaceImages(const VkSwapchainCreateInfoKHR* pCreateInfo
|
||||
mvkEnableFlags(imgInfo.flags, VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT);
|
||||
}
|
||||
|
||||
// The VK_SWAPCHAIN_CREATE_DEFERRED_MEMORY_ALLOCATION_BIT_EXT flag is ignored, because
|
||||
// swapchain image memory allocation is provided by a MTLDrawable, which is retrieved
|
||||
// lazily, and hence is already deferred (or as deferred as we can make it).
|
||||
|
||||
for (uint32_t imgIdx = 0; imgIdx < imgCnt; imgIdx++) {
|
||||
_presentableImages.push_back(_device->createPresentableSwapchainImage(&imgInfo, this, imgIdx, NULL));
|
||||
_presentableImages.push_back(_device->createPresentableSwapchainImage(&imgInfo, this, imgIdx, nullptr));
|
||||
}
|
||||
|
||||
NSString* screenName = @"Main Screen";
|
||||
@ -469,7 +557,7 @@ VkResult MVKSwapchain::getPastPresentationTiming(uint32_t *pCount, VkPastPresent
|
||||
return res;
|
||||
}
|
||||
|
||||
void MVKSwapchain::recordPresentTime(MVKPresentTimingInfo presentTimingInfo, uint64_t actualPresentTime) {
|
||||
void MVKSwapchain::recordPresentTime(MVKImagePresentInfo& presentInfo, uint64_t actualPresentTime) {
|
||||
std::lock_guard<std::mutex> lock(_presentHistoryLock);
|
||||
if (_presentHistoryCount < kMaxPresentationHistory) {
|
||||
_presentHistoryCount++;
|
||||
@ -478,10 +566,10 @@ void MVKSwapchain::recordPresentTime(MVKPresentTimingInfo presentTimingInfo, uin
|
||||
}
|
||||
|
||||
// If actual time not supplied, use desired time instead
|
||||
if (actualPresentTime == 0) { actualPresentTime = presentTimingInfo.desiredPresentTime; }
|
||||
if (actualPresentTime == 0) { actualPresentTime = presentInfo.desiredPresentTime; }
|
||||
|
||||
_presentTimingHistory[_presentHistoryIndex].presentID = presentTimingInfo.presentID;
|
||||
_presentTimingHistory[_presentHistoryIndex].desiredPresentTime = presentTimingInfo.desiredPresentTime;
|
||||
_presentTimingHistory[_presentHistoryIndex].presentID = presentInfo.presentID;
|
||||
_presentTimingHistory[_presentHistoryIndex].desiredPresentTime = presentInfo.desiredPresentTime;
|
||||
_presentTimingHistory[_presentHistoryIndex].actualPresentTime = actualPresentTime;
|
||||
// These details are not available in Metal
|
||||
_presentTimingHistory[_presentHistoryIndex].earliestPresentTime = actualPresentTime;
|
||||
|
@ -114,7 +114,9 @@ MVK_EXTENSION(EXT_separate_stencil_usage, EXT_SEPARATE_STENCIL_USAGE,
|
||||
MVK_EXTENSION(EXT_shader_stencil_export, EXT_SHADER_STENCIL_EXPORT, DEVICE, 10.14, 12.0)
|
||||
MVK_EXTENSION(EXT_shader_viewport_index_layer, EXT_SHADER_VIEWPORT_INDEX_LAYER, DEVICE, 10.11, 8.0)
|
||||
MVK_EXTENSION(EXT_subgroup_size_control, EXT_SUBGROUP_SIZE_CONTROL, DEVICE, 10.14, 13.0)
|
||||
MVK_EXTENSION(EXT_surface_maintenance1, EXT_SURFACE_MAINTENANCE_1, INSTANCE, 10.11, 8.0)
|
||||
MVK_EXTENSION(EXT_swapchain_colorspace, EXT_SWAPCHAIN_COLOR_SPACE, INSTANCE, 10.11, 9.0)
|
||||
MVK_EXTENSION(EXT_swapchain_maintenance1, EXT_SWAPCHAIN_MAINTENANCE_1, DEVICE, 10.11, 8.0)
|
||||
MVK_EXTENSION(EXT_texel_buffer_alignment, EXT_TEXEL_BUFFER_ALIGNMENT, DEVICE, 10.13, 11.0)
|
||||
MVK_EXTENSION(EXT_texture_compression_astc_hdr, EXT_TEXTURE_COMPRESSION_ASTC_HDR, DEVICE, 11.0, 13.0)
|
||||
MVK_EXTENSION(EXT_vertex_attribute_divisor, EXT_VERTEX_ATTRIBUTE_DIVISOR, DEVICE, 10.11, 8.0)
|
||||
|
@ -38,20 +38,11 @@
|
||||
/**
|
||||
* Returns the natural drawable size for this layer.
|
||||
*
|
||||
* The natural drawable size is the size of the bounds property of this layer, multiplied
|
||||
* by the contentsScale property of this layer, and is the value that the drawableSize
|
||||
* property will be set to when the updatedDrawableSizeMVK nethod is invoked.
|
||||
* The natural drawable size is the size of the bounds
|
||||
* property multiplied by the contentsScale property.
|
||||
*/
|
||||
@property(nonatomic, readonly) CGSize naturalDrawableSizeMVK;
|
||||
|
||||
/**
|
||||
* Ensures the drawableSize property of this layer is up to date, by ensuring
|
||||
* it is set to the value returned by the naturalDrawableSizeMVK property.
|
||||
*
|
||||
* Returns the updated drawableSize value.
|
||||
*/
|
||||
-(CGSize) updatedDrawableSizeMVK;
|
||||
|
||||
/**
|
||||
* Replacement for the displaySyncEnabled property.
|
||||
*
|
||||
|
@ -31,18 +31,8 @@
|
||||
-(CGSize) naturalDrawableSizeMVK {
|
||||
CGSize drawSize = self.bounds.size;
|
||||
CGFloat scaleFactor = self.contentsScale;
|
||||
drawSize.width = trunc(drawSize.width * scaleFactor);
|
||||
drawSize.height = trunc(drawSize.height * scaleFactor);
|
||||
return drawSize;
|
||||
}
|
||||
|
||||
// Only update drawableSize property value if it needs to be,
|
||||
// in case updating to same value causes internal reconfigurations.
|
||||
-(CGSize) updatedDrawableSizeMVK {
|
||||
CGSize drawSize = self.naturalDrawableSizeMVK;
|
||||
if ( !CGSizeEqualToSize(drawSize, self.drawableSize) ) {
|
||||
self.drawableSize = drawSize;
|
||||
}
|
||||
drawSize.width *= scaleFactor;
|
||||
drawSize.height *= scaleFactor;
|
||||
return drawSize;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,8 @@ static void mvkInitConfigFromEnvVars() {
|
||||
MVK_SET_FROM_ENV_OR_BUILD_INT32 (evCfg.maxActiveMetalCommandBuffersPerQueue, MVK_CONFIG_MAX_ACTIVE_METAL_COMMAND_BUFFERS_PER_QUEUE);
|
||||
MVK_SET_FROM_ENV_OR_BUILD_BOOL (evCfg.supportLargeQueryPools, MVK_CONFIG_SUPPORT_LARGE_QUERY_POOLS);
|
||||
MVK_SET_FROM_ENV_OR_BUILD_BOOL (evCfg.presentWithCommandBuffer, MVK_CONFIG_PRESENT_WITH_COMMAND_BUFFER);
|
||||
MVK_SET_FROM_ENV_OR_BUILD_BOOL (evCfg.swapchainMagFilterUseNearest, MVK_CONFIG_SWAPCHAIN_MAG_FILTER_USE_NEAREST);
|
||||
MVK_SET_FROM_ENV_OR_BUILD_BOOL (evCfg.swapchainMinMagFilterUseNearest, MVK_CONFIG_SWAPCHAIN_MAG_FILTER_USE_NEAREST); // Deprecated legacy env var
|
||||
MVK_SET_FROM_ENV_OR_BUILD_BOOL (evCfg.swapchainMinMagFilterUseNearest, MVK_CONFIG_SWAPCHAIN_MIN_MAG_FILTER_USE_NEAREST);
|
||||
MVK_SET_FROM_ENV_OR_BUILD_INT64 (evCfg.metalCompileTimeout, MVK_CONFIG_METAL_COMPILE_TIMEOUT);
|
||||
MVK_SET_FROM_ENV_OR_BUILD_BOOL (evCfg.performanceTracking, MVK_CONFIG_PERFORMANCE_TRACKING);
|
||||
MVK_SET_FROM_ENV_OR_BUILD_INT32 (evCfg.performanceLoggingFrameCount, MVK_CONFIG_PERFORMANCE_LOGGING_FRAME_COUNT);
|
||||
|
@ -128,9 +128,12 @@ void mvkSetConfig(const MVKConfiguration& mvkConfig);
|
||||
# define MVK_CONFIG_PRESENT_WITH_COMMAND_BUFFER 1
|
||||
#endif
|
||||
|
||||
/** Use nearest sampling to magnify swapchain images. Enabled by default. */
|
||||
/** Use nearest sampling to minify or magnify swapchain images. Enabled by default. Second macro name is deprecated legacy. */
|
||||
#ifndef MVK_CONFIG_SWAPCHAIN_MIN_MAG_FILTER_USE_NEAREST
|
||||
# define MVK_CONFIG_SWAPCHAIN_MIN_MAG_FILTER_USE_NEAREST 1
|
||||
#endif
|
||||
#ifndef MVK_CONFIG_SWAPCHAIN_MAG_FILTER_USE_NEAREST
|
||||
# define MVK_CONFIG_SWAPCHAIN_MAG_FILTER_USE_NEAREST 1
|
||||
# define MVK_CONFIG_SWAPCHAIN_MAG_FILTER_USE_NEAREST MVK_CONFIG_SWAPCHAIN_MIN_MAG_FILTER_USE_NEAREST
|
||||
#endif
|
||||
|
||||
/** The maximum amount of time, in nanoseconds, to wait for a Metal library. Default is infinite. */
|
||||
|
@ -24,49 +24,57 @@
|
||||
const char* mvkVkResultName(VkResult vkResult) {
|
||||
switch (vkResult) {
|
||||
|
||||
CASE_STRINGIFY(VK_SUCCESS);
|
||||
CASE_STRINGIFY(VK_NOT_READY);
|
||||
CASE_STRINGIFY(VK_TIMEOUT);
|
||||
CASE_STRINGIFY(VK_EVENT_SET);
|
||||
CASE_STRINGIFY(VK_EVENT_RESET);
|
||||
CASE_STRINGIFY(VK_INCOMPLETE);
|
||||
CASE_STRINGIFY(VK_SUCCESS);
|
||||
CASE_STRINGIFY(VK_NOT_READY);
|
||||
CASE_STRINGIFY(VK_TIMEOUT);
|
||||
CASE_STRINGIFY(VK_EVENT_SET);
|
||||
CASE_STRINGIFY(VK_EVENT_RESET);
|
||||
CASE_STRINGIFY(VK_INCOMPLETE);
|
||||
|
||||
CASE_STRINGIFY(VK_ERROR_OUT_OF_HOST_MEMORY);
|
||||
CASE_STRINGIFY(VK_ERROR_OUT_OF_DEVICE_MEMORY);
|
||||
CASE_STRINGIFY(VK_ERROR_INITIALIZATION_FAILED);
|
||||
CASE_STRINGIFY(VK_ERROR_DEVICE_LOST);
|
||||
CASE_STRINGIFY(VK_ERROR_MEMORY_MAP_FAILED);
|
||||
CASE_STRINGIFY(VK_ERROR_LAYER_NOT_PRESENT);
|
||||
CASE_STRINGIFY(VK_ERROR_EXTENSION_NOT_PRESENT);
|
||||
CASE_STRINGIFY(VK_ERROR_FEATURE_NOT_PRESENT);
|
||||
CASE_STRINGIFY(VK_ERROR_INCOMPATIBLE_DRIVER);
|
||||
CASE_STRINGIFY(VK_ERROR_TOO_MANY_OBJECTS);
|
||||
CASE_STRINGIFY(VK_ERROR_FORMAT_NOT_SUPPORTED);
|
||||
CASE_STRINGIFY(VK_ERROR_FRAGMENTED_POOL);
|
||||
CASE_STRINGIFY(VK_ERROR_OUT_OF_HOST_MEMORY);
|
||||
CASE_STRINGIFY(VK_ERROR_OUT_OF_DEVICE_MEMORY);
|
||||
CASE_STRINGIFY(VK_ERROR_INITIALIZATION_FAILED);
|
||||
CASE_STRINGIFY(VK_ERROR_DEVICE_LOST);
|
||||
CASE_STRINGIFY(VK_ERROR_MEMORY_MAP_FAILED);
|
||||
CASE_STRINGIFY(VK_ERROR_LAYER_NOT_PRESENT);
|
||||
CASE_STRINGIFY(VK_ERROR_EXTENSION_NOT_PRESENT);
|
||||
CASE_STRINGIFY(VK_ERROR_FEATURE_NOT_PRESENT);
|
||||
CASE_STRINGIFY(VK_ERROR_INCOMPATIBLE_DRIVER);
|
||||
CASE_STRINGIFY(VK_ERROR_TOO_MANY_OBJECTS);
|
||||
CASE_STRINGIFY(VK_ERROR_FORMAT_NOT_SUPPORTED);
|
||||
CASE_STRINGIFY(VK_ERROR_FRAGMENTED_POOL);
|
||||
|
||||
CASE_STRINGIFY(VK_ERROR_UNKNOWN);
|
||||
CASE_STRINGIFY(VK_ERROR_OUT_OF_POOL_MEMORY);
|
||||
CASE_STRINGIFY(VK_ERROR_INVALID_EXTERNAL_HANDLE);
|
||||
CASE_STRINGIFY(VK_ERROR_FRAGMENTATION);
|
||||
CASE_STRINGIFY(VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS);
|
||||
CASE_STRINGIFY(VK_ERROR_UNKNOWN);
|
||||
CASE_STRINGIFY(VK_ERROR_OUT_OF_POOL_MEMORY);
|
||||
CASE_STRINGIFY(VK_ERROR_INVALID_EXTERNAL_HANDLE);
|
||||
CASE_STRINGIFY(VK_ERROR_FRAGMENTATION);
|
||||
CASE_STRINGIFY(VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS);
|
||||
CASE_STRINGIFY(VK_PIPELINE_COMPILE_REQUIRED);
|
||||
|
||||
CASE_STRINGIFY(VK_ERROR_SURFACE_LOST_KHR);
|
||||
CASE_STRINGIFY(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR);
|
||||
CASE_STRINGIFY(VK_SUBOPTIMAL_KHR);
|
||||
CASE_STRINGIFY(VK_ERROR_OUT_OF_DATE_KHR);
|
||||
CASE_STRINGIFY(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR);
|
||||
CASE_STRINGIFY(VK_ERROR_SURFACE_LOST_KHR);
|
||||
CASE_STRINGIFY(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR);
|
||||
CASE_STRINGIFY(VK_SUBOPTIMAL_KHR);
|
||||
CASE_STRINGIFY(VK_ERROR_OUT_OF_DATE_KHR);
|
||||
CASE_STRINGIFY(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR);
|
||||
|
||||
CASE_STRINGIFY(VK_ERROR_VALIDATION_FAILED_EXT);
|
||||
CASE_STRINGIFY(VK_ERROR_INVALID_SHADER_NV);
|
||||
CASE_STRINGIFY(VK_ERROR_VALIDATION_FAILED_EXT);
|
||||
CASE_STRINGIFY(VK_ERROR_INVALID_SHADER_NV);
|
||||
CASE_STRINGIFY(VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR);
|
||||
|
||||
CASE_STRINGIFY(VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT);
|
||||
CASE_STRINGIFY(VK_ERROR_NOT_PERMITTED_EXT);
|
||||
CASE_STRINGIFY(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT);
|
||||
CASE_STRINGIFY(VK_THREAD_IDLE_KHR);
|
||||
CASE_STRINGIFY(VK_THREAD_DONE_KHR);
|
||||
CASE_STRINGIFY(VK_OPERATION_DEFERRED_KHR);
|
||||
CASE_STRINGIFY(VK_OPERATION_NOT_DEFERRED_KHR);
|
||||
CASE_STRINGIFY(VK_PIPELINE_COMPILE_REQUIRED_EXT);
|
||||
CASE_STRINGIFY(VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR);
|
||||
CASE_STRINGIFY(VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR);
|
||||
CASE_STRINGIFY(VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR);
|
||||
CASE_STRINGIFY(VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR);
|
||||
CASE_STRINGIFY(VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR);
|
||||
|
||||
CASE_STRINGIFY(VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT);
|
||||
CASE_STRINGIFY(VK_ERROR_NOT_PERMITTED_KHR);
|
||||
CASE_STRINGIFY(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT);
|
||||
CASE_STRINGIFY(VK_THREAD_IDLE_KHR);
|
||||
CASE_STRINGIFY(VK_THREAD_DONE_KHR);
|
||||
CASE_STRINGIFY(VK_OPERATION_DEFERRED_KHR);
|
||||
CASE_STRINGIFY(VK_OPERATION_NOT_DEFERRED_KHR);
|
||||
CASE_STRINGIFY(VK_ERROR_COMPRESSION_EXHAUSTED_EXT);
|
||||
|
||||
default: return "VK_UNKNOWN_VK_Result";
|
||||
}
|
||||
|
@ -137,8 +137,7 @@ static inline std::string mvkGetMoltenVKVersionString(uint32_t mvkVersion) {
|
||||
|
||||
/** Returns whether the specified positive value is a power-of-two. */
|
||||
template<typename T>
|
||||
static inline bool mvkIsPowerOfTwo(T value) {
|
||||
// Test POT: (x != 0) && ((x & (x - 1)) == 0)
|
||||
static constexpr bool mvkIsPowerOfTwo(T value) {
|
||||
return value && ((value & (value - 1)) == 0);
|
||||
}
|
||||
|
||||
@ -148,7 +147,7 @@ static inline bool mvkIsPowerOfTwo(T value) {
|
||||
* that is larger than the specified value is returned.
|
||||
*/
|
||||
template<typename T>
|
||||
static inline T mvkEnsurePowerOfTwo(T value) {
|
||||
static constexpr T mvkEnsurePowerOfTwo(T value) {
|
||||
if (mvkIsPowerOfTwo(value)) { return value; }
|
||||
|
||||
T pot = 1;
|
||||
@ -163,7 +162,7 @@ static inline T mvkEnsurePowerOfTwo(T value) {
|
||||
* This implementation returns zero for both zero and one as inputs.
|
||||
*/
|
||||
template<typename T>
|
||||
static inline T mvkPowerOfTwoExponent(T value) {
|
||||
static constexpr T mvkPowerOfTwoExponent(T value) {
|
||||
T p2Value = mvkEnsurePowerOfTwo(value);
|
||||
|
||||
// Count the trailing zeros
|
||||
@ -184,7 +183,7 @@ static inline T mvkPowerOfTwoExponent(T value) {
|
||||
* This is a low level utility method. Usually you will use the convenience functions
|
||||
* mvkAlignAddress() and mvkAlignByteCount() to align addresses and offsets respectively.
|
||||
*/
|
||||
static inline uintptr_t mvkAlignByteRef(uintptr_t byteRef, uintptr_t byteAlignment, bool alignDown = false) {
|
||||
static constexpr uintptr_t mvkAlignByteRef(uintptr_t byteRef, uintptr_t byteAlignment, bool alignDown = false) {
|
||||
if (byteAlignment == 0) { return byteRef; }
|
||||
|
||||
assert(mvkIsPowerOfTwo(byteAlignment));
|
||||
@ -213,7 +212,7 @@ static inline void* mvkAlignAddress(void* address, uintptr_t byteAlignment, bool
|
||||
* which will be greater than or equal to the original offset if alignDown is false, or less
|
||||
* than or equal to the original offset if alignDown is true.
|
||||
*/
|
||||
static inline uintptr_t mvkAlignByteCount(uintptr_t byteCount, uintptr_t byteAlignment, bool alignDown = false) {
|
||||
static constexpr uint64_t mvkAlignByteCount(uint64_t byteCount, uint64_t byteAlignment, bool alignDown = false) {
|
||||
return mvkAlignByteRef(byteCount, byteAlignment, alignDown);
|
||||
}
|
||||
|
||||
@ -254,22 +253,22 @@ static inline VkExtent2D mvkVkExtent2DFromVkExtent3D(VkExtent3D e) { return {e.w
|
||||
static inline VkExtent3D mvkVkExtent3DFromVkExtent2D(VkExtent2D e) { return {e.width, e.height, 1U }; }
|
||||
|
||||
/** Returns whether the two Vulkan extents are equal by comparing their respective components. */
|
||||
static inline bool mvkVkExtent2DsAreEqual(VkExtent2D e1, VkExtent2D e2) {
|
||||
static constexpr bool mvkVkExtent2DsAreEqual(VkExtent2D e1, VkExtent2D e2) {
|
||||
return (e1.width == e2.width) && (e1.height == e2.height);
|
||||
}
|
||||
|
||||
/** Returns whether the two Vulkan extents are equal by comparing their respective components. */
|
||||
static inline bool mvkVkExtent3DsAreEqual(VkExtent3D e1, VkExtent3D e2) {
|
||||
static constexpr bool mvkVkExtent3DsAreEqual(VkExtent3D e1, VkExtent3D e2) {
|
||||
return (e1.width == e2.width) && (e1.height == e2.height) && (e1.depth == e2.depth);
|
||||
}
|
||||
|
||||
/** Returns whether the two Vulkan offsets are equal by comparing their respective components. */
|
||||
static inline bool mvkVkOffset2DsAreEqual(VkOffset2D os1, VkOffset2D os2) {
|
||||
static constexpr bool mvkVkOffset2DsAreEqual(VkOffset2D os1, VkOffset2D os2) {
|
||||
return (os1.x == os2.x) && (os1.y == os2.y);
|
||||
}
|
||||
|
||||
/** Returns whether the two Vulkan offsets are equal by comparing their respective components. */
|
||||
static inline bool mvkVkOffset3DsAreEqual(VkOffset3D os1, VkOffset3D os2) {
|
||||
static constexpr bool mvkVkOffset3DsAreEqual(VkOffset3D os1, VkOffset3D os2) {
|
||||
return (os1.x == os2.x) && (os1.y == os2.y) && (os1.z == os2.z);
|
||||
}
|
||||
|
||||
@ -286,9 +285,9 @@ static inline VkOffset3D mvkVkOffset3DDifference(VkOffset3D minuend, VkOffset3D
|
||||
}
|
||||
|
||||
/** Packs the four swizzle components into a single 32-bit word. */
|
||||
static inline uint32_t mvkPackSwizzle(VkComponentMapping components) {
|
||||
return ((components.r & 0xFF) << 0) | ((components.g & 0xFF) << 8) |
|
||||
((components.b & 0xFF) << 16) | ((components.a & 0xFF) << 24);
|
||||
static constexpr uint32_t mvkPackSwizzle(VkComponentMapping components) {
|
||||
return (((components.r & 0xFF) << 0) | ((components.g & 0xFF) << 8) |
|
||||
((components.b & 0xFF) << 16) | ((components.a & 0xFF) << 24));
|
||||
}
|
||||
|
||||
/** Unpacks a single 32-bit word containing four swizzle components. */
|
||||
@ -311,9 +310,9 @@ static inline VkComponentMapping mvkUnpackSwizzle(uint32_t packed) {
|
||||
* 3) Either cs1 or cs2 is VK_COMPONENT_SWIZZLE_MAX_ENUM, which is considered a wildcard,
|
||||
* and matches any value.
|
||||
*/
|
||||
static inline bool mvkVKComponentSwizzlesMatch(VkComponentSwizzle cs1,
|
||||
VkComponentSwizzle cs2,
|
||||
VkComponentSwizzle csPos) {
|
||||
static constexpr bool mvkVKComponentSwizzlesMatch(VkComponentSwizzle cs1,
|
||||
VkComponentSwizzle cs2,
|
||||
VkComponentSwizzle csPos) {
|
||||
return ((cs1 == cs2) ||
|
||||
((cs1 == VK_COMPONENT_SWIZZLE_IDENTITY) && (cs2 == csPos)) ||
|
||||
((cs2 == VK_COMPONENT_SWIZZLE_IDENTITY) && (cs1 == csPos)) ||
|
||||
@ -328,7 +327,7 @@ static inline bool mvkVKComponentSwizzlesMatch(VkComponentSwizzle cs1,
|
||||
* A component value of VK_COMPONENT_SWIZZLE_MAX_ENUM is considered a wildcard and matches
|
||||
* any value in the corresponding component in the other mapping.
|
||||
*/
|
||||
static inline bool mvkVkComponentMappingsMatch(VkComponentMapping cm1, VkComponentMapping cm2) {
|
||||
static constexpr bool mvkVkComponentMappingsMatch(VkComponentMapping cm1, VkComponentMapping cm2) {
|
||||
return (mvkVKComponentSwizzlesMatch(cm1.r, cm2.r, VK_COMPONENT_SWIZZLE_R) &&
|
||||
mvkVKComponentSwizzlesMatch(cm1.g, cm2.g, VK_COMPONENT_SWIZZLE_G) &&
|
||||
mvkVKComponentSwizzlesMatch(cm1.b, cm2.b, VK_COMPONENT_SWIZZLE_B) &&
|
||||
@ -339,11 +338,13 @@ static inline bool mvkVkComponentMappingsMatch(VkComponentMapping cm1, VkCompone
|
||||
#define mvkPrintSizeOf(type) printf("Size of " #type " is %lu.\n", sizeof(type))
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Template functions
|
||||
|
||||
#pragma mark Math
|
||||
|
||||
/** Rounds the value to nearest integer using half-to-even rounding. */
|
||||
static inline double mvkRoundHalfToEven(const double val) {
|
||||
return val - std::remainder(val, 1.0); // remainder() uses half-to-even rounding, and unfortunately isn't constexpr until C++23.
|
||||
}
|
||||
|
||||
/** Returns whether the value will fit inside the numeric type. */
|
||||
template<typename T, typename Tval>
|
||||
const bool mvkFits(const Tval& val) {
|
||||
@ -554,7 +555,7 @@ bool mvkAreEqual(const T* pV1, const T* pV2, size_t count = 1) {
|
||||
* otherwise returns false. This functionality is different than the char version of mvkAreEqual(),
|
||||
* which works on individual chars or char arrays, not strings.
|
||||
*/
|
||||
static inline bool mvkStringsAreEqual(const char* pV1, const char* pV2, size_t count = 1) {
|
||||
static constexpr bool mvkStringsAreEqual(const char* pV1, const char* pV2, size_t count = 1) {
|
||||
return (pV1 && pV2) ? (strcmp(pV1, pV2) == 0) : false;
|
||||
}
|
||||
|
||||
@ -571,7 +572,7 @@ static inline bool mvkStringsAreEqual(const char* pV1, const char* pV2, size_t c
|
||||
* If the destination pointer is NULL, does nothing, and returns false.
|
||||
*/
|
||||
template<typename T>
|
||||
bool mvkSetOrClear(T* pDest, const T* pSrc) {
|
||||
static constexpr bool mvkSetOrClear(T* pDest, const T* pSrc) {
|
||||
if (pDest && pSrc) {
|
||||
*pDest = *pSrc;
|
||||
return true;
|
||||
@ -593,17 +594,17 @@ void mvkDisableFlags(Tv& value, const Tm bitMask) { value = (Tv)(value & ~(Tv)bi
|
||||
|
||||
/** Returns whether the specified value has ANY of the flags specified in bitMask enabled (set to 1). */
|
||||
template<typename Tv, typename Tm>
|
||||
bool mvkIsAnyFlagEnabled(Tv value, const Tm bitMask) { return ((value & bitMask) != 0); }
|
||||
static constexpr bool mvkIsAnyFlagEnabled(Tv value, const Tm bitMask) { return ((value & bitMask) != 0); }
|
||||
|
||||
/** Returns whether the specified value has ALL of the flags specified in bitMask enabled (set to 1). */
|
||||
template<typename Tv, typename Tm>
|
||||
bool mvkAreAllFlagsEnabled(Tv value, const Tm bitMask) { return ((value & bitMask) == bitMask); }
|
||||
static constexpr bool mvkAreAllFlagsEnabled(Tv value, const Tm bitMask) { return ((value & bitMask) == bitMask); }
|
||||
|
||||
/** Returns whether the specified value has ONLY one or more of the flags specified in bitMask enabled (set to 1), and none others. */
|
||||
template<typename Tv, typename Tm>
|
||||
bool mvkIsOnlyAnyFlagEnabled(Tv value, const Tm bitMask) { return (mvkIsAnyFlagEnabled(value, bitMask) && ((value | bitMask) == bitMask)); }
|
||||
static constexpr bool mvkIsOnlyAnyFlagEnabled(Tv value, const Tm bitMask) { return (mvkIsAnyFlagEnabled(value, bitMask) && ((value | bitMask) == bitMask)); }
|
||||
|
||||
/** Returns whether the specified value has ONLY ALL of the flags specified in bitMask enabled (set to 1), and none others. */
|
||||
template<typename Tv, typename Tm>
|
||||
bool mvkAreOnlyAllFlagsEnabled(Tv value, const Tm bitMask) { return (value == bitMask); }
|
||||
static constexpr bool mvkAreOnlyAllFlagsEnabled(Tv value, const Tm bitMask) { return (value == bitMask); }
|
||||
|
||||
|
@ -207,9 +207,6 @@ do { \
|
||||
# define MVK_DEBUGGER() { kill( getpid(), SIGINT ) ; }
|
||||
#endif
|
||||
|
||||
// Log the size of a type, struct, or class
|
||||
#define MVKLogSizeOf(T) printf("sizeof(%s): %lu.\n", #T, sizeof(T))
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -730,6 +730,24 @@ MVK_PUBLIC_SYMBOL MTLBarrierScope mvkMTLBarrierScopeFromVkAccessFlags(VkAccessFl
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Geometry conversions
|
||||
|
||||
MVK_PUBLIC_SYMBOL VkExtent2D mvkVkExtent2DFromCGSize(CGSize cgSize) {
|
||||
VkExtent2D vkExt;
|
||||
vkExt.width = mvkRoundHalfToEven(cgSize.width);
|
||||
vkExt.height = mvkRoundHalfToEven(cgSize.height);
|
||||
return vkExt;
|
||||
}
|
||||
|
||||
MVK_PUBLIC_SYMBOL CGSize mvkCGSizeFromVkExtent2D(VkExtent2D vkExtent) {
|
||||
CGSize cgSize;
|
||||
cgSize.width = vkExtent.width;
|
||||
cgSize.height = vkExtent.height;
|
||||
return cgSize;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Memory options
|
||||
|
||||
|
@ -2795,7 +2795,7 @@ MVK_PUBLIC_VULKAN_SYMBOL VkResult vkAcquireNextImageKHR(
|
||||
|
||||
MVKTraceVulkanCallStart();
|
||||
MVKSwapchain* mvkSwapchain = (MVKSwapchain*)swapchain;
|
||||
VkResult rslt = mvkSwapchain->acquireNextImageKHR(timeout, semaphore, fence, ~0u, pImageIndex);
|
||||
VkResult rslt = mvkSwapchain->acquireNextImage(timeout, semaphore, fence, ~0u, pImageIndex);
|
||||
MVKTraceVulkanCallEnd();
|
||||
return rslt;
|
||||
}
|
||||
@ -2856,11 +2856,25 @@ MVK_PUBLIC_VULKAN_SYMBOL VkResult vkAcquireNextImage2KHR(
|
||||
|
||||
MVKTraceVulkanCallStart();
|
||||
MVKSwapchain* mvkSwapchain = (MVKSwapchain*)pAcquireInfo->swapchain;
|
||||
VkResult rslt = mvkSwapchain->acquireNextImageKHR(pAcquireInfo->timeout,
|
||||
pAcquireInfo->semaphore,
|
||||
pAcquireInfo->fence,
|
||||
pAcquireInfo->deviceMask,
|
||||
pImageIndex);
|
||||
VkResult rslt = mvkSwapchain->acquireNextImage(pAcquireInfo->timeout,
|
||||
pAcquireInfo->semaphore,
|
||||
pAcquireInfo->fence,
|
||||
pAcquireInfo->deviceMask,
|
||||
pImageIndex);
|
||||
MVKTraceVulkanCallEnd();
|
||||
return rslt;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark VK_EXT_swapchain_maintenance1 extension
|
||||
|
||||
MVK_PUBLIC_VULKAN_SYMBOL VkResult vkReleaseSwapchainImagesEXT(
|
||||
VkDevice device,
|
||||
const VkReleaseSwapchainImagesInfoEXT* pReleaseInfo) {
|
||||
|
||||
MVKTraceVulkanCallStart();
|
||||
MVKSwapchain* mvkSwapchain = (MVKSwapchain*)pReleaseInfo->swapchain;
|
||||
VkResult rslt = mvkSwapchain->releaseImages(pReleaseInfo);
|
||||
MVKTraceVulkanCallEnd();
|
||||
return rslt;
|
||||
}
|
||||
@ -2901,8 +2915,7 @@ MVK_PUBLIC_VULKAN_SYMBOL VkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
|
||||
|
||||
MVKTraceVulkanCallStart();
|
||||
MVKPhysicalDevice* mvkPD = MVKPhysicalDevice::getMVKPhysicalDevice(physicalDevice);
|
||||
MVKSurface* mvkSrfc = (MVKSurface*)surface;
|
||||
VkResult rslt = mvkPD->getSurfaceCapabilities(mvkSrfc, pSurfaceCapabilities);
|
||||
VkResult rslt = mvkPD->getSurfaceCapabilities(surface, pSurfaceCapabilities);
|
||||
MVKTraceVulkanCallEnd();
|
||||
return rslt;
|
||||
}
|
||||
@ -2946,8 +2959,7 @@ MVK_PUBLIC_VULKAN_SYMBOL VkResult vkGetPhysicalDeviceSurfaceCapabilities2KHR(
|
||||
|
||||
MVKTraceVulkanCallStart();
|
||||
MVKPhysicalDevice* mvkPD = MVKPhysicalDevice::getMVKPhysicalDevice(physicalDevice);
|
||||
MVKSurface* mvkSrfc = (MVKSurface*)pSurfaceInfo->surface;
|
||||
VkResult rslt = mvkPD->getSurfaceCapabilities(mvkSrfc, &pSurfaceCapabilities->surfaceCapabilities);
|
||||
VkResult rslt = mvkPD->getSurfaceCapabilities(pSurfaceInfo, pSurfaceCapabilities);
|
||||
MVKTraceVulkanCallEnd();
|
||||
return rslt;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user