JBR-8608 Vulkan: Cleanup capability checks

(cherry picked from commit 305cf4b2526dc6189e6715e7780ec9d8be44d1c6)
This commit is contained in:
Nikita Gubarkov
2025-04-11 19:23:49 +02:00
committed by Alexey Ushakov
parent df366c74c1
commit 52b213d96c
9 changed files with 258 additions and 197 deletions

View File

@@ -546,7 +546,7 @@ final class VKSwToSurfaceBlit extends VKMultiplexedBlit {
}
private static boolean hasCap(VKSurfaceData dst, int cap) {
return (dst.getGraphicsConfig().getGPU().getCaps() & cap) != 0;
return dst.getGraphicsConfig().getGPU().hasCap(cap);
}
/**

View File

@@ -80,7 +80,7 @@ public final class VKEnv {
Options.deviceNumber : 0];
// Check whether the presentation is supported.
for (VKGPU device : devices) {
if ((device.getCaps() & VKGPU.CAP_PRESENTABLE_BIT) != 0 &&
if (device.hasCap(VKGPU.CAP_PRESENTABLE_BIT) &&
device.getPresentableGraphicsConfigs().findAny().isPresent()) {
newState |= PRESENT_BIT;
break;

View File

@@ -39,6 +39,7 @@ import java.util.stream.Stream;
public class VKGPU {
@Native public static final int CAP_PRESENTABLE_BIT = 0x80000000;
@Native public static final int CAP_LOGIC_OP_BIT = 0x40000000;
@Native public static final int CAP_SAMPLED_4BYTE_BIT = 0; // Considered always supported.
@Native public static final int CAP_SAMPLED_3BYTE_BIT = 1;
@Native public static final int CAP_SAMPLED_565_BIT = 2;
@@ -98,6 +99,10 @@ public class VKGPU {
return caps;
}
public boolean hasCap(int cap) {
return (caps & cap) == cap;
}
/**
* Initialize the device and return its native handle.
*/

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef VKCapabilityUtil_h_Included
#define VKCapabilityUtil_h_Included
#include "VKUtil.h"
#define VK_KHR_VALIDATION_LAYER_NAME "VK_LAYER_KHRONOS_validation"
// Named entries are layers or extensions, arragned into on-stack linked list.
typedef struct VKNamedEntry {
pchar name;
const void* found; // Pointer to the found struct.
struct VKNamedEntry* next; // Pointer to the next entry.
} VKNamedEntry;
#define DEF_NAMED_ENTRY(LIST, NAME) VKNamedEntry NAME = { NAME ## _NAME, NULL, (LIST) }; \
if (NAME.name != NULL) (LIST) = &(NAME)
static void VKNamedEntry_LogAll(pchar what, pchar all, uint32_t count, size_t stride) {
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " Supported %s:", what)
for (uint32_t i = 0; i < count; i++) {
if (i == 0) J2dRlsTrace(J2D_TRACE_VERBOSE, " ")
else J2dRlsTrace(J2D_TRACE_VERBOSE, ", ")
J2dRlsTrace(J2D_TRACE_VERBOSE, all)
all += stride;
}
J2dRlsTrace(J2D_TRACE_VERBOSE, "\n")
}
static void VKNamedEntry_LogFound(const VKNamedEntry* list) {
for (; list != NULL; list = list->next) {
J2dRlsTraceLn2(J2D_TRACE_INFO, " %s = %s", list->name, list->found ? "true" : "false")
}
}
static void VKNamedEntry_Match(VKNamedEntry* list, pchar all, uint32_t count, size_t stride) {
for (; list != NULL; list = list->next) {
pchar check = all;
for (uint32_t i = 0; i < count; i++) {
if (strcmp(list->name, check) == 0) {
list->found = check;
break;
}
check += stride;
}
}
}
static ARRAY(pchar) VKNamedEntry_CollectNames(const VKNamedEntry* list) {
ARRAY(pchar) result = NULL;
for (; list != NULL; list = list->next) {
if (list->found) ARRAY_PUSH_BACK(result) = list->name;
}
return result;
}
static void VKCapabilityUtil_LogErrors(int level, ARRAY(pchar) errors) {
for (uint32_t i = 0; i < ARRAY_SIZE(errors); i++) {
J2dRlsTraceLn1(level, " %s", errors[i])
}
}
#endif //VKCapabilityUtil_h_Included

View File

@@ -27,11 +27,14 @@
#include <string.h>
#include "sun_java2d_vulkan_VKGPU.h"
#include "VKUtil.h"
#include "VKCapabilityUtil.h"
#include "VKEnv.h"
#include "VKAllocator.h"
#include "VKRenderer.h"
#include "VKTexturePool.h"
#define CAP_PRESENTABLE_BIT sun_java2d_vulkan_VKGPU_CAP_PRESENTABLE_BIT
#if !defined(__BYTE_ORDER__) || __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define VK_LITTLE_ENDIAN
#endif
@@ -71,7 +74,6 @@ static VkBool32 VKDevice_CheckAndAddFormat(VKEnv* vk, VkPhysicalDevice physicalD
}
void VKDevice_CheckAndAdd(VKEnv* vk, VkPhysicalDevice physicalDevice) {
jint caps = 0;
// Query device properties.
VkPhysicalDeviceVulkan12Features device12Features = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
@@ -85,43 +87,58 @@ void VKDevice_CheckAndAdd(VKEnv* vk, VkPhysicalDevice physicalDevice) {
VkPhysicalDeviceProperties2 deviceProperties2 = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 };
vk->vkGetPhysicalDeviceProperties2(physicalDevice, &deviceProperties2);
// Check features.
J2dRlsTrace5(J2D_TRACE_INFO, "\t- %s (%d.%d.%d, %s) ",
// Query supported layers.
uint32_t layerCount;
VK_IF_ERROR(vk->vkEnumerateDeviceLayerProperties(physicalDevice, &layerCount, NULL)) return;
VkLayerProperties allLayers[layerCount];
VK_IF_ERROR(vk->vkEnumerateDeviceLayerProperties(physicalDevice, &layerCount, allLayers)) return;
// Query supported extensions.
uint32_t extensionCount;
VK_IF_ERROR(vk->vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &extensionCount, NULL)) return;
VkExtensionProperties allExtensions[extensionCount];
VK_IF_ERROR(vk->vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &extensionCount, allExtensions)) return;
// Check API version.
ARRAY(pchar) errors = NULL;
jint caps = 0;
J2dRlsTraceLn5(J2D_TRACE_INFO, "%s (%d.%d.%d, %s)",
(const char *) deviceProperties2.properties.deviceName,
VK_API_VERSION_MAJOR(deviceProperties2.properties.apiVersion),
VK_API_VERSION_MINOR(deviceProperties2.properties.apiVersion),
VK_API_VERSION_PATCH(deviceProperties2.properties.apiVersion),
physicalDeviceTypeString(deviceProperties2.properties.deviceType))
if (deviceProperties2.properties.apiVersion < REQUIRED_VULKAN_VERSION) {
J2dRlsTraceLn(J2D_TRACE_INFO, " - unsupported API version, skipped")
return;
ARRAY_PUSH_BACK(errors) = "Unsupported API version";
}
if (!deviceFeatures2.features.logicOp) {
J2dRlsTraceLn(J2D_TRACE_INFO, " - hasLogicOp not supported, skipped")
return;
}
if (!device12Features.timelineSemaphore) {
J2dRlsTraceLn(J2D_TRACE_INFO, " - hasTimelineSemaphore not supported, skipped")
return;
}
J2dRlsTraceLn(J2D_TRACE_INFO, "")
// Log layers and extensions.
VKNamedEntry_LogAll("device layers", allLayers[0].layerName, layerCount, sizeof(VkLayerProperties));
VKNamedEntry_LogAll("device extensions", allExtensions[0].extensionName, extensionCount, sizeof(VkExtensionProperties));
// Check layers.
VKNamedEntry* layers = NULL;
#ifdef DEBUG
DEF_NAMED_ENTRY(layers, VK_KHR_VALIDATION_LAYER);
#endif
VKNamedEntry_Match(layers, allLayers[0].layerName, layerCount, sizeof(VkLayerProperties));
// Check extensions.
VKNamedEntry* extensions = NULL;
DEF_NAMED_ENTRY(extensions, VK_KHR_SWAPCHAIN_EXTENSION);
VKNamedEntry_Match(extensions, allExtensions[0].extensionName, extensionCount, sizeof(VkExtensionProperties));
// Query queue family properties.
uint32_t queueFamilyCount = 0;
vk->vkGetPhysicalDeviceQueueFamilyProperties(
physicalDevice, &queueFamilyCount, NULL);
vk->vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, NULL);
VkQueueFamilyProperties queueFamilies[queueFamilyCount];
vk->vkGetPhysicalDeviceQueueFamilyProperties(
physicalDevice, &queueFamilyCount, queueFamilies);
vk->vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies);
// Find a queue family.
int64_t queueFamily = -1;
for (uint32_t j = 0; j < queueFamilyCount; j++) {
VkBool32 presentationSupported = VK_FALSE;
if (vk->platformData != NULL && vk->platformData->checkPresentationSupport != NULL) {
presentationSupported = vk->platformData->checkPresentationSupport(vk, physicalDevice, j);
}
VkBool32 presentationSupported = vk->presentationSupported && VK_KHR_SWAPCHAIN_EXTENSION.found &&
vk->platformData->checkPresentationSupport(vk, physicalDevice, j);
char logFlags[5] = {
queueFamilies[j].queueFlags & VK_QUEUE_GRAPHICS_BIT ? 'G' : '-',
queueFamilies[j].queueFlags & VK_QUEUE_COMPUTE_BIT ? 'C' : '-',
@@ -134,9 +151,9 @@ void VKDevice_CheckAndAdd(VKEnv* vk, VkPhysicalDevice physicalDevice) {
// TODO use compute workloads? Separate transfer-only DMA queue?
if (queueFamilies[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) { // Queue supports graphics operations.
if (!(caps & sun_java2d_vulkan_VKGPU_CAP_PRESENTABLE_BIT) && presentationSupported) {
if (!(caps & CAP_PRESENTABLE_BIT) && presentationSupported) {
// Queue supports presentation, choose it.
caps |= sun_java2d_vulkan_VKGPU_CAP_PRESENTABLE_BIT;
caps |= CAP_PRESENTABLE_BIT;
queueFamily = j;
} else if (queueFamily == -1) {
// We have chosen no queue so far, choose this for now.
@@ -144,22 +161,20 @@ void VKDevice_CheckAndAdd(VKEnv* vk, VkPhysicalDevice physicalDevice) {
}
}
}
if (queueFamily == -1) {
J2dRlsTraceLn(J2D_TRACE_INFO, " --------------------- Suitable queue not found, skipped")
return;
}
if (queueFamily == -1) ARRAY_PUSH_BACK(errors) = "Suitable queue not found";
// Query supported layers.
uint32_t layerCount;
VK_IF_ERROR(vk->vkEnumerateDeviceLayerProperties(physicalDevice, &layerCount, NULL)) return;
VkLayerProperties layers[layerCount];
VK_IF_ERROR(vk->vkEnumerateDeviceLayerProperties(physicalDevice, &layerCount, layers)) return;
// Check features.
VKNamedEntry_LogFound(layers);
VKNamedEntry_LogFound(extensions);
// Query supported extensions.
uint32_t extensionCount;
VK_IF_ERROR(vk->vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &extensionCount, NULL)) return;
VkExtensionProperties extensions[extensionCount];
VK_IF_ERROR(vk->vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &extensionCount, extensions)) return;
J2dRlsTraceLn1(J2D_TRACE_INFO, " presentable = %s", (caps & CAP_PRESENTABLE_BIT) ? "true" : "false")
if (!(caps & CAP_PRESENTABLE_BIT)) VK_KHR_SWAPCHAIN_EXTENSION.found = NULL;
J2dRlsTraceLn1(J2D_TRACE_INFO, " logicOp = %s", deviceFeatures2.features.logicOp ? "true" : "false")
if (deviceFeatures2.features.logicOp) caps |= sun_java2d_vulkan_VKGPU_CAP_LOGIC_OP_BIT;
J2dRlsTraceLn1(J2D_TRACE_INFO, " timelineSemaphore = %s", device12Features.timelineSemaphore ? "true" : "false")
if (!device12Features.timelineSemaphore) ARRAY_PUSH_BACK(errors) = "timelineSemaphore not supported";
// Query supported formats.
J2dRlsTraceLn(J2D_TRACE_INFO, " Supported device formats:")
@@ -171,7 +186,7 @@ void VKDevice_CheckAndAdd(VKEnv* vk, VkPhysicalDevice physicalDevice) {
ARRAY(jint) supportedFormats = NULL;
#define CHECK_AND_ADD_FORMAT(FORMAT) VKDevice_CheckAndAddFormat(vk, physicalDevice, &supportedFormats, FORMAT, #FORMAT)
if (CHECK_AND_ADD_FORMAT(VK_FORMAT_B8G8R8A8_UNORM) && SRCTYPE_4BYTE->format == VK_FORMAT_UNDEFINED) {
supportedFormats[0] |= sun_java2d_vulkan_VKGPU_CAP_PRESENTABLE_BIT; // TODO Check presentation support.
supportedFormats[0] |= CAP_PRESENTABLE_BIT; // TODO Check presentation support.
*SRCTYPE_4BYTE = (VKSampledSrcType) { VK_FORMAT_B8G8R8A8_UNORM, {
VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A }};
}
@@ -208,11 +223,8 @@ void VKDevice_CheckAndAdd(VKEnv* vk, VkPhysicalDevice physicalDevice) {
// Check sampled formats capabilities.
if (SRCTYPE_4BYTE->format == VK_FORMAT_UNDEFINED) {
J2dRlsTraceLn(J2D_TRACE_INFO, " - 4-byte sampled format not found, skipped")
ARRAY_FREE(supportedFormats);
return;
}
caps |= sun_java2d_vulkan_VKGPU_CAP_SAMPLED_4BYTE_BIT;
ARRAY_PUSH_BACK(errors) = "4-byte sampled format not found";
} else caps |= sun_java2d_vulkan_VKGPU_CAP_SAMPLED_4BYTE_BIT;
if (SRCTYPE_3BYTE->format != VK_FORMAT_UNDEFINED) caps |= sun_java2d_vulkan_VKGPU_CAP_SAMPLED_3BYTE_BIT;
if (SRCTYPE_565->format != VK_FORMAT_UNDEFINED) caps |= sun_java2d_vulkan_VKGPU_CAP_SAMPLED_565_BIT;
if (SRCTYPE_555->format != VK_FORMAT_UNDEFINED) caps |= sun_java2d_vulkan_VKGPU_CAP_SAMPLED_555_BIT;
@@ -222,61 +234,21 @@ void VKDevice_CheckAndAdd(VKEnv* vk, VkPhysicalDevice physicalDevice) {
vk->vkGetPhysicalDeviceFormatProperties(physicalDevice, VK_FORMAT_S8_UINT, &formatProperties);
if ((formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0) {
J2dRlsTraceLn1(J2D_TRACE_INFO, " %s", "VK_FORMAT_S8_UINT (stencil)")
} else {
J2dRlsTraceLn(J2D_TRACE_INFO, " - VK_FORMAT_S8_UINT not supported, skipped")
ARRAY_FREE(supportedFormats);
return;
}
} else ARRAY_PUSH_BACK(errors) = "VK_FORMAT_S8_UINT not supported";
}
// Log layers.
J2dRlsTraceLn(J2D_TRACE_VERBOSE, " Supported device layers:")
for (uint32_t j = 0; j < layerCount; j++) {
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " %s", (char *) layers[j].layerName)
}
// Check extensions.
J2dRlsTraceLn(J2D_TRACE_VERBOSE, " Supported device extensions:")
VkBool32 hasSwapChain = VK_FALSE;
for (uint32_t j = 0; j < extensionCount; j++) {
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " %s", (char *) extensions[j].extensionName)
hasSwapChain = hasSwapChain || strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME, extensions[j].extensionName) == 0;
}
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "Vulkan: Found device extensions:")
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " " VK_KHR_SWAPCHAIN_EXTENSION_NAME " = %s", hasSwapChain ? "true" : "false")
if (!hasSwapChain) {
J2dRlsTraceLn(J2D_TRACE_INFO,
" --------------------- Required " VK_KHR_SWAPCHAIN_EXTENSION_NAME " not found, skipped")
ARRAY_FREE(supportedFormats);
// Check found errors.
if (errors != NULL) {
J2dRlsTraceLn(J2D_TRACE_WARNING, " Device is not supported:")
VKCapabilityUtil_LogErrors(J2D_TRACE_WARNING, errors);
ARRAY_FREE(errors);
return;
}
ARRAY(pchar) deviceEnabledLayers = NULL;
ARRAY(pchar) deviceEnabledExtensions = NULL;
ARRAY_PUSH_BACK(deviceEnabledExtensions) = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
// Check validation layer.
#ifdef DEBUG
int validationLayerNotSupported = 1;
for (uint32_t j = 0; j < layerCount; j++) {
if (strcmp("VK_LAYER_KHRONOS_validation", layers[j].layerName) == 0) {
validationLayerNotSupported = 0;
ARRAY_PUSH_BACK(deviceEnabledLayers) = "VK_LAYER_KHRONOS_validation";
break;
}
}
if (validationLayerNotSupported) {
J2dRlsTraceLn1(J2D_TRACE_INFO, " %s device layer is not supported", "VK_LAYER_KHRONOS_validation")
}
#endif
// Copy device name.
char* deviceName = strdup(deviceProperties2.properties.deviceName);
if (deviceName == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Vulkan: Cannot duplicate deviceName")
ARRAY_FREE(deviceEnabledLayers);
ARRAY_FREE(deviceEnabledExtensions);
J2dRlsTraceLn(J2D_TRACE_ERROR, " Cannot duplicate deviceName")
ARRAY_FREE(supportedFormats);
return;
}
@@ -288,8 +260,8 @@ void VKDevice_CheckAndAdd(VKEnv* vk, VkPhysicalDevice physicalDevice) {
.handle = VK_NULL_HANDLE,
.physicalDevice = physicalDevice,
.queueFamily = queueFamily,
.enabledLayers = deviceEnabledLayers,
.enabledExtensions = deviceEnabledExtensions,
.enabledLayers = VKNamedEntry_CollectNames(layers),
.enabledExtensions = VKNamedEntry_CollectNames(extensions),
.sampledSrcTypes = sampledSrcTypes,
.supportedFormats = supportedFormats,
.caps = caps
@@ -304,11 +276,11 @@ void VKDevice_Reset(VKDevice* device) {
ARRAY_FREE(device->enabledExtensions);
ARRAY_FREE(device->enabledLayers);
ARRAY_FREE(device->supportedFormats);
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKDevice_Reset(%s)", device->name);
free(device->name);
if (device->vkDestroyDevice != NULL) {
device->vkDestroyDevice(device->handle, NULL);
}
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKDevice_Reset(%s)", device->name);
}
/*
@@ -348,7 +320,7 @@ Java_sun_java2d_vulkan_VKGPU_init(JNIEnv *env, jclass jClass, jlong jDevice) {
.pQueuePriorities = &queuePriority
};
VkPhysicalDeviceFeatures features10 = { .logicOp = VK_TRUE };
VkPhysicalDeviceFeatures features10 = { .logicOp = device->caps & sun_java2d_vulkan_VKGPU_CAP_LOGIC_OP_BIT };
VkPhysicalDeviceVulkan12Features features12 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.timelineSemaphore = VK_TRUE
@@ -377,17 +349,22 @@ Java_sun_java2d_vulkan_VKGPU_init(JNIEnv *env, jclass jClass, jlong jDevice) {
VkBool32 missingAPI = JNI_FALSE;
DEVICE_FUNCTION_TABLE(CHECK_PROC_ADDR, missingAPI, vk->vkGetDeviceProcAddr, device->handle, device->)
if (device->caps & CAP_PRESENTABLE_BIT) {
SWAPCHAIN_DEVICE_FUNCTION_TABLE(CHECK_PROC_ADDR, missingAPI, vk->vkGetDeviceProcAddr, device->handle, device->)
}
if (missingAPI) {
VKDevice_Reset(device);
#define REQUIRED_API_MISSING_MESSAGE "Vulkan: Required API is missing: "
size_t size = sizeof(REQUIRED_API_MISSING_MESSAGE);
#define PFN_CALC_MISSING_NAMES_SIZE(_, NAME) if (device->NAME == NULL) size += sizeof(#NAME) + 1;
DEVICE_FUNCTION_TABLE(PFN_CALC_MISSING_NAMES_SIZE)
if (device->caps & CAP_PRESENTABLE_BIT) { SWAPCHAIN_DEVICE_FUNCTION_TABLE(PFN_CALC_MISSING_NAMES_SIZE) }
char message[size];
memcpy(message, REQUIRED_API_MISSING_MESSAGE, size = sizeof(REQUIRED_API_MISSING_MESSAGE) - 1);
#define PFN_APPEND_MISSING_NAME(_, NAME) if (device->NAME == NULL) { \
memcpy(message + size, #NAME ", ", sizeof(#NAME) + 1); size += sizeof(#NAME) + 1; }
DEVICE_FUNCTION_TABLE(PFN_APPEND_MISSING_NAME)
if (device->caps & CAP_PRESENTABLE_BIT) { SWAPCHAIN_DEVICE_FUNCTION_TABLE(PFN_APPEND_MISSING_NAME) }
message[size - 2] = '\0';
JNU_ThrowByName(env, "java/lang/RuntimeException", message);
return;

View File

@@ -61,6 +61,7 @@ struct VKDevice {
VKTexturePool* texturePool;
DEVICE_FUNCTION_TABLE(DECL_PFN)
SWAPCHAIN_DEVICE_FUNCTION_TABLE(DECL_PFN)
};
#endif //VKDevice_h_Included

View File

@@ -27,6 +27,7 @@
#include <dlfcn.h>
#include <string.h>
#include "VKUtil.h"
#include "VKCapabilityUtil.h"
#include "VKEnv.h"
#include "VKDevice.h"
@@ -128,70 +129,67 @@ static VKEnv* VKEnv_Create(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr, VKPl
return NULL;
}
// Query API version.
uint32_t apiVersion = 0;
VK_IF_ERROR(vkEnumerateInstanceVersion(&apiVersion)) return NULL;
J2dRlsTraceLn3(J2D_TRACE_INFO, "Vulkan: Available (%d.%d.%d)",
VK_API_VERSION_MAJOR(apiVersion),
VK_API_VERSION_MINOR(apiVersion),
VK_API_VERSION_PATCH(apiVersion))
if (apiVersion < REQUIRED_VULKAN_VERSION) {
J2dRlsTraceLn3(J2D_TRACE_ERROR, "Vulkan: Unsupported version. Required at least (%d.%d.%d)",
VK_API_VERSION_MAJOR(REQUIRED_VULKAN_VERSION),
VK_API_VERSION_MINOR(REQUIRED_VULKAN_VERSION),
VK_API_VERSION_PATCH(REQUIRED_VULKAN_VERSION))
// Query supported layers.
uint32_t layerCount;
VK_IF_ERROR(vkEnumerateInstanceLayerProperties(&layerCount, NULL)) return NULL;
VkLayerProperties allLayers[layerCount];
VK_IF_ERROR(vkEnumerateInstanceLayerProperties(&layerCount, allLayers)) return NULL;
// Query supported extensions.
uint32_t extensionCount;
VK_IF_ERROR(vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, NULL)) return NULL;
VkExtensionProperties allExtensions[extensionCount];
VK_IF_ERROR(vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, allExtensions)) return NULL;
// Log layers and extensions.
VKNamedEntry_LogAll("instance layers", allLayers[0].layerName, layerCount, sizeof(VkLayerProperties));
VKNamedEntry_LogAll("instance extensions", allExtensions[0].extensionName, extensionCount, sizeof(VkExtensionProperties));
// Check API version.
ARRAY(pchar) errors = NULL;
if (apiVersion < REQUIRED_VULKAN_VERSION) ARRAY_PUSH_BACK(errors) = "Unsupported API version";
// Check layers.
VKNamedEntry* layers = NULL;
#ifdef DEBUG
DEF_NAMED_ENTRY(layers, VK_KHR_VALIDATION_LAYER);
#endif
VKNamedEntry_Match(layers, allLayers[0].layerName, layerCount, sizeof(VkLayerProperties));
VKNamedEntry_LogFound(layers);
// Check extensions.
pchar PLATFORM_SURFACE_EXTENSION_NAME = platformData != NULL ? platformData->surfaceExtensionName : NULL;
VKNamedEntry* extensions = NULL;
DEF_NAMED_ENTRY(extensions, PLATFORM_SURFACE_EXTENSION);
DEF_NAMED_ENTRY(extensions, VK_KHR_SURFACE_EXTENSION);
#ifdef DEBUG
DEF_NAMED_ENTRY(extensions, VK_EXT_DEBUG_UTILS_EXTENSION);
#endif
VKNamedEntry_Match(extensions, allExtensions[0].extensionName, extensionCount, sizeof(VkExtensionProperties));
VKNamedEntry_LogFound(extensions);
// Check found errors.
if (errors != NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, " Vulkan is not supported:")
VKCapabilityUtil_LogErrors(J2D_TRACE_ERROR, errors);
ARRAY_FREE(errors);
return NULL;
}
uint32_t extensionsCount;
// Get the number of extensions and layers
VK_IF_ERROR(vkEnumerateInstanceExtensionProperties(NULL, &extensionsCount, NULL)) return NULL;
VkExtensionProperties extensions[extensionsCount];
VK_IF_ERROR(vkEnumerateInstanceExtensionProperties(NULL, &extensionsCount, extensions)) return NULL;
uint32_t layersCount;
VK_IF_ERROR(vkEnumerateInstanceLayerProperties(&layersCount, NULL)) return NULL;
VkLayerProperties layers[layersCount];
VK_IF_ERROR(vkEnumerateInstanceLayerProperties(&layersCount, layers)) return NULL;
J2dRlsTraceLn(J2D_TRACE_VERBOSE, " Supported instance layers:")
for (uint32_t i = 0; i < layersCount; i++) {
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " %s", (char *) layers[i].layerName)
}
J2dRlsTraceLn(J2D_TRACE_VERBOSE, " Supported instance extensions:")
for (uint32_t i = 0; i < extensionsCount; i++) {
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " %s", (char *) extensions[i].extensionName)
}
ARRAY(pchar) enabledLayers = NULL;
ARRAY(pchar) enabledExtensions = NULL;
void *pNext = NULL;
ARRAY_PUSH_BACK(enabledExtensions) = VK_KHR_SURFACE_EXTENSION_NAME;
if (platformData != NULL && platformData->surfaceExtensionName != NULL) {
ARRAY_PUSH_BACK(enabledExtensions) = platformData->surfaceExtensionName;
}
// Check required layers & extensions.
for (uint32_t i = 0; i < ARRAY_SIZE(enabledExtensions); i++) {
int notFound = 1;
for (uint32_t j = 0; j < extensionsCount; j++) {
if (strcmp(extensions[j].extensionName, enabledExtensions[i]) == 0) {
notFound = 0;
break;
}
}
if (notFound) {
J2dRlsTraceLn1(J2D_TRACE_ERROR, "Vulkan: Required extension %s not found", enabledExtensions[i])
ARRAY_FREE(enabledLayers);
ARRAY_FREE(enabledExtensions);
return NULL;
}
}
// Check presentation support.
VkBool32 presentationSupported = PLATFORM_SURFACE_EXTENSION.found && VK_KHR_SURFACE_EXTENSION.found;
if (!presentationSupported) PLATFORM_SURFACE_EXTENSION.found = VK_KHR_SURFACE_EXTENSION.found = NULL;
// Configure validation
void *pNext = NULL;
#ifdef DEBUG
VkValidationFeatureEnableEXT enables[] = {
// VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT,
@@ -200,51 +198,32 @@ static VKEnv* VKEnv_Create(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr, VKPl
// VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT,
VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT
};
VkValidationFeaturesEXT features = {};
features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
features.enabledValidationFeatureCount = SARRAY_COUNT_OF(enables);
features.pEnabledValidationFeatures = enables;
// Includes the validation features into the instance creation process
int foundDebugLayer = 0;
for (uint32_t i = 0; i < layersCount; i++) {
if (strcmp((char *) layers[i].layerName, "VK_LAYER_KHRONOS_validation") == 0) {
foundDebugLayer = 1;
break;
}
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " %s", (char *) layers[i].layerName)
}
int foundDebugExt = 0;
for (uint32_t i = 0; i < extensionsCount; i++) {
if (strcmp((char *) extensions[i].extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0) {
foundDebugExt = 1;
break;
}
}
if (foundDebugLayer && foundDebugExt) {
ARRAY_PUSH_BACK(enabledLayers) = "VK_LAYER_KHRONOS_validation";
ARRAY_PUSH_BACK(enabledExtensions) = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
VkValidationFeaturesEXT features = {
.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT,
.enabledValidationFeatureCount = SARRAY_COUNT_OF(enables),
.pEnabledValidationFeatures = enables
};
if (VK_KHR_VALIDATION_LAYER.found && VK_EXT_DEBUG_UTILS_EXTENSION.found) {
pNext = &features;
} else {
J2dRlsTraceLn2(J2D_TRACE_WARNING, "Vulkan: %s and %s are not supported",
"VK_LAYER_KHRONOS_validation", VK_EXT_DEBUG_UTILS_EXTENSION_NAME)
VK_KHR_VALIDATION_LAYER.found = VK_EXT_DEBUG_UTILS_EXTENSION.found = NULL;
J2dRlsTraceLn(J2D_TRACE_WARNING, " Vulkan validation is not supported")
}
#endif
VKEnv* vk = malloc(sizeof(VKEnv));
if (vk == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Vulkan: Cannot allocate VKEnv")
ARRAY_FREE(enabledLayers);
ARRAY_FREE(enabledExtensions);
J2dRlsTraceLn(J2D_TRACE_ERROR, " Cannot allocate VKEnv")
return NULL;
}
*vk = (VKEnv) {
.platformData = platformData
.platformData = platformData,
.presentationSupported = presentationSupported
};
ARRAY(pchar) enabledLayers = VKNamedEntry_CollectNames(layers);
ARRAY(pchar) enabledExtensions = VKNamedEntry_CollectNames(extensions);
VkApplicationInfo applicationInfo = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = NULL,
@@ -284,17 +263,23 @@ static VKEnv* VKEnv_Create(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr, VKPl
VKEnv_Destroy(vk);
return NULL;
}
if (vk->platformData != NULL && vk->platformData->initFunctions != NULL &&
!vk->platformData->initFunctions(vk, vkGetInstanceProcAddr)) {
VKEnv_Destroy(vk);
return NULL;
if (presentationSupported) {
SURFACE_INSTANCE_FUNCTION_TABLE(CHECK_PROC_ADDR, missingAPI, vkGetInstanceProcAddr, vk->instance, vk->)
if (missingAPI) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Vulkan: Required API is missing:")
SURFACE_INSTANCE_FUNCTION_TABLE(LOG_MISSING_PFN, vk->)
}
if (missingAPI || !vk->platformData->initFunctions(vk, vkGetInstanceProcAddr)) {
vk->presentationSupported = presentationSupported = VK_FALSE;
}
}
J2dRlsTraceLn1(J2D_TRACE_INFO, "Vulkan: Presentation supported = %s", presentationSupported ? "true" : "false")
vk->composites = VKComposites_Create();
// Create debug messenger
#if defined(DEBUG)
if (foundDebugLayer && foundDebugExt &&
if (VK_KHR_VALIDATION_LAYER.found && VK_EXT_DEBUG_UTILS_EXTENSION.found &&
vk->vkCreateDebugUtilsMessengerEXT != NULL && pNext) {
VkDebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
@@ -321,10 +306,10 @@ void VKDevice_CheckAndAdd(VKEnv* vk, VkPhysicalDevice physicalDevice);
static VkBool32 VKEnv_FindDevices(VKEnv* vk) {
uint32_t count;
VK_IF_ERROR(vk->vkEnumeratePhysicalDevices(vk->instance, &count, NULL)) return JNI_FALSE;
J2dRlsTraceLn1(J2D_TRACE_INFO, "Vulkan: Found %d physical devices:", count)
VkPhysicalDevice physicalDevices[count];
VK_IF_ERROR(vk->vkEnumeratePhysicalDevices(vk->instance, &count, physicalDevices)) return JNI_FALSE;
ARRAY_ENSURE_CAPACITY(vk->devices, count);
J2dRlsTraceLn1(J2D_TRACE_INFO, "Vulkan: Found %d physical devices:", count)
for (uint32_t i = 0; i < count; i++) {
VKDevice_CheckAndAdd(vk, physicalDevices[i]);
}

View File

@@ -59,8 +59,10 @@ struct VKEnv {
#endif
VKPlatformData* platformData;
VkBool32 presentationSupported;
INSTANCE_FUNCTION_TABLE(DECL_PFN)
SURFACE_INSTANCE_FUNCTION_TABLE(DECL_PFN)
DEBUG_INSTANCE_FUNCTION_TABLE(DECL_PFN)
};

View File

@@ -44,16 +44,18 @@ ENTRY(__VA_ARGS__, vkGetPhysicalDeviceMemoryProperties); \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceFeatures2); \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceProperties2); \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceQueueFamilyProperties); \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceSurfaceCapabilitiesKHR); \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceSurfaceFormatsKHR); \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceFormatProperties); \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceSurfacePresentModesKHR); \
ENTRY(__VA_ARGS__, vkEnumerateDeviceLayerProperties); \
ENTRY(__VA_ARGS__, vkEnumerateDeviceExtensionProperties); \
ENTRY(__VA_ARGS__, vkCreateDevice); \
ENTRY(__VA_ARGS__, vkDestroySurfaceKHR); \
ENTRY(__VA_ARGS__, vkGetDeviceProcAddr); \
#define SURFACE_INSTANCE_FUNCTION_TABLE(ENTRY, ...) \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceSurfaceCapabilitiesKHR); \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceSurfaceFormatsKHR); \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceSurfacePresentModesKHR); \
ENTRY(__VA_ARGS__, vkDestroySurfaceKHR); \
#if defined(DEBUG)
#define DEBUG_INSTANCE_FUNCTION_TABLE(ENTRY, ...) \
ENTRY(__VA_ARGS__, vkCreateDebugUtilsMessengerEXT); \
@@ -71,9 +73,6 @@ ENTRY(__VA_ARGS__, vkCreatePipelineLayout); \
ENTRY(__VA_ARGS__, vkDestroyPipelineLayout); \
ENTRY(__VA_ARGS__, vkCreateGraphicsPipelines); \
ENTRY(__VA_ARGS__, vkDestroyPipeline); \
ENTRY(__VA_ARGS__, vkCreateSwapchainKHR); \
ENTRY(__VA_ARGS__, vkDestroySwapchainKHR); \
ENTRY(__VA_ARGS__, vkGetSwapchainImagesKHR); \
ENTRY(__VA_ARGS__, vkCreateImageView); \
ENTRY(__VA_ARGS__, vkCreateFramebuffer); \
ENTRY(__VA_ARGS__, vkCreateCommandPool); \
@@ -88,11 +87,9 @@ ENTRY(__VA_ARGS__, vkCreateFence); \
ENTRY(__VA_ARGS__, vkGetDeviceQueue); \
ENTRY(__VA_ARGS__, vkWaitForFences); \
ENTRY(__VA_ARGS__, vkResetFences); \
ENTRY(__VA_ARGS__, vkAcquireNextImageKHR); \
ENTRY(__VA_ARGS__, vkResetCommandBuffer); \
ENTRY(__VA_ARGS__, vkQueueSubmit); \
ENTRY(__VA_ARGS__, vkQueueWaitIdle); \
ENTRY(__VA_ARGS__, vkQueuePresentKHR); \
ENTRY(__VA_ARGS__, vkBeginCommandBuffer); \
ENTRY(__VA_ARGS__, vkCmdBlitImage); \
ENTRY(__VA_ARGS__, vkCmdPipelineBarrier); \
@@ -141,6 +138,13 @@ ENTRY(__VA_ARGS__, vkCmdCopyBufferToImage); \
ENTRY(__VA_ARGS__, vkCmdCopyImageToBuffer); \
ENTRY(__VA_ARGS__, vkCmdCopyBuffer); \
#define SWAPCHAIN_DEVICE_FUNCTION_TABLE(ENTRY, ...) \
ENTRY(__VA_ARGS__, vkCreateSwapchainKHR); \
ENTRY(__VA_ARGS__, vkDestroySwapchainKHR); \
ENTRY(__VA_ARGS__, vkGetSwapchainImagesKHR); \
ENTRY(__VA_ARGS__, vkAcquireNextImageKHR); \
ENTRY(__VA_ARGS__, vkQueuePresentKHR); \
// Utilities for working with function pointers.
#define DECL_PFN(_, NAME) PFN_ ## NAME NAME