|
Grado en Ingeniería Informática
Animación por Ordenador
Curso 2025/2026
|
Práctica 1e
|
|
Selección del dispositivo físico
|
|
Objetivos
|
|
Añade al proyecto la selección del dispositivo físico (tarjeta
gráfica) a utilizar para generar las imágenes. Junto a la referencia
al dispositivo también se almacenan las propiedades de los
diferentes tipos de memoria incluidas en el dispositivo y las
familias de colas que soportan comandos de renderizado y de
presentación.
|
|
Dispositivos físicos
|
|
Un dispositivo físico se representa mediante una valor de tipo
VkPhysicalDevice, que no es más que un descriptor (handle).
Para obtener los dispositivos disponibles se ejecuta dos veces la
función vkEnumeratePhysicalDevices(), una vez para obtener
el número de dispositivos y la segunda para obtener los
descriptores:
VkResult vkEnumeratePhysicalDevices (
VkInstance instance,
uint32_t* pPhysicalDeviceCount,
VkPhysicalDevice* pPhysicalDevices);
|
A partir de la referencia a un dispositivo físico se pueden obtener
sus propiedades, sus características y los tipos de memoria que
utiliza internamente.
void vkGetPhysicalDeviceProperties (
VkPhysicalDevice physicalDevice,
VkPhysicalDeviceProperties* pProperties);
void vkGetPhysicalDeviceFeatures (
VkPhysicalDevice physicalDevice,
VkPhysicalDeviceFeatures* pFeatures);
void vkGetPhysicalDeviceMemoryProperties (
VkPhysicalDevice physicalDevice,
VkPhysicalDeviceMemoryProperties* pMemoryProperties);
|
Las propiedades de un dispositivo físico se expresan en una
estructura denominada VkPhysicalDeviceProperties que
contiene campos como el nombre del dispositivo, el fabricante o la
versión del driver utilizado.
typedef struct VkPhysicalDeviceProperties {
uint32_t apiVersion;
uint32_t driverVersion;
uint32_t vendorID;
uint32_t deviceID;
VkPhysicalDeviceType deviceType;
char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE];
uint8_t pipelineCacheUUID[VK_UUID_SIZE];
VkPhysicalDeviceLimits limits;
VkPhysicalDeviceSparseProperties sparseProperties;
} VkPhysicalDeviceProperties;
|
El tipo de dispositivo se describe en el campo deviceType que
corresponde a un valor del tipo enumerado VkPhysicalDeviceType. Este
valor indica si el dispositivo se trata de una tarjeta gráfica
integrada o es una tarjeta gráfica dedicada o virtualizada,
etc.
typedef enum VkPhysicalDeviceType {
VK_PHYSICAL_DEVICE_TYPE_OTHER = 0,
VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU = 1,
VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU = 2,
VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU = 3,
VK_PHYSICAL_DEVICE_TYPE_CPU = 4,
} VkPhysicalDeviceType;
|
Muchas de las propiedades del dispositivo se encuentran descritas en
el campo limits que almacena una estructura de tipo
VkPhysicalDeviceLimits formada por numerosos campos que describen los
valores máximos y mínimos soportados por el dispositivo para
distintas propiedades.
typedef struct VkPhysicalDeviceLimits {
uint32_t maxImageDimension1D;
uint32_t maxImageDimension2D;
uint32_t maxImageDimension3D;
uint32_t maxImageDimensionCube;
uint32_t maxImageArrayLayers;
uint32_t maxTexelBufferElements;
uint32_t maxUniformBufferRange;
uint32_t maxStorageBufferRange;
uint32_t maxPushConstantsSize;
uint32_t maxMemoryAllocationCount;
uint32_t maxSamplerAllocationCount;
VkDeviceSize bufferImageGranularity;
VkDeviceSize sparseAddressSpaceSize;
uint32_t maxBoundDescriptorSets;
uint32_t maxPerStageDescriptorSamplers;
uint32_t maxPerStageDescriptorUniformBuffers;
uint32_t maxPerStageDescriptorStorageBuffers;
uint32_t maxPerStageDescriptorSampledImages;
uint32_t maxPerStageDescriptorStorageImages;
uint32_t maxPerStageDescriptorInputAttachments;
uint32_t maxPerStageResources;
uint32_t maxDescriptorSetSamplers;
uint32_t maxDescriptorSetUniformBuffers;
uint32_t maxDescriptorSetUniformBuffersDynamic;
uint32_t maxDescriptorSetStorageBuffers;
uint32_t maxDescriptorSetStorageBuffersDynamic;
uint32_t maxDescriptorSetSampledImages;
uint32_t maxDescriptorSetStorageImages;
uint32_t maxDescriptorSetInputAttachments;
uint32_t maxVertexInputAttributes;
uint32_t maxVertexInputBindings;
uint32_t maxVertexInputAttributeOffset;
uint32_t maxVertexInputBindingStride;
uint32_t maxVertexOutputComponents;
uint32_t maxTessellationGenerationLevel;
uint32_t maxTessellationPatchSize;
uint32_t maxTessellationControlPerVertexInputComponents;
uint32_t maxTessellationControlPerVertexOutputComponents;
uint32_t maxTessellationControlPerPatchOutputComponents;
uint32_t maxTessellationControlTotalOutputComponents;
uint32_t maxTessellationEvaluationInputComponents;
uint32_t maxTessellationEvaluationOutputComponents;
uint32_t maxGeometryShaderInvocations;
uint32_t maxGeometryInputComponents;
uint32_t maxGeometryOutputComponents;
uint32_t maxGeometryOutputVertices;
uint32_t maxGeometryTotalOutputComponents;
uint32_t maxFragmentInputComponents;
uint32_t maxFragmentOutputAttachments;
uint32_t maxFragmentDualSrcAttachments;
uint32_t maxFragmentCombinedOutputResources;
uint32_t maxComputeSharedMemorySize;
uint32_t maxComputeWorkGroupCount[3];
uint32_t maxComputeWorkGroupInvocations;
uint32_t maxComputeWorkGroupSize[3];
uint32_t subPixelPrecisionBits;
uint32_t subTexelPrecisionBits;
uint32_t mipmapPrecisionBits;
uint32_t maxDrawIndexedIndexValue;
uint32_t maxDrawIndirectCount;
float maxSamplerLodBias;
float maxSamplerAnisotropy;
uint32_t maxViewports;
uint32_t maxViewportDimensions[2];
float viewportBoundsRange[2];
uint32_t viewportSubPixelBits;
size_t minMemoryMapAlignment;
VkDeviceSize minTexelBufferOffsetAlignment;
VkDeviceSize minUniformBufferOffsetAlignment;
VkDeviceSize minStorageBufferOffsetAlignment;
int32_t minTexelOffset;
uint32_t maxTexelOffset;
int32_t minTexelGatherOffset;
uint32_t maxTexelGatherOffset;
float minInterpolationOffset;
float maxInterpolationOffset;
uint32_t subPixelInterpolationOffsetBits;
uint32_t maxFramebufferWidth;
uint32_t maxFramebufferHeight;
uint32_t maxFramebufferLayers;
VkSampleCountFlags framebufferColorSampleCounts;
VkSampleCountFlags framebufferDepthSampleCounts;
VkSampleCountFlags framebufferStencilSampleCounts;
VkSampleCountFlags framebufferNoAttachmentsSampleCounts;
uint32_t maxColorAttachments;
VkSampleCountFlags sampledImageColorSampleCounts;
VkSampleCountFlags sampledImageIntegerSampleCounts;
VkSampleCountFlags sampledImageDepthSampleCounts;
VkSampleCountFlags sampledImageStencilSampleCounts;
VkSampleCountFlags storageImageSampleCounts;
uint32_t maxSampleMaskWords;
VkBool32 timestampComputeAndGraphics;
float timestampPeriod;
uint32_t maxClipDistances;
uint32_t maxCullDistances;
uint32_t maxCombinedClipAndCullDistances;
uint32_t discreteQueuePriorities;
float pointSizeRange[2];
float lineWidthRange[2];
float pointSizeGranularity;
float lineWidthGranularity;
VkBool32 strictLines;
VkBool32 standardSampleLocations;
VkDeviceSize optimalBufferCopyOffsetAlignment;
VkDeviceSize optimalBufferCopyRowPitchAlignment;
VkDeviceSize nonCoherentAtomSize;
} VkPhysicalDeviceLimits;
|
Las características del dispositivo se describen en una estructura
de tipo VkPhysicalDeviceFeatures, que está formada por numerosos flags.
typedef struct VkPhysicalDeviceFeatures {
VkBool32 robustBufferAccess;
VkBool32 fullDrawIndexUint32;
VkBool32 imageCubeArray;
VkBool32 independentBlend;
VkBool32 geometryShader;
VkBool32 tessellationShader;
VkBool32 sampleRateShading;
VkBool32 dualSrcBlend;
VkBool32 logicOp;
VkBool32 multiDrawIndirect;
VkBool32 drawIndirectFirstInstance;
VkBool32 depthClamp;
VkBool32 depthBiasClamp;
VkBool32 fillModeNonSolid;
VkBool32 depthBounds;
VkBool32 wideLines;
VkBool32 largePoints;
VkBool32 alphaToOne;
VkBool32 multiViewport;
VkBool32 samplerAnisotropy;
VkBool32 textureCompressionETC2;
VkBool32 textureCompressionASTC_LDR;
VkBool32 textureCompressionBC;
VkBool32 occlusionQueryPrecise;
VkBool32 pipelineStatisticsQuery;
VkBool32 vertexPipelineStoresAndAtomics;
VkBool32 fragmentStoresAndAtomics;
VkBool32 shaderTessellationAndGeometryPointSize;
VkBool32 shaderImageGatherExtended;
VkBool32 shaderStorageImageExtendedFormats;
VkBool32 shaderStorageImageMultisample;
VkBool32 shaderStorageImageReadWithoutFormat;
VkBool32 shaderStorageImageWriteWithoutFormat;
VkBool32 shaderUniformBufferArrayDynamicIndexing;
VkBool32 shaderSampledImageArrayDynamicIndexing;
VkBool32 shaderStorageBufferArrayDynamicIndexing;
VkBool32 shaderStorageImageArrayDynamicIndexing;
VkBool32 shaderClipDistance;
VkBool32 shaderCullDistance;
VkBool32 shaderFloat64;
VkBool32 shaderInt64;
VkBool32 shaderInt16;
VkBool32 shaderResourceResidency;
VkBool32 shaderResourceMinLod;
VkBool32 sparseBinding;
VkBool32 sparseResidencyBuffer;
VkBool32 sparseResidencyImage2D;
VkBool32 sparseResidencyImage3D;
VkBool32 sparseResidency2Samples;
VkBool32 sparseResidency4Samples;
VkBool32 sparseResidency8Samples;
VkBool32 sparseResidency16Samples;
VkBool32 sparseResidencyAliased;
VkBool32 variableMultisampleRate;
VkBool32 inheritedQueries;
} VkPhysicalDeviceFeatures;
|
Las tarjetas gráficas contienen diferentes tipos de memoria interna.
La descripción de los tipos de memoria incluidos en un dispositivo
se representa en una estructura VkPhysicalDeviceMemoryProperties
.
typedef struct VkPhysicalDeviceMemoryProperties {
uint32_t memoryTypeCount;
VkMemoryType memoryTypes[VK_MAX_MEMORY_TYPES];
uint32_t memoryHeapCount;
VkMemoryHeap memoryHeaps[VK_MAX_MEMORY_HEAPS];
} VkPhysicalDeviceMemoryProperties;
|
Los tipos de memoria se describen en estructuras VkMemoryType. Estas
indican el heap al que está vinculada cada memoria y un campo
propertyFlags
que indica si la memoria es accesible con rapidez desde el interior
del dispositivo y desde el exterior.
typedef struct VkMemoryType {
VkMemoryPropertyFlags propertyFlags;
uint32_t heapIndex;
} VkMemoryType;
typedef enum VkMemoryPropertyFlagBits {
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT = 0x00000001,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT = 0x00000002,
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT = 0x00000004,
VK_MEMORY_PROPERTY_HOST_CACHED_BIT = 0x00000008,
VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT = 0x00000010,
// Provided by VK_VERSION_1_1
VK_MEMORY_PROPERTY_PROTECTED_BIT = 0x00000020,
// Provided by VK_AMD_device_coherent_memory
VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD = 0x00000040,
// Provided by VK_AMD_device_coherent_memory
VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD = 0x00000080,
// Provided by VK_NV_external_memory_rdma
VK_MEMORY_PROPERTY_RDMA_CAPABLE_BIT_NV = 0x00000100,
} VkMemoryPropertyFlagBits;
|
Para enviar trabajos a los dispositivos se utilizan colas (queue).
Los dispositivos pueden trabajar con varias colas en paralelo. Las
colas se distribuyen en familias, que son colas con las mismas
características. Dado un dispositivo, se pueden obtener las familias
de colas soportadas utilizando
vkGetPhysicalDeviceQueueFamilyProperties(). Esta función se
suele ejecutar dos veces para obtener primero el número de familias
de colas soportadas y luego las propiedades de estas familias.
void vkGetPhysicalDeviceQueueFamilyProperties (
VkPhysicalDevice physicalDevice,
uint32_t* pQueueFamilyPropertyCount,
VkQueueFamilyProperties* pQueueFamilyProperties);
|
Las propiedades de las familias de colas se expresan mediante la
estructura VkQueueFamilyProperties. El campo queueFlags
indica el uso que permite una determinada familia: operaciones
gráficas, operaciones de computación, operaciones de transferencia,
etc. El campo queueCount indica el número de colas de esa familia
que pueden ejecutarse en paralelo en el dispositivo.
typedef struct VkQueueFamilyProperties {
VkQueueFlags queueFlags;
uint32_t queueCount;
uint32_t timestampValidBits;
VkExtent3D minImageTransferGranularity;
} VkQueueFamilyProperties;
|
El tipo de dato VkQueueFlags permite encapsular diferentes
opciones en forma de bits. Los valores definidos para estos bits
están descritos en la enumeración VkQueueFlagBits.
typedef enum VkQueueFlagBits {
VK_QUEUE_GRAPHICS_BIT = 0x00000001,
VK_QUEUE_COMPUTE_BIT = 0x00000002,
VK_QUEUE_TRANSFER_BIT = 0x00000004,
VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008,
VK_QUEUE_PROTECTED_BIT = 0x00000010,
VK_QUEUE_VIDEO_DECODE_BIT_KHR = 0x00000020,
VK_QUEUE_VIDEO_ENCODE_BIT_KHR = 0x00000040,
} VkQueueFlagBits;
|
No todos los dispositivos físicos soportan la capacidad de presentar imágenes
sobre una superficie. Para comprobarlo se puede utilizar una función genérica
o una función específica de la plataforma utilizada. La comprobación se realiza
sobre las familias de colas que soporta el dispositivo.
VkResult vkGetPhysicalDeviceSurfaceSupportKHR(
VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex,
VkSurfaceKHR surface,
VkBool32* pSupported);
VkBool32 vkGetPhysicalDeviceWin32PresentationSupportKHR(
VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex);
|
|
|
Modificaciones de la clase
GEGraphicsContext
|
|
Para incluir
el dispositivo físico (una estructura VkPhysicalDevice)
es necesario modificar la clase GEGraphicsContext. La
modificación consiste en añadir el campo physicalDevice, el
campo memProperties y los campos
graphicsQueueFamilyIndex y presentQueueFamilyIndex.
#pragma once
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <vulkan/vulkan.h>
class GEGraphicsContext
{
public:
VkInstance instance;
VkSurfaceKHR surface;
VkPhysicalDevice physicalDevice;
uint32_t graphicsQueueFamilyIndex;
uint32_t presentQueueFamilyIndex;
private:
VkPhysicalDeviceMemoryProperties memProperties;
public:
GEGraphicsContext(GLFWwindow* window);
~GEGraphicsContext();
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);
VkFormat findDepthFormat();
private:
// Métodos de inicialización de Vulkan
void createInstance();
void createSurface(GLFWwindow* window);
void pickPhysicalDevice();
// Métodos auxiliares
void showInstanceProperties();
bool isDeviceSuitable(VkPhysicalDevice pDevice);
void showDevices();
void resumeDeviceProperties(VkPhysicalDevice pDevice, int index);
};
|
En el código de la clase es necesario
añadir algunos métodos y modificar otros.
-
GEGraphicsContext(GLFWwindow* window): Constructor de
la clase. Se ha modificado para seledccionar el dispositivo
físico y mostrar las propiedades de todos los dispositivos
disponibles.
-
findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties):
Busca entre las diferentes memorias del dispositivo una que
cumpla con las propiedades solicitadas.
-
findDepthFormat(): Busca el formato adecuado para
representar los datos de profundidad. El buffer de profundidad
se utiliza al generar las imágenes para detectar si un objeto
está por delante de otro. Existen diferentes formatos que pueden
utilizarse para almacenar la profundiad y no todos los
dispositivos soportan todos los formatos.
-
pickPhysicalDevice(): Selecciona el dispositivo físico
a utilizar para generar los gráficos. Debe ser un dispositivo
que soporte tanto la generación de gráficos como la presentación
de los gráficos generados.Obtiene la
lista de dispositivos disponibles llamando dos veces a la función
vkEnumeratePhysicalDevices() y verifica si el
dispositivo es seleccionable llamando al método auxiliar
isDeviceSuitable(). Una vez seleccionado el dispositivo
almacena las propiedades de las memorias para facilitar las
búsquedas de memorias posteriores.
-
isDeviceSuitable():
Estudia si un cierto dispositivo físico
admite la generación de gráficos y su presentación. Para que un dispositivo pueda
generar gráficos es necesario que admita alguna cola que soporte la
generación de gráficos. Por tanto hay que examinar las familias de
colas disponibles en el dispositivo y estudiar si las propiedades de
alguna de ellas contiene el bit VK_QUEUE_GRAPHICS_BIT que indica
esta propiedad de las colas. Para saber si una familia de colas
soporta la presentación sobre la superficie creada se utiliza el
método vkGetPhysicalDeviceSurfaceSupportKHR(). SAl mismo tiempo
que se estudia el dispositivo se asigna el valor de los campos
graphicsQueueFamilyIndex y presentQueueFamilyIndex y
se intenta escoger una familia que soorte las dos
características.
-
showDevices(): Recorre todos los dispositivos generando
un fichero con sus propiedades.
-
resumeDeviceProperties(VkPhysicalDevice
pDevice, int index): Lee las propiedades de un dispositivo
y genera un fichero describiendo estas propiedades.
///////////////////////////////////////////////////////////////////////////////////
///// /////
///// Métodos públicos /////
///// /////
///////////////////////////////////////////////////////////////////////////////////
//
// FUNCIÓN: GEGraphicsContext::GEGraphicsContext()
//
// PROPÓSITO: Crea un contexto gráfico de Vulkan (instancia, superficie, dispositivo)
//
GEGraphicsContext::GEGraphicsContext(GLFWwindow* window)
{
createInstance();
createSurface(window);
// showInstanceProperties();
pickPhysicalDevice();
showDevices();
}
//
// FUNCIÓN: GEGraphicsContext::findMemoryType()
//
// PROPÓSITO: Busca el tipo de memoria adecuado para el filtro indicado
//
uint32_t GEGraphicsContext::findMemoryType(uint32_t typeFilter,
VkMemoryPropertyFlags properties)
{
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
{
if ((typeFilter & (1 << i)) &&
(memProperties.memoryTypes[i].propertyFlags & properties) == properties)
{
return i;
}
}
throw std::runtime_error("failed to find suitable memory type!");
}
//
// FUNCIÓN: GEGraphicsContext::findDepthFormat()
//
// PROPÓSITO: Busca el formato adecuado para el buffer de profundidad
//
VkFormat GEGraphicsContext::findDepthFormat()
{
VkFormat candidates[] = { VK_FORMAT_D32_SFLOAT,
VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D24_UNORM_S8_UINT };
VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;
VkFormatFeatureFlags features = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
for (VkFormat format : candidates)
{
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props);
if (tiling == VK_IMAGE_TILING_LINEAR &&
(props.linearTilingFeatures & features) == features)
{
return format;
}
else if (tiling == VK_IMAGE_TILING_OPTIMAL &&
(props.optimalTilingFeatures & features) == features)
{
return format;
}
}
throw std::runtime_error("failed to find supported format!");
}
///////////////////////////////////////////////////////////////////////////////////
///// /////
///// Métodos de inicialización de Vulkan /////
///// /////
///////////////////////////////////////////////////////////////////////////////////
...
//
// FUNCIÓN: GEGraphicsContext::pickPhysicalDevice()
//
// PROPÓSITO: Selecciona el dispositivo físico sobre el que generar los gráficos
//
void GEGraphicsContext::pickPhysicalDevice()
{
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
if (deviceCount == 0)
{
throw std::runtime_error("failed to find GPUs with Vulkan support!");
}
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
for (unsigned int i = 0; i < deviceCount; i++)
{
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(devices[i], &deviceProperties);
if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
isDeviceSuitable(devices[i]))
{
physicalDevice = devices[i];
break;
}
}
if (physicalDevice == VK_NULL_HANDLE)
{
for (unsigned int i = 0; i < deviceCount; i++)
{
if (isDeviceSuitable(devices[i]))
{
physicalDevice = devices[i];
break;
}
}
}
if (physicalDevice == VK_NULL_HANDLE)
{
throw std::runtime_error("failed to find a suitable GPU!");
}
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
}
///////////////////////////////////////////////////////////////////////////////////
///// /////
///// Métodos auxiliares /////
///// /////
///////////////////////////////////////////////////////////////////////////////////
//
// FUNCIÓN: GEGraphicsContext::isDeviceSuitable(VkPhysicalDevice pDevice)
//
// PROPÓSITO: Verifica si un dispositivo físico admite generación de gráficos
//
bool GEGraphicsContext::isDeviceSuitable(VkPhysicalDevice pDevice)
{
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(pDevice, &queueFamilyCount, nullptr);
std::vector< VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(pDevice, &queueFamilyCount,
queueFamilies.data());
bool graphics = false;
bool present = false;
for (uint32_t i = 0; i < queueFamilyCount; i++)
{
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(pDevice, i, surface, &presentSupport);
if (presentSupport && (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))
{
graphicsQueueFamilyIndex = i;
presentQueueFamilyIndex = i;
graphics = true;
present = true;
break;
}
}
if (!(graphics && present))
{
for (uint32_t i = 0; i < queueFamilyCount; i++)
{
if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
{
graphicsQueueFamilyIndex = i;
graphics = true;
}
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(pDevice, i, surface, &presentSupport);
if (presentSupport)
{
presentQueueFamilyIndex = i;
present = true;
}
}
}
if (graphics && present) return true;
return false;
}
//
// FUNCIÓN: GEGraphicsContext::showInstanceProperties()
//
// PROPÓSITO: Muestra los dispositivos físicos disponibles
// y genera un archivo con las propiedades de cada uno
//
void GEGraphicsContext::showDevices()
{
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
// Mostrar los dispositivos físicos disponibles
std::cout << "Physical devices:" << std::endl;
for (unsigned int i = 0; i < deviceCount; i++)
{
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(devices[i], &deviceProperties);
std::cout << "\t" << deviceProperties.deviceName << std::endl;
resumeDeviceProperties(devices[i], i);
}
std::cout << "Selected device:" << std::endl;
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
std::cout << "\t" << deviceProperties.deviceName << std::endl;
}
//
// FUNCIÓN: GEGraphicsContext::resume(VkPhysicalDevice pDevice, int index)
//
// PROPÓSITO: Escribe las caracterísiticas del dispositivo físico
//
#define BOOL(x) ( (x)!=0? "true" : "false")
void GEGraphicsContext::resumeDeviceProperties(VkPhysicalDevice pDevice, int index)
{
...
}
|
|
|
Aspecto final
|
|
El
aspecto de la aplicación es el siguiente. La consola muestra la
lista de dispositivos disponibles compatibles con Vulkan. Esta
información depende de la plataforma en la que se esté ejecutando la
aplicación. La ejecución también genera el fichero "deviceX.txt" con
las características de cada dispositivo.
|
|