Add support for VK_EXT_swapchain_maintenance1 and VK_EXT_surface_maintenance1.
- Support querying scaling capabilities and present mode compatibilies when querying surface capabilities. - Rename MVKPresentTimingInfo to MVKImagePresentInfo and add present mode and fence to support dynamic present mode changes and fence signaling. - MVKPresentableSwapchainImage remove static functions from class declaration. - MVKSwapchain support releasing swapchain images on command. - MVKSwapchain support configuring with scaling and gravity info, apply it to CAMetalLayer.and do not return VK_SUBOPTIMAL_KHR if swapchain was configured with scaling info. - Rename MVKSwapchain::acquireNextImageKHR to acquireNextImage. - CAMetalLayer naturalDrawableSizeMVK compute precise drawable size. - CAMetalLayer remove obsolete and unused updatedDrawableSizeMVK method. - Rename MVKConfiguration::swapchainMagFilterUseNearest to swapchainMinMagFilterUseNearest to apply CAMetalLayer size filtering to both magnification and minification, and rename corresponding env var MVK_CONFIG_SWAPCHAIN_MAG_FILTER_USE_NEAREST to MVK_CONFIG_SWAPCHAIN_MIN_MAG_FILTER_USE_NEAREST. - 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. - Remove MVKLogSizeOf() logging macro as redundant to mvkPrintSizeOf() macro.
This commit is contained in:
parent
e8885a24e8
commit
fabad21405
@ -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,9 +18,13 @@ 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.
|
||||
|
||||
- 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.
|
||||
|
||||
|
||||
MoltenVK 1.2.2
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
@ -540,6 +544,7 @@ public:
|
||||
MVKPresentableSwapchainImage* createPresentableSwapchainImage(const VkImageCreateInfo* pCreateInfo,
|
||||
MVKSwapchain* swapchain,
|
||||
uint32_t swapchainIndex,
|
||||
bool deferImgMemAlloc,
|
||||
const VkAllocationCallbacks* pAllocator);
|
||||
void destroyPresentableSwapchainImage(MVKPresentableSwapchainImage* mvkImg,
|
||||
const VkAllocationCallbacks* pAllocator);
|
||||
|
@ -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 = mvkVkExtent2DFromCGSize(mtlLayer.naturalDrawableSizeMVK);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -3458,8 +3570,11 @@ void MVKDevice::destroySwapchain(MVKSwapchain* mvkSwpChn,
|
||||
MVKPresentableSwapchainImage* MVKDevice::createPresentableSwapchainImage(const VkImageCreateInfo* pCreateInfo,
|
||||
MVKSwapchain* swapchain,
|
||||
uint32_t swapchainIndex,
|
||||
bool deferImgMemAlloc,
|
||||
const VkAllocationCallbacks* pAllocator) {
|
||||
MVKPresentableSwapchainImage* mvkImg = new MVKPresentableSwapchainImage(this, pCreateInfo, swapchain, swapchainIndex);
|
||||
MVKPresentableSwapchainImage* mvkImg = new MVKPresentableSwapchainImage(this, pCreateInfo,
|
||||
swapchain, swapchainIndex,
|
||||
deferImgMemAlloc);
|
||||
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,8 +449,7 @@ 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
|
||||
@ -456,7 +457,8 @@ public:
|
||||
MVKPresentableSwapchainImage(MVKDevice* device,
|
||||
const VkImageCreateInfo* pCreateInfo,
|
||||
MVKSwapchain* swapchain,
|
||||
uint32_t swapchainIndex);
|
||||
uint32_t swapchainIndex,
|
||||
bool deferImgMemAlloc);
|
||||
|
||||
~MVKPresentableSwapchainImage() override;
|
||||
|
||||
@ -464,16 +466,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();
|
||||
@ -1365,13 +1346,14 @@ void MVKPresentableSwapchainImage::presentCAMetalDrawable(id<MTLCommandBuffer> m
|
||||
[mtlDrwbl release];
|
||||
makeAvailable(signaler);
|
||||
release();
|
||||
if (presentInfo.fence) { presentInfo.fence->signal(); }
|
||||
}];
|
||||
|
||||
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 +1361,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 +1374,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,13 +1386,39 @@ 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
|
||||
|
||||
// The deferImgMemAlloc parameter is ignored, because image memory allocation is provided by a MTLDrawable,
|
||||
// which is retrieved lazily, and hence is already as deferred (or as deferred as we can make it).
|
||||
MVKPresentableSwapchainImage::MVKPresentableSwapchainImage(MVKDevice* device,
|
||||
const VkImageCreateInfo* pCreateInfo,
|
||||
MVKSwapchain* swapchain,
|
||||
uint32_t swapchainIndex) :
|
||||
uint32_t swapchainIndex,
|
||||
bool deferImgMemAlloc) :
|
||||
MVKSwapchainImage(device, pCreateInfo, swapchain, swapchainIndex) {
|
||||
|
||||
_mtlDrawable = nil;
|
||||
@ -1421,10 +1429,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() {
|
||||
|
@ -272,6 +272,6 @@ public:
|
||||
protected:
|
||||
void stopAutoGPUCapture();
|
||||
|
||||
MVKSmallVector<MVKPresentTimingInfo, 4> _presentInfo;
|
||||
MVKSmallVector<MVKImagePresentInfo, 4> _presentInfo;
|
||||
};
|
||||
|
||||
|
@ -538,8 +538,7 @@ void MVKQueuePresentSurfaceSubmission::execute() {
|
||||
[mtlCmdBuff enqueue];
|
||||
for (auto& ws : _waitSemaphores) { ws.first->encodeWait(mtlCmdBuff, 0); }
|
||||
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];
|
||||
@ -564,13 +563,19 @@ MVKQueuePresentSurfaceSubmission::MVKQueuePresentSurfaceSubmission(MVKQueue* que
|
||||
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 +584,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;
|
||||
}
|
||||
|
@ -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,38 @@ 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.
|
||||
* It is 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 hasOptimalSurface() {
|
||||
if (_isDeliberatelyScaled) { return true; }
|
||||
|
||||
auto drawSize = _mtlLayer.drawableSize;
|
||||
return (CGSizeEqualToSize(drawSize, _mtlLayerDrawableSize) &&
|
||||
CGSizeEqualToSize(drawSize, _mtlLayer.naturalDrawableSizeMVK));
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
||||
@ -97,7 +109,7 @@ public:
|
||||
|
||||
/** VK_GOOGLE_display_timing - returns past presentation times */
|
||||
VkResult getPastPresentationTiming(uint32_t *pCount, VkPastPresentationTimingGOOGLE *pPresentationTimings);
|
||||
|
||||
|
||||
void destroy() override;
|
||||
|
||||
#pragma mark Construction
|
||||
@ -110,7 +122,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 +132,25 @@ 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 = nil;
|
||||
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;
|
||||
CGSize _mtlLayerDrawableSize = {0.0, 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;
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
@ -227,19 +235,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 +273,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 +327,22 @@ 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 drawable size to later detect if it has changed under the covers.
|
||||
_mtlLayerDrawableSize = mvkCGSizeFromVkExtent2D(pCreateInfo->imageExtent);
|
||||
_mtlLayer.drawableSize = _mtlLayerDrawableSize;
|
||||
|
||||
if (pCreateInfo->compositeAlpha != VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) {
|
||||
_mtlLayer.opaque = pCreateInfo->compositeAlpha == VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
}
|
||||
@ -330,7 +402,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 +469,11 @@ void MVKSwapchain::initSurfaceImages(const VkSwapchainCreateInfoKHR* pCreateInfo
|
||||
mvkEnableFlags(imgInfo.flags, VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT);
|
||||
}
|
||||
|
||||
bool deferImgMemAlloc = mvkAreAllFlagsEnabled(pCreateInfo->flags, VK_SWAPCHAIN_CREATE_DEFERRED_MEMORY_ALLOCATION_BIT_EXT);
|
||||
|
||||
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,
|
||||
deferImgMemAlloc, NULL));
|
||||
}
|
||||
|
||||
NSString* screenName = @"Main Screen";
|
||||
@ -469,7 +543,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 +552,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 of this layer,
|
||||
* multiplied by the contentsScale property of this layer.
|
||||
*/
|
||||
@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. */
|
||||
|
@ -339,11 +339,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
|
||||
}
|
||||
|
||||
/** Returns whether the value will fit inside the numeric type. */
|
||||
template<typename T, typename Tval>
|
||||
const bool mvkFits(const Tval& val) {
|
||||
|
@ -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