|
Grado en Ingeniería Informática
Animación por Ordenador
Curso 2025/2026
|
Práctica 2d
|
|
Creación de los buffers de comandos
|
|
Objetivo
|
|
Añade
la definición de los buffers de comandos para lanzar los procesos de
renderizado.
|
|
La estructura VkCommandPool
|
|
Para lanzar un proceso de renderizado (una vez configuradas todas las
estructuras anteriores) es necesario crear un buffer de comandos que
ejecutar sobre una cola del dispositivo lógico. El primer paso para
crear un buffer de comandos es generar un objeto VkCommandPool (un generador
de buffers de comandos). Para ello se utiliza la función vkCreateCommandPool().
VkResult vkCreateCommandPool(
VkDevice device,
const VkCommandPoolCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkCommandPool* pCommandPool);
|
Para destruir un objeto VkCommandPool se usa la función
vkDestroyCommandPool().
void vkDestroyCommandPool(
VkDevice device,
VkCommandPool commandPool,
const VkAllocationCallbacks* pAllocator);
|
La configuración del objeto VkCommandPool se introduce en una
estructura de tipo VkCommandPoolCreateInfo.
typedef struct VkCommandPoolCreateInfo {
VkStructureType sType;
const void* pNext;
VkCommandPoolCreateFlags flags;
uint32_t queueFamilyIndex;
} VkCommandPoolCreateInfo;
|
El campo sType debe tener el valor
VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO.
El campo pNext debe dejarse nulo.
El campo flags puede dejarse a nulo aunque existen definidos
otros tres bits que indican si los buffers de comandos a construir
tendrán una vida breve (VK_COMMAND_POOL_CREATE_TRANSIENT_BIT), si
pueden resetearse para ser reutilizados
(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) y si son
protegidos (VK_COMMAND_POOL_CREATE_PROTECTED_BIT).
El campo queueFamilyIndex se refiere a la familia de
colas sobre la que debe funcioinar los buffers de comandos a
gestionar por el objeto VkCommandPool .
|
|
La estructura VkCommandBuffer
|
|
Una vez creado el objeto VkCommandPool, se puede utilizar para crear buffers de comandos.
Para ello se utiliza la función vkAllocateCommandBuffers() que permite construir una lista de buffers
de comandos.
VkResult vkAllocateCommandBuffers(
VkDevice device,
const VkCommandBufferAllocateInfo* pAllocateInfo,
VkCommandBuffer* pCommandBuffers);
|
La configuración de esta función se realiza con una estructura
VkCommandBufferAllocateInfo.
typedef struct VkCommandBufferAllocateInfo {
VkStructureType sType;
const void* pNext;
VkCommandPool commandPool;
VkCommandBufferLevel level;
uint32_t commandBufferCount;
} VkCommandBufferAllocateInfo;
|
El campo sType debe tener el valor
VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO.
El campo pNext debe dejarse nulo.
El campo
commandPool es una referencia al objeto VkCommandPool
que generará los buffers de comandos .
El campo level indica si los buffers serán primarios (VK_COMMAND_BUFFER_LEVEL_PRIMARY)
o secundarios (VK_COMMAND_BUFFER_LEVEL_SECONDARY). Los buffers secundarios deben
ejecutarse desde buffers primarios.
El campo commandBufferCount indica el número de buffers a
crear.
|
|
La clase
GECommandContext
|
|
Para incorporar los buffers de comandos se
ha creado una nueva clase denominada GECommandContext que contiene
como campos las variables
commandPool y commandBuffers. El fichero de
cabecera tiene el siguiente contenido.
#pragma once
#include <vulkan/vulkan.h>
#include <vector>
#include "GEGraphicsContext.h"
class GECommandContext
{
public:
std::vector<VkCommandBuffer> commandBuffers;
GECommandContext(GEGraphicsContext* gc, uint32_t imageCount);
void destroy(GEGraphicsContext* gc);
private:
VkCommandPool commandPool;
// Métodos de inicialización
void createCommandPool(GEGraphicsContext* gc);
void createCommandBuffers(GEGraphicsContext* gc, uint32_t imageCount);
};
|
El código de GECommandContext incluye
los siguientes métodos:
-
GECommandContext(GEGraphicsContext* gc, uint32_t imageCount):
Constructor de la clase que inicializa sus campos.
-
destroy(GEGraphicsContext* gc): Destruye los campos de
la clase.
-
createCommandPool(GEGraphicsContext* gc): Crea el
objeto VkCommandPool
vinculado a la familia de colas gráficas del dispositivo.
-
createCommandBuffers(GEGraphicsContext* gc, uint32_t imageCount):
Crea un buffer de comandos para cada imagen a generar en la
swapchain.
El código de estos métodos es el siguiente.
#include "GECommandContext.h"
#include <iostream>
///////////////////////////////////////////////////////////////////////////////////
///// /////
///// Métodos públicos /////
///// /////
///////////////////////////////////////////////////////////////////////////////////
//
// FUNCIÓN: GECommandContext::GECommandContext(...)
//
// PROPÓSITO: Construye los buffers de comnandos
//
GECommandContext::GECommandContext(GEGraphicsContext* gc, uint32_t imageCount)
{
createCommandPool(gc);
createCommandBuffers(gc, imageCount);
std::cout << "Command buffers created!" << std::endl;
}
//
// FUNCIÓN: GECommandContext::destroy(GEGraphicsContext* gc)
//
// PROPÓSITO: Destruye los buffers de comnandos
//
void GECommandContext::destroy(GEGraphicsContext* gc)
{
size_t bufferCount = commandBuffers.size();
vkFreeCommandBuffers(gc->device, commandPool, bufferCount, commandBuffers.data());
vkDestroyCommandPool(gc->device, commandPool, nullptr);
}
///////////////////////////////////////////////////////////////////////////////////
///// /////
///// Métodos de creación de los componentes /////
///// /////
///////////////////////////////////////////////////////////////////////////////////
//
// FUNCIÓN: GECommandContext::createCommandPool(GEGraphicsContext* gc)
//
// PROPÓSITO: Crea el command pool vinculado a la familia de colas para gráficos
//
void GECommandContext::createCommandPool(GEGraphicsContext* gc)
{
VkCommandPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.queueFamilyIndex = gc->graphicsQueueFamilyIndex;
if(vkCreateCommandPool(gc->device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS)
{
throw std::runtime_error("failed to create command pool!");
}
}
//
// FUNCIÓN: GECommandContext::createCommandBuffers(...)
//
// PROPÓSITO: Crea los buffers de comandos que se enviarán a la cola gráfica
// El contenido de los buffers incluye la orden de dibujar.
//
void GECommandContext::createCommandBuffers(GEGraphicsContext* gc,
uint32_t imageCount)
{
commandBuffers.resize(imageCount);
VkCommandBufferAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = commandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = imageCount;
if (vkAllocateCommandBuffers(gc->device, &allocInfo, commandBuffers.data())
!= VK_SUCCESS)
{
throw std::runtime_error("failed to allocate command buffers!");
}
}
|
|
|
Modificaciones de la clase
GEApplication
|
|
La clase GEApplication debe incluir un objeto
GECommandContext para incorporar los buffers de comandos a la
aplicación gráfica. La cabecera de la clase queda así.
#pragma once
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include "GEWindowPosition.h"
#include "GEGraphicsContext.h"
#include "GEDrawingContext.h"
#include "GECommandContext.h"
const int WIDTH = 800;
const int HEIGHT = 600;
//
// CLASE: GEApplication
//
// DESCRIPCIÓN: Clase que crea y lanza la aplicación gráfica.
//
class GEApplication
{
public:
void run();
private:
GLFWwindow* window;
GEWindowPosition windowPos;
GEGraphicsContext* gc;
GEDrawingContext* dc;
GECommandContext* cc;
...
};
|
Los métodos modificados de GEApplication son run(),
para incluir la creación del objeto GECommandContext, y
cleanup(), para incluir la destrucción de este objeto.
//
// FUNCIÓN: GEApplication::run()
//
// PROPÓSITO: Ejecuta la aplicación
//
void GEApplication::run()
{
this->window = initWindow();
this->windowPos = initWindowPos();
this->gc = new GEGraphicsContext(window);
this->dc = new GEDrawingContext(this->gc, this->windowPos);
this->cc = new GECommandContext(this->gc, this->dc->getImageCount());
mainLoop();
cleanup();
}
//
// FUNCIÓN: GEApplication::cleanup()
//
// PROPÓSITO: Libera los recursos y finaliza la aplicación
//
void GEApplication::cleanup()
{
cc->destroy(gc);
dc->destroy(gc);
delete cc;
delete dc;
delete gc;
glfwDestroyWindow(window);
glfwTerminate();
}
|
|
|
Aspecto final
|
|
El
único cambio respecto a la aplicación es el mensaje presentado en
consola.
|
|