Escuela Técnica Superior de Ingeniería

 

Grado en Ingeniería Informática

Animación por Ordenador

Curso 2025/2026

 

Práctica 1f

Creación del dispositivo lógico

 

Objetivos

 

Añadir al proyecto la creación de un dispositivo lógico.

 

 

Dispositivos lógicos

 

A partir de un dispositivo físico (VkPhysicalDevice) se puede construir un dispositivo lógico (VkDevice), que es una representación del dispositivo físico con un determinado estado. Se pueden construir varios dispositivos lógicos a partir de un mismo dispositivo físico. La mayoría de las funciones de Vulkan tienen como primer argumento el dispositivo lógico sobre el que se aplica la función.

VkResult vkCreateDevice (
  VkPhysicalDevice             physicalDevice,
  const VkDeviceCreateInfo*    pCreateInfo,
  const VkAllocationCallbacks* pAllocator,
  VkDevice*                    pDevice);

Para configurar el dispositivo lógico a crear se utiliza una estructura VkDeviceCreateInfo, que se identifica indicando como campo sType el valor VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO. La estructura especifica las capas, las extensiones, las familias de colas y las características a utilizar en el dispositivo.

typedef struct VkDeviceCreateInfo {
  VkStructureType                 sType;
  const void*                     pNext;
  VkDeviceCreateFlags             flags;
  uint32_t                        queueCreateInfoCount;
  const VkDeviceQueueCreateInfo*  pQueueCreateInfos;
  uint32_t                        enabledLayerCount;
  const char* const*              ppEnabledLayerNames;
  uint32_t                        enabledExtensionCount;
  const char* const*              ppEnabledExtensionNames;
  const VkPhysicalDeviceFeatures* pEnabledFeatures;
} VkDeviceCreateInfo;

Las colas de comandos a utilizar en el dispositivo lógico se describen con estructuras VkDeviceQueueCreateInfo. El campo sType toma el valor VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO. De momento no se han definido flags para esta estructura. El índice de la familia corresponde a su posición en el listado de familias del dispositivo y queueCount se refiere al número de colas en paralelo a utilizar en el dispositivo. Al crear un dispositivo lógico se pueden configurar varias familias de colas con varias colas en cada una de ellas.

typedef struct VkDeviceQueueCreateInfo {
  VkStructureType          sType;
  const void*              pNext;
  VkDeviceQueueCreateFlags flags;
  uint32_t                 queueFamilyIndex;
  uint32_t                 queueCount;
  const float*             pQueuePriorities;
} VkDeviceQueueCreateInfo;

 

 

Modificaciones de la clase GEGraphicsContext

 

Las modificaciones necesarias para incluir el dispositivo lógico (una estructura VkDevice) se limitan a la clase GEGraphicsContext. La modificación consiste en añadir el campos device (dispositivo lógico) y el método createLogicalDevice().

#pragma once

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <vulkan/vulkan.h>

class GEGraphicsContext
{
public:
  VkInstance instance;
  VkSurfaceKHR surface;
  VkPhysicalDevice physicalDevice;
  VkDevice device;
  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();
  void createLogicalDevice();

  // Métodos auxiliares
  void showInstanceProperties();
  bool isDeviceSuitable(VkPhysicalDevice pDevice);
  void showDevices();
  void resumeDeviceProperties(VkPhysicalDevice pDevice, int index);
};

Los métodos modificados son el constructor (para añadir la construcción del dispositivo lógico) y el destructor (para añadir la destrucción del dispositivo lógico). Además se ha añadido el método createLogicalDevice() que genera el dispositivo lógico asociado al dispositivo físico considerando en su configuración la selección de familias de colas para la generación de gráficos y su presentación.

///////////////////////////////////////////////////////////////////////////////////
/////                                                                         /////
/////                          Métodos públicos                               /////
/////                                                                         /////
///////////////////////////////////////////////////////////////////////////////////

//
// FUNCIÓN: GEGraphicsContext::GEGraphicsContext()
//
// PROPÓSITO: Crea un contexto gráfico de Vulkan (instancia, superficie y dispositivo)
//
GEGraphicsContext::GEGraphicsContext(GLFWwindow* window)
{
  createInstance();
  createSurface(window);
  // showInstanceProperties();
  pickPhysicalDevice();
  // showDevices();
  createLogicalDevice();

  std::cout << "Logical device created!" << std::endl;
}

//
// FUNCIÓN: GEGraphicsContext::~GEGraphicsContext()
//
// PROPÓSITO: Destruye el contexto gráfico
//
GEGraphicsContext::~GEGraphicsContext()
{
  vkDestroyDevice(device, nullptr);
  vkDestroySurfaceKHR(instance, surface, nullptr);
  vkDestroyInstance(instance, nullptr);
}

...

///////////////////////////////////////////////////////////////////////////////////
/////                                                                         /////
/////                 Métodos de inicialización de Vulkan                     /////
/////                                                                         /////
///////////////////////////////////////////////////////////////////////////////////

...

//
// FUNCIÓN: GEGraphicsContext::createLogicalDevice()
//
// PROPÓSITO: Crea el dispositivo lógico sobre el que generar los gráficos 
// y selecciona la familia de colas sobre el dispositivo
//
void GEGraphicsContext::createLogicalDevice()
{
  std::vector<VkDeviceQueueCreateInfo> queueCreateInfo;
  uint32_t queueCreateInfoCount;

  if (graphicsQueueFamilyIndex == presentQueueFamilyIndex)
  {
    VkDeviceQueueCreateInfo graphicsQueueCreateInfo = {};
    graphicsQueueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
    graphicsQueueCreateInfo.queueFamilyIndex = graphicsQueueFamilyIndex;
    graphicsQueueCreateInfo.queueCount = 1;
    float graphicsQueuePriority = 1.0f;
    graphicsQueueCreateInfo.pQueuePriorities = &graphicsQueuePriority;

    queueCreateInfoCount = 1;
    queueCreateInfo = { graphicsQueueCreateInfo };
  }
  else
  {
    VkDeviceQueueCreateInfo graphicsQueueCreateInfo = {};
    graphicsQueueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
    graphicsQueueCreateInfo.queueFamilyIndex = graphicsQueueFamilyIndex;
    graphicsQueueCreateInfo.queueCount = 1;
    float graphicsQueuePriority = 1.0f;
    graphicsQueueCreateInfo.pQueuePriorities = &graphicsQueuePriority;

    VkDeviceQueueCreateInfo presentQueueCreateInfo = {};
    presentQueueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
    presentQueueCreateInfo.queueFamilyIndex = presentQueueFamilyIndex;
    presentQueueCreateInfo.queueCount = 1;
    float presentQueuePriority = 1.0f;
    presentQueueCreateInfo.pQueuePriorities = &presentQueuePriority;

    queueCreateInfoCount = 2;
    queueCreateInfo = { graphicsQueueCreateInfo ,presentQueueCreateInfo };
  }

  VkDeviceCreateInfo createInfo = {};
  createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
  createInfo.pQueueCreateInfos = queueCreateInfo.data();
  createInfo.queueCreateInfoCount = queueCreateInfoCount;
  createInfo.enabledExtensionCount = 0;
  createInfo.enabledLayerCount = 0;

  std::vector<const char*> deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };

  VkPhysicalDeviceFeatures supportedFeatures = {};
  VkPhysicalDeviceFeatures requiredFeatures = {};
  vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
  requiredFeatures.multiDrawIndirect = supportedFeatures.multiDrawIndirect;
  requiredFeatures.tessellationShader = VK_TRUE;
  requiredFeatures.geometryShader = VK_TRUE;
  requiredFeatures.samplerAnisotropy = VK_TRUE; 
  createInfo.pEnabledFeatures = &requiredFeatures;

  createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
  createInfo.ppEnabledExtensionNames = deviceExtensions.data();

  if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS)
  {
    throw std::runtime_error("failed to create logical device!");
  }
}

 

 

Aspecto final

 

El aspecto de la aplicación muestra la consola con el mensaje de creación del dispositivo lógico.

Figura21