Related
I'm learning Vulkan following vulkan-tutorial.com.
I'm stuck because I can't figure out why I'm getting this error when creating the graphics pipeline.
VUID-VkPipelineShaderStageCreateInfo-module-parameter(ERROR / SPEC): msgNum: 0 - Invalid VkShaderModule Object 0x70000000007. The Vulkan spec states: module must be a valid VkShaderModule handle (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-VkPipelineShaderStageCreateInfo-module-parameter)
Objects: 1
[0] 0x70000000007, type: 15, name: NULL
VUID-VkPipelineShaderStageCreateInfo-module-parameter(ERROR / SPEC): msgNum: 0 - Invalid VkShaderModule Object 0x80000000008. The Vulkan spec states: module must be a valid VkShaderModule handle (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-VkPipelineShaderStageCreateInfo-module-parameter)
Objects: 1
[0] 0x80000000008, type: 15, name: NULL
This is how I create the shader modules:
auto createShaderModule = [](const char* fileName)
{
int len;
char* data = readBinFile(len, fileName);
VkShaderModuleCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
info.codeSize = len;
info.pCode = (u32*)data;
VkShaderModule module;
const VkResult res = vkCreateShaderModule(vk.device, &info, nullptr, &module);
if(res != VK_SUCCESS) {
printf("Error: could not create vertex shader module\n");
exit(-1);
}
delete[] data;
return module;
};
VkShaderModule vertShadModule = createShaderModule(SHADERS_PATH"/simple.vert.glsl.spv");
VkShaderModule fragShadModule = createShaderModule(SHADERS_PATH"/simple.frag.glsl.spv");
auto createStage = [](VkShaderStageFlagBits stage, VkShaderModule module)
{
VkPipelineShaderStageCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
info.stage = stage;
info.module = module;
info.pName = "main";
info.pSpecializationInfo = nullptr; // allows to specify values for shader constants
return info;
};
const VkPipelineShaderStageCreateInfo stagesInfos[] = {
createStage(VK_SHADER_STAGE_VERTEX_BIT, vertShadModule),
createStage(VK_SHADER_STAGE_FRAGMENT_BIT, fragShadModule)
};
As you can see, I'm checking that the shader modules where created ok (VK_SUCCESS). But still the validation layers say that the modules are invalid.
This is the whole code:
#include <stdio.h>
#include <vector>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <vulkan/vulkan.h>
typedef const char* const ConstStr;
typedef uint8_t u8;
typedef uint32_t u32;
typedef uint64_t u64;
GLFWwindow* window;
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
struct VulkanData
{
VkInstance inst;
VkSurfaceKHR surface;
VkPhysicalDevice physicalDevice;
VkDevice device;
u32 graphicsQueueFamily, presentationQueueFamily;
VkQueue graphicsQueue;
VkQueue presentationQueue;
VkSwapchainKHR swapchain;
VkImageView swapchainImageViews[2];
VkRenderPass renderPass;
VkPipeline pipeline;
} vk;
static VKAPI_ATTR VkBool32 VKAPI_CALL vulkanDebugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData) {
printf("%s\n", pCallbackData->pMessage);
return VK_FALSE;
}
void initVulkan()
{
{ // create vulkan instance
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pNext = nullptr;
appInfo.pApplicationName = "hello";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "hello_engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0);
VkInstanceCreateInfo instInfo = {};
instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instInfo.pNext = nullptr;
instInfo.flags = 0;
instInfo.pApplicationInfo = &appInfo;
static ConstStr layers[] = {
#if !defined(NDEBUG)
"VK_LAYER_KHRONOS_validation"
#endif
};
instInfo.enabledLayerCount = std::size(layers);
instInfo.ppEnabledLayerNames = layers;
u32 numGlfwExtensions;
ConstStr* glfwExtensions = glfwGetRequiredInstanceExtensions(&instInfo.enabledExtensionCount);
std::vector<const char*> extensions;
extensions.reserve(numGlfwExtensions + 1);
for(u32 i = 0; i < numGlfwExtensions; i++)
extensions.push_back(glfwExtensions[i]);
#ifndef NDEBUG
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
#endif
instInfo.ppEnabledExtensionNames = glfwGetRequiredInstanceExtensions(&instInfo.enabledExtensionCount);
if (vkCreateInstance(&instInfo, nullptr, &vk.inst) != VK_SUCCESS) {
printf("Error creating vulkan instance\n");
exit(-1);
}
}
// create window surface
if(glfwCreateWindowSurface(vk.inst, window, nullptr, &vk.surface) != VK_SUCCESS) {
printf("Error: can't create window surface\n");
exit(-1);
}
{ // pick physical device
u32 numPhysicalDevices;
vkEnumeratePhysicalDevices(vk.inst, &numPhysicalDevices, nullptr);
std::vector<VkPhysicalDevice> physicalDevices(numPhysicalDevices);
if(numPhysicalDevices == 0) {
printf("Error: there are no devices supporting Vulkan\n");
exit(-1);
}
vkEnumeratePhysicalDevices(vk.inst, &numPhysicalDevices, &physicalDevices[0]);
auto compareProps = [](
const VkPhysicalDeviceProperties& propsA,
const VkPhysicalDeviceMemoryProperties& memPropsA,
const VkPhysicalDeviceProperties& propsB,
const VkPhysicalDeviceMemoryProperties& memPropsB) -> bool
{
auto calcDeviceTypeScore = [](VkPhysicalDeviceType a) -> u8 {
switch(a) {
case VK_PHYSICAL_DEVICE_TYPE_CPU: return 1;
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: return 2;
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: return 3;
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: return 4;
default: return 0;
}
};
const u8 scoreA = calcDeviceTypeScore(propsA.deviceType);
const u8 scoreB = calcDeviceTypeScore(propsB.deviceType);
if(scoreA != scoreB)
return scoreA < scoreB;
auto calcMem = [](const VkPhysicalDeviceMemoryProperties& a) -> u64
{
u64 mem = 0;
for(u32 i = 0; i < a.memoryHeapCount; i++) {
if(a.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)
mem = std::max(mem, a.memoryHeaps[i].size);
}
return mem;
};
u32 memA = calcMem(memPropsA);
u32 memB = calcMem(memPropsB);
return memA < memB;
};
VkPhysicalDeviceProperties bestProps;
vkGetPhysicalDeviceProperties(physicalDevices[0], &bestProps);
VkPhysicalDeviceMemoryProperties bestMemProps;
vkGetPhysicalDeviceMemoryProperties(physicalDevices[0], &bestMemProps);
u32 bestI = 0;
for(u32 i = 1; i < numPhysicalDevices; i++) {
VkPhysicalDeviceProperties props;
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceProperties(physicalDevices[i], &props);
vkGetPhysicalDeviceMemoryProperties(physicalDevices[i], &memProps);
if(compareProps(bestProps, bestMemProps, props, memProps)) {
bestProps = props;
bestMemProps = memProps;
bestI = i;
}
}
vk.physicalDevice = physicalDevices[bestI];
}
{ // create logical device
u32 numQueueFamilies;
vkGetPhysicalDeviceQueueFamilyProperties(vk.physicalDevice, &numQueueFamilies, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilyProps(numQueueFamilies);
vkGetPhysicalDeviceQueueFamilyProperties(vk.physicalDevice, &numQueueFamilies, &queueFamilyProps[0]);
vk.graphicsQueueFamily = numQueueFamilies;
vk.presentationQueueFamily = numQueueFamilies;
for(u32 i = 0; i < numQueueFamilies; i++) {
if(queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
vk.graphicsQueueFamily = i;
VkBool32 supportPresentation;
vkGetPhysicalDeviceSurfaceSupportKHR(vk.physicalDevice, i, vk.surface, &supportPresentation);
if(supportPresentation)
vk.presentationQueueFamily = i;
}
if(vk.graphicsQueueFamily == numQueueFamilies) {
printf("Error: there is no queue that supports graphics\n");
exit(-1);
}
if(vk.presentationQueueFamily == numQueueFamilies) {
printf("Error: there is no queue that supports presentation\n");
exit(-1);
}
u32 queueFamilyInds[2] = {vk.graphicsQueueFamily};
u32 numQueues;
if(vk.graphicsQueueFamily == vk.presentationQueueFamily) {
numQueues = 1;
}
else {
numQueues = 2;
queueFamilyInds[1] = vk.graphicsQueueFamily;
}
const float queuePriorities[] = {1.f};
VkDeviceQueueCreateInfo queueCreateInfos[2] = {};
for(u32 i = 0; i < numQueues; i++)
{
queueCreateInfos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfos[i].queueFamilyIndex = queueFamilyInds[i];
queueCreateInfos[i].queueCount = 1;
queueCreateInfos[i].pQueuePriorities = queuePriorities;
}
VkDeviceCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
info.queueCreateInfoCount = numQueues;
info.pQueueCreateInfos = queueCreateInfos;
info.enabledLayerCount = 0;
info.ppEnabledLayerNames = nullptr;
ConstStr deviceExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
info.enabledExtensionCount = std::size(deviceExtensions);
info.ppEnabledExtensionNames = deviceExtensions;
const VkResult deviceCreatedOk = vkCreateDevice(vk.physicalDevice, &info, nullptr, &vk.device);
if(deviceCreatedOk != VK_SUCCESS) {
printf("Error: couldn't create device\n");
exit(-1);
}
vkGetDeviceQueue(vk.device, vk.graphicsQueueFamily, 0, &vk.graphicsQueue);
vkGetDeviceQueue(vk.device, vk.presentationQueueFamily, 0, &vk.presentationQueue);
// queues
// https://community.khronos.org/t/guidelines-for-selecting-queues-and-families/7222
// https://www.reddit.com/r/vulkan/comments/aara8f/best_way_for_selecting_queuefamilies/
// https://stackoverflow.com/questions/37575012/should-i-try-to-use-as-many-queues-as-possible
}
{ // crete swapchain
VkSurfaceCapabilitiesKHR surfaceCpabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk.physicalDevice, vk.surface, &surfaceCpabilities);
VkSwapchainCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = vk.surface;
createInfo.minImageCount = 2;
createInfo.imageFormat = VK_FORMAT_B8G8R8A8_SRGB; // TODO: I think this format has mandatory support but I'm not sure
createInfo.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
createInfo.imageExtent = surfaceCpabilities.currentExtent;
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 1;
createInfo.pQueueFamilyIndices = &vk.presentationQueueFamily;
createInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
createInfo.clipped = VK_FALSE;
//createInfo.oldSwapchain = ; // this can be used to recycle the old swapchain when resizing the window
vkCreateSwapchainKHR(vk.device, &createInfo, nullptr, &vk.swapchain);
}
{ // create image views of the swapchain
u32 imageCount;
VkImage images[2];
vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &imageCount, nullptr);
assert(imageCount == 2);
vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &imageCount, images);
for(u32 i = 0; i < 2; i++)
{
VkImageViewCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.image = images[0];
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
info.format = VK_FORMAT_B8G8R8A8_SRGB;
// info.components = ; // channel swizzling VK_COMPONENT_SWIZZLE_IDENTITY is 0
info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
info.subresourceRange.baseMipLevel = 0;
info.subresourceRange.levelCount = 1;
info.subresourceRange.baseArrayLayer = 0;
info.subresourceRange.layerCount = 1;
vkCreateImageView(vk.device, &info, nullptr, &vk.swapchainImageViews[i]);
}
}
}
#define SHADERS_PATH "shaders"
char* readBinFile(int& len, const char* fileName)
{
FILE* file = fopen(fileName, "rb");
fseek(file, 0, SEEK_END);
len = ftell(file);
rewind(file);
char* txt = new char[len];
fread(txt, len, 1, file);
fclose(file);
return txt;
}
void createTheGraphicsPipeline()
{
auto createShaderModule = [](const char* fileName)
{
int len;
char* data = readBinFile(len, fileName);
VkShaderModuleCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
info.codeSize = len;
info.pCode = (u32*)data;
VkShaderModule module;
const VkResult res = vkCreateShaderModule(vk.device, &info, nullptr, &module);
if(res != VK_SUCCESS) {
printf("Error: could not create vertex shader module\n");
exit(-1);
}
delete[] data;
return module;
};
VkShaderModule vertShadModule = createShaderModule(SHADERS_PATH"/simple.vert.glsl.spv");
VkShaderModule fragShadModule = createShaderModule(SHADERS_PATH"/simple.frag.glsl.spv");
auto createStage = [](VkShaderStageFlagBits stage, VkShaderModule module)
{
VkPipelineShaderStageCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
info.stage = stage;
info.module = module;
info.pName = "main";
info.pSpecializationInfo = nullptr; // allows to specify values for shader constants
return info;
};
const VkPipelineShaderStageCreateInfo stagesInfos[] = {
createStage(VK_SHADER_STAGE_VERTEX_BIT, vertShadModule),
createStage(VK_SHADER_STAGE_FRAGMENT_BIT, fragShadModule)
};
VkPipelineVertexInputStateCreateInfo vertInputInfo = {};
vertInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertInputInfo.vertexBindingDescriptionCount = 0;
vertInputInfo.pVertexBindingDescriptions = nullptr;
vertInputInfo.vertexAttributeDescriptionCount = 0;
vertInputInfo.pVertexAttributeDescriptions = nullptr;
VkPipelineInputAssemblyStateCreateInfo assemblyInfo = {};
assemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
assemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
assemblyInfo.primitiveRestartEnable = VK_FALSE;
VkViewport viewport;
viewport.x = 0;
viewport.y = 0;
viewport.width = WINDOW_WIDTH;
viewport.height = WINDOW_HEIGHT;
viewport.minDepth = 0;
viewport.maxDepth = 1;
VkRect2D scissor;
scissor.offset = {0, 0};
scissor.extent = {WINDOW_WIDTH, WINDOW_HEIGHT};
VkPipelineViewportStateCreateInfo viewportInfo = {};
viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportInfo.viewportCount = 1;
viewportInfo.pViewports = &viewport;
viewportInfo.scissorCount = 1;
viewportInfo.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizerInfo = {};
rasterizerInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizerInfo.depthClampEnable = VK_FALSE; // enabling will clamp depth instead of discarding which can be useful when rendering shadowmaps
rasterizerInfo.rasterizerDiscardEnable = VK_FALSE; // if enable discards all geometry (could be useful for transformfeedback)
rasterizerInfo.polygonMode = VK_POLYGON_MODE_FILL;
rasterizerInfo.lineWidth = 1.f;
rasterizerInfo.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizerInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizerInfo.depthBiasEnable = VK_FALSE; // useful for shadow mapping
VkPipelineMultisampleStateCreateInfo multisampleInfo = {}; // multisampling: it works by combining the fragment shader results of multiple polygons that rasterize to the same pixel
multisampleInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleInfo.sampleShadingEnable = VK_FALSE;
multisampleInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
//VkPipelineDepthStencilStateCreateInfo depthStencilInfo = {};
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_G_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlendInfo = {};
colorBlendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlendInfo.attachmentCount = 1;
colorBlendInfo.pAttachments = &colorBlendAttachment;
const VkDynamicState dynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicStateInfo = {};
dynamicStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicStateInfo.dynamicStateCount = 2;
dynamicStateInfo.pDynamicStates = dynamicStates;
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
VkPipelineLayout pipelineLayout;
vkCreatePipelineLayout(vk.device, &pipelineLayoutInfo, nullptr, &pipelineLayout);
vkDestroyShaderModule(vk.device, fragShadModule, nullptr);
vkDestroyShaderModule(vk.device, vertShadModule, nullptr);
// -- create the renderPass ---
VkAttachmentDescription attachmentDesc = {};
attachmentDesc.format = VK_FORMAT_B8G8R8A8_SRGB;
attachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT;
attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // clear before rendering
attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; // store the result after rendering
attachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachmentDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // we don't care. This doesn't guarantee that the contents of th eimage will be preserved, but that's not a problem since we are going to clear it anyways
attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0; // the index in the attachemntDescs array (we only have one)
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // this is an output attachment so we use this enum for best performance
VkSubpassDescription subpassDesc = {};
subpassDesc.colorAttachmentCount = 1;
subpassDesc.pColorAttachments = &colorAttachmentRef; // the index of the attachement in this array is referenced in the shader with "layout(location = 0) out vec4 o_color"
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &attachmentDesc;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpassDesc;
if(vkCreateRenderPass(vk.device, &renderPassInfo, nullptr, &vk.renderPass) != VK_SUCCESS) {
printf("Error creating the renderPass\n");
exit(-1);
}
// -- finally create the pipeline
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = stagesInfos;
pipelineInfo.pVertexInputState = &vertInputInfo;
pipelineInfo.pInputAssemblyState = &assemblyInfo;
pipelineInfo.pViewportState = &viewportInfo;
pipelineInfo.pRasterizationState = &rasterizerInfo;
pipelineInfo.pMultisampleState = &multisampleInfo;
pipelineInfo.pDepthStencilState = nullptr;
pipelineInfo.pColorBlendState = &colorBlendInfo;
pipelineInfo.pDynamicState = &dynamicStateInfo;
pipelineInfo.layout = pipelineLayout;
pipelineInfo.renderPass = vk.renderPass; // render pass describing the enviroment in which the pipeline will be used
// the pipeline must only be used with a render pass compatilble with this one
pipelineInfo.subpass = 0; // index of the subpass in the render pass where this pipeline will be used
//pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // you can derive from another pipeline
//pipelineInfo.basePipelineIndex = -1;
if(vkCreateGraphicsPipelines(vk.device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &vk.pipeline) != VK_SUCCESS) {
printf("Error creating the graphics piepeline\n");
exit(-1);
}
}
int main()
{
printf("hello vulkan\n");
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // explicitly tell GLFW not to create an OpenGL context
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "hello vulkan", nullptr, nullptr);
initVulkan();
createTheGraphicsPipeline();
while(!glfwWindowShouldClose(window))
{
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
}
simple.vert.glsl:
#version 450
vec2 positions[3] = vec2[](
vec2(0.0, -0.5),
vec2(0.5, 0.5),
vec2(-0.5, 0.5)
);
const vec3 colors[3] = vec3[](
vec3(1, 0, 0),
vec3(0, 1, 0),
vec3(0, 0, 1)
);
layout (location = 0)out vec3 v_color;
void main()
{
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
v_color = colors[gl_VertexIndex];
}
simple.frag.glsl
#version 450
layout(location = 0) in vec3 v_color;
layout(location = 0) out vec4 o_color;
void main() {
o_color = vec4(v_color, 1.0);
}
I finally found the problem: I was destroying the shader modules too early. Looks like you have to keep the shader modules alive ultil after you have created the pipeline.
This is the fixed code
#include <stdio.h>
#include <string.h>
#include <vector>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <vulkan/vulkan.h>
typedef const char* const ConstStr;
typedef uint8_t u8;
typedef uint32_t u32;
typedef uint64_t u64;
GLFWwindow* window;
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
struct VulkanData
{
VkInstance inst;
VkSurfaceKHR surface;
VkPhysicalDevice physicalDevice;
VkDevice device;
u32 graphicsQueueFamily, presentationQueueFamily;
VkQueue graphicsQueue;
VkQueue presentationQueue;
VkSwapchainKHR swapchain;
VkImageView swapchainImageViews[2];
VkRenderPass renderPass;
VkPipeline pipeline;
} vk;
static VKAPI_ATTR VkBool32 VKAPI_CALL vulkanDebugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData) {
printf("%s\n", pCallbackData->pMessage);
return VK_FALSE;
}
void initVulkan()
{
{ // create vulkan instance
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pNext = nullptr;
appInfo.pApplicationName = "hello";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "hello_engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_0;
static ConstStr layers[] = {
#if !defined(NDEBUG)
"VK_LAYER_KHRONOS_validation",
//"VK_LAYER_LUNARG_api_dump",
#endif
};
#if !defined(NDEBUG)
u32 numSupportedLayers;
vkEnumerateInstanceLayerProperties(&numSupportedLayers, nullptr);
std::vector<VkLayerProperties> supportedLayers(numSupportedLayers);
vkEnumerateInstanceLayerProperties(&numSupportedLayers, &supportedLayers[0]);
for(ConstStr layer : layers) {
bool supported = false;
for(const auto& supportedLayer : supportedLayers) {
if(strcmp(supportedLayer.layerName, layer) == 0) {
supported = true;
break;
}
}
if(!supported) {
printf("Layer %s is not supported\n", layer);
//assert(false);
}
}
#endif
VkInstanceCreateInfo instInfo = {};
instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instInfo.pApplicationInfo = &appInfo;
instInfo.enabledLayerCount = std::size(layers);
instInfo.ppEnabledLayerNames = layers;
u32 numGlfwExtensions;
ConstStr* glfwExtensions = glfwGetRequiredInstanceExtensions(&numGlfwExtensions);
std::vector<const char*> extensions;
extensions.reserve(numGlfwExtensions + 1);
for(u32 i = 0; i < numGlfwExtensions; i++)
extensions.push_back(glfwExtensions[i]);
#ifndef NDEBUG
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
#endif
instInfo.enabledExtensionCount = extensions.size();
instInfo.ppEnabledExtensionNames = extensions.data();
if (vkCreateInstance(&instInfo, nullptr, &vk.inst) != VK_SUCCESS) {
printf("Error creating vulkan instance\n");
exit(-1);
}
}
// create window surface
if(glfwCreateWindowSurface(vk.inst, window, nullptr, &vk.surface) != VK_SUCCESS) {
printf("Error: can't create window surface\n");
exit(-1);
}
{ // pick physical device
u32 numPhysicalDevices;
vkEnumeratePhysicalDevices(vk.inst, &numPhysicalDevices, nullptr);
std::vector<VkPhysicalDevice> physicalDevices(numPhysicalDevices);
if(numPhysicalDevices == 0) {
printf("Error: there are no devices supporting Vulkan\n");
exit(-1);
}
vkEnumeratePhysicalDevices(vk.inst, &numPhysicalDevices, &physicalDevices[0]);
auto compareProps = [](
const VkPhysicalDeviceProperties& propsA,
const VkPhysicalDeviceMemoryProperties& memPropsA,
const VkPhysicalDeviceProperties& propsB,
const VkPhysicalDeviceMemoryProperties& memPropsB) -> bool
{
auto calcDeviceTypeScore = [](VkPhysicalDeviceType a) -> u8 {
switch(a) {
case VK_PHYSICAL_DEVICE_TYPE_CPU: return 1;
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: return 2;
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: return 3;
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: return 4;
default: return 0;
}
};
const u8 scoreA = calcDeviceTypeScore(propsA.deviceType);
const u8 scoreB = calcDeviceTypeScore(propsB.deviceType);
if(scoreA != scoreB)
return scoreA < scoreB;
auto calcMem = [](const VkPhysicalDeviceMemoryProperties& a) -> u64
{
u64 mem = 0;
for(u32 i = 0; i < a.memoryHeapCount; i++) {
if(a.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)
mem = std::max(mem, a.memoryHeaps[i].size);
}
return mem;
};
u32 memA = calcMem(memPropsA);
u32 memB = calcMem(memPropsB);
return memA < memB;
};
VkPhysicalDeviceProperties bestProps;
vkGetPhysicalDeviceProperties(physicalDevices[0], &bestProps);
VkPhysicalDeviceMemoryProperties bestMemProps;
vkGetPhysicalDeviceMemoryProperties(physicalDevices[0], &bestMemProps);
u32 bestI = 0;
for(u32 i = 1; i < numPhysicalDevices; i++) {
VkPhysicalDeviceProperties props;
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceProperties(physicalDevices[i], &props);
vkGetPhysicalDeviceMemoryProperties(physicalDevices[i], &memProps);
if(compareProps(bestProps, bestMemProps, props, memProps)) {
bestProps = props;
bestMemProps = memProps;
bestI = i;
}
}
vk.physicalDevice = physicalDevices[bestI];
}
{ // create logical device
u32 numQueueFamilies;
vkGetPhysicalDeviceQueueFamilyProperties(vk.physicalDevice, &numQueueFamilies, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilyProps(numQueueFamilies);
vkGetPhysicalDeviceQueueFamilyProperties(vk.physicalDevice, &numQueueFamilies, &queueFamilyProps[0]);
vk.graphicsQueueFamily = numQueueFamilies;
vk.presentationQueueFamily = numQueueFamilies;
for(u32 i = 0; i < numQueueFamilies; i++) {
if(queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
vk.graphicsQueueFamily = i;
VkBool32 supportPresentation;
vkGetPhysicalDeviceSurfaceSupportKHR(vk.physicalDevice, i, vk.surface, &supportPresentation);
if(supportPresentation)
vk.presentationQueueFamily = i;
}
if(vk.graphicsQueueFamily == numQueueFamilies) {
printf("Error: there is no queue that supports graphics\n");
exit(-1);
}
if(vk.presentationQueueFamily == numQueueFamilies) {
printf("Error: there is no queue that supports presentation\n");
exit(-1);
}
u32 queueFamilyInds[2] = {vk.graphicsQueueFamily};
u32 numQueues;
if(vk.graphicsQueueFamily == vk.presentationQueueFamily) {
numQueues = 1;
}
else {
numQueues = 2;
queueFamilyInds[1] = vk.graphicsQueueFamily;
}
const float queuePriorities[] = {1.f};
VkDeviceQueueCreateInfo queueCreateInfos[2] = {};
for(u32 i = 0; i < numQueues; i++)
{
queueCreateInfos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfos[i].queueFamilyIndex = queueFamilyInds[i];
queueCreateInfos[i].queueCount = 1;
queueCreateInfos[i].pQueuePriorities = queuePriorities;
}
VkDeviceCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
info.queueCreateInfoCount = numQueues;
info.pQueueCreateInfos = queueCreateInfos;
info.enabledLayerCount = 0;
info.ppEnabledLayerNames = nullptr;
ConstStr deviceExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
info.enabledExtensionCount = std::size(deviceExtensions);
info.ppEnabledExtensionNames = deviceExtensions;
const VkResult deviceCreatedOk = vkCreateDevice(vk.physicalDevice, &info, nullptr, &vk.device);
if(deviceCreatedOk != VK_SUCCESS) {
printf("Error: couldn't create device\n");
exit(-1);
}
vkGetDeviceQueue(vk.device, vk.graphicsQueueFamily, 0, &vk.graphicsQueue);
vkGetDeviceQueue(vk.device, vk.presentationQueueFamily, 0, &vk.presentationQueue);
// queues
// https://community.khronos.org/t/guidelines-for-selecting-queues-and-families/7222
// https://www.reddit.com/r/vulkan/comments/aara8f/best_way_for_selecting_queuefamilies/
// https://stackoverflow.com/questions/37575012/should-i-try-to-use-as-many-queues-as-possible
}
{ // crete swapchain
VkSurfaceCapabilitiesKHR surfaceCpabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk.physicalDevice, vk.surface, &surfaceCpabilities);
VkSwapchainCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = vk.surface;
createInfo.minImageCount = 2;
createInfo.imageFormat = VK_FORMAT_B8G8R8A8_SRGB; // TODO: I think this format has mandatory support but I'm not sure
createInfo.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
createInfo.imageExtent = surfaceCpabilities.currentExtent;
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 1;
createInfo.pQueueFamilyIndices = &vk.presentationQueueFamily;
createInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
createInfo.clipped = VK_FALSE;
//createInfo.oldSwapchain = ; // this can be used to recycle the old swapchain when resizing the window
vkCreateSwapchainKHR(vk.device, &createInfo, nullptr, &vk.swapchain);
}
{ // create image views of the swapchain
u32 imageCount;
VkImage images[2];
vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &imageCount, nullptr);
assert(imageCount == 2);
vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &imageCount, images);
for(u32 i = 0; i < 2; i++)
{
VkImageViewCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.image = images[0];
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
info.format = VK_FORMAT_B8G8R8A8_SRGB;
// info.components = ; // channel swizzling VK_COMPONENT_SWIZZLE_IDENTITY is 0
info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
info.subresourceRange.baseMipLevel = 0;
info.subresourceRange.levelCount = 1;
info.subresourceRange.baseArrayLayer = 0;
info.subresourceRange.layerCount = 1;
vkCreateImageView(vk.device, &info, nullptr, &vk.swapchainImageViews[i]);
}
}
}
#define SHADERS_PATH "shaders"
char* readBinFile(int& len, const char* fileName)
{
FILE* file = fopen(fileName, "rb");
if(!file)
return nullptr;
fseek(file, 0, SEEK_END);
len = ftell(file);
rewind(file);
char* txt = new char[len];
fread(txt, len, 1, file);
fclose(file);
return txt;
}
VkShaderModule createShaderModule(const char* fileName)
{
int len;
char* data = readBinFile(len, fileName);
if(!data) {
printf("Error loading shader spir-v\n");
exit(-1);
}
VkShaderModuleCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
info.codeSize = len;
info.pCode = (u32*)data;
VkShaderModule module;
const VkResult res = vkCreateShaderModule(vk.device, &info, nullptr, &module);
if(res != VK_SUCCESS) {
printf("Error: could not create vertex shader module\n");
exit(-1);
}
delete[] data;
return module;
}
VkPipelineShaderStageCreateInfo makeStageCreateInfo(VkShaderStageFlagBits stage, VkShaderModule module)
{
VkPipelineShaderStageCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
info.stage = stage;
info.module = module;
info.pName = "main";
info.pSpecializationInfo = nullptr; // allows to specify values for shader constants
return info;
}
void createTheGraphicsPipeline()
{
VkShaderModule vertShadModule = createShaderModule(SHADERS_PATH"/simple.vert.glsl.spv");
VkShaderModule fragShadModule = createShaderModule(SHADERS_PATH"/simple.frag.glsl.spv");
const VkPipelineShaderStageCreateInfo stagesInfos[] = {
makeStageCreateInfo(VK_SHADER_STAGE_VERTEX_BIT, vertShadModule),
makeStageCreateInfo(VK_SHADER_STAGE_FRAGMENT_BIT, fragShadModule)
};
VkPipelineVertexInputStateCreateInfo vertInputInfo = {};
vertInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertInputInfo.vertexBindingDescriptionCount = 0;
vertInputInfo.pVertexBindingDescriptions = nullptr;
vertInputInfo.vertexAttributeDescriptionCount = 0;
vertInputInfo.pVertexAttributeDescriptions = nullptr;
VkPipelineInputAssemblyStateCreateInfo assemblyInfo = {};
assemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
assemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
assemblyInfo.primitiveRestartEnable = VK_FALSE;
VkViewport viewport;
viewport.x = 0;
viewport.y = 0;
viewport.width = WINDOW_WIDTH;
viewport.height = WINDOW_HEIGHT;
viewport.minDepth = 0;
viewport.maxDepth = 1;
VkRect2D scissor;
scissor.offset = {0, 0};
scissor.extent = {WINDOW_WIDTH, WINDOW_HEIGHT};
VkPipelineViewportStateCreateInfo viewportInfo = {};
viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportInfo.viewportCount = 1;
viewportInfo.pViewports = &viewport;
viewportInfo.scissorCount = 1;
viewportInfo.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizerInfo = {};
rasterizerInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizerInfo.depthClampEnable = VK_FALSE; // enabling will clamp depth instead of discarding which can be useful when rendering shadowmaps
rasterizerInfo.rasterizerDiscardEnable = VK_FALSE; // if enable discards all geometry (could be useful for transformfeedback)
rasterizerInfo.polygonMode = VK_POLYGON_MODE_FILL;
rasterizerInfo.lineWidth = 1.f;
rasterizerInfo.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizerInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizerInfo.depthBiasEnable = VK_FALSE; // useful for shadow mapping
VkPipelineMultisampleStateCreateInfo multisampleInfo = {}; // multisampling: it works by combining the fragment shader results of multiple polygons that rasterize to the same pixel
multisampleInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleInfo.sampleShadingEnable = VK_FALSE;
multisampleInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
//VkPipelineDepthStencilStateCreateInfo depthStencilInfo = {};
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_G_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlendInfo = {};
colorBlendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
//colorBlendInfo.logicOpEnable = VK_FALSE;
//colorBlendInfo.logicOp = VK_LOGIC_OP_COPY;
colorBlendInfo.attachmentCount = 1;
colorBlendInfo.pAttachments = &colorBlendAttachment;
//const VkDynamicState dynamicStates[] = {
// VK_DYNAMIC_STATE_VIEWPORT,
// VK_DYNAMIC_STATE_SCISSOR
//};
//VkPipelineDynamicStateCreateInfo dynamicStateInfo = {};
//dynamicStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
//dynamicStateInfo.dynamicStateCount = 2;
//dynamicStateInfo.pDynamicStates = dynamicStates;
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
VkPipelineLayout pipelineLayout;
vkCreatePipelineLayout(vk.device, &pipelineLayoutInfo, nullptr, &pipelineLayout);
// -- create the renderPass ---
VkAttachmentDescription attachmentDesc = {};
attachmentDesc.format = VK_FORMAT_B8G8R8A8_SRGB;
attachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT;
attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // clear before rendering
attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; // store the result after rendering
attachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachmentDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // we don't care. This doesn't guarantee that the contents of th eimage will be preserved, but that's not a problem since we are going to clear it anyways
attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0; // the index in the attachemntDescs array (we only have one)
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // this is an output attachment so we use this enum for best performance
VkSubpassDescription subpassDesc = {};
subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDesc.colorAttachmentCount = 1;
subpassDesc.pColorAttachments = &colorAttachmentRef; // the index of the attachement in this array is referenced in the shader with "layout(location = 0) out vec4 o_color"
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &attachmentDesc;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpassDesc;
if(vkCreateRenderPass(vk.device, &renderPassInfo, nullptr, &vk.renderPass) != VK_SUCCESS) {
printf("Error creating the renderPass\n");
exit(-1);
}
// -- finally create the pipeline
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = stagesInfos;
pipelineInfo.pVertexInputState = &vertInputInfo;
pipelineInfo.pInputAssemblyState = &assemblyInfo;
pipelineInfo.pViewportState = &viewportInfo;
pipelineInfo.pRasterizationState = &rasterizerInfo;
pipelineInfo.pMultisampleState = &multisampleInfo;
pipelineInfo.pDepthStencilState = nullptr;
pipelineInfo.pColorBlendState = &colorBlendInfo;
//pipelineInfo.pDynamicState = &dynamicStateInfo;
pipelineInfo.layout = pipelineLayout;
pipelineInfo.renderPass = vk.renderPass; // render pass describing the enviroment in which the pipeline will be used
// the pipeline must only be used with a render pass compatilble with this one
pipelineInfo.subpass = 0; // index of the subpass in the render pass where this pipeline will be used
//pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // you can derive from another pipeline
//pipelineInfo.basePipelineIndex = -1;
getchar();
if(vkCreateGraphicsPipelines(vk.device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &vk.pipeline) != VK_SUCCESS) {
printf("Error creating the graphics piepeline\n");
exit(-1);
}
vkDestroyShaderModule(vk.device, fragShadModule, nullptr);
vkDestroyShaderModule(vk.device, vertShadModule, nullptr);
}
int main()
{
printf("hello vulkan\n");
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // explicitly tell GLFW not to create an OpenGL context
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "hello vulkan", nullptr, nullptr);
initVulkan();
createTheGraphicsPipeline();
while(!glfwWindowShouldClose(window))
{
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
}
I'm trying to clear the the screen with a color, but I'm always getting an error
"Cannot submit cmd buffer using image (...) with layout VK_IMAGE_LAYOUT_UNDEFINED when first use is VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL.
And Actully i tried to modify some undefine values to depth_stenci_attachment optimal, but The only thing is I get is more of these errors. So is there a field,that is incorrectly filled, or I forget to fill?
So here is my full main.cpp Because I have no idea where the error could be.
#include<stdio.h>
#include<string.h>
#include<vector>
#define DEBUG
#ifdef _WIN32
#define VK_USE_PLATFORM_WIN32_KHR
#endif
#define KNOCH_JULIA 42
#include"window.h"
using namespace std;
#ifdef DEBUG
#include<iostream>
using namespace std;
VkDebugReportCallbackEXT report;
void init_debug(vulkan *vulk);
PFN_vkCreateDebugReportCallbackEXT fvkCreateDebugReportCallbackEXT = VK_NULL_HANDLE;
PFN_vkDestroyDebugReportCallbackEXT fvkDestroyDebugReportCallbackEXT = VK_NULL_HANDLE;
VKAPI_ATTR VkBool32 VKAPI_CALL callback(VkDebugReportFlagsEXT flag, VkDebugReportObjectTypeEXT obj_t, uint64_t src_obj, size_t loc, int32_t msg_code, const char* layer_pref, const char* msg, void* user_data) {
switch (flag) {
case VK_DEBUG_REPORT_ERROR_BIT_EXT:
cout<<"error!"<<" "<< flag<<" source:"<<src_obj<<"location: "<<loc<<": "<< msg<<endl;
break;
case VK_DEBUG_REPORT_WARNING_BIT_EXT:
cout << "warning!" << obj_t << ": " << msg << endl;
break;
}
return false;
}
void init_debug(vulkan *vulk) {
fvkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(vulk->inst, "vkCreateDebugReportCallbackEXT");
fvkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(vulk->inst, "vkDestroyDebugReportCallbackEXT");
if (nullptr == fvkCreateDebugReportCallbackEXT || nullptr == fvkDestroyDebugReportCallbackEXT) {
exit(-5);
}
VkDebugReportCallbackCreateInfoEXT info = {};
info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
info.pfnCallback = callback;
info.pNext = nullptr;
info.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT;
fvkCreateDebugReportCallbackEXT(vulk->inst, &info, nullptr, &report);
}
#endif
FILE *fileptr;
void initInstance(vulkan *vulk){
vector<char*> ext;
ext.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
ext.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
VkApplicationInfo app_info = {};
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
app_info.apiVersion = VK_MAKE_VERSION(1, 0, 39);
app_info.engineVersion = VK_MAKE_VERSION(0, 0, 1);
app_info.pApplicationName = "szar";
app_info.pEngineName = "yayitstarts";
app_info.pNext = nullptr;
VkInstanceCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
#ifdef DEBUG
vector<char*>layers;
layers.push_back("VK_LAYER_LUNARG_object_tracker");
layers.push_back("VK_LAYER_LUNARG_core_validation");
layers.push_back("VK_LAYER_LUNARG_parameter_validation");
//layers.push_back("VK_LAYER_LUNARG_vktrace");
layers.push_back("VK_LAYER_LUNARG_swapchain");
layers.push_back("VK_LAYER_LUNARG_image");
ext.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
#endif
#ifdef DEBUG
info.enabledLayerCount = layers.size();
info.ppEnabledLayerNames = layers.data();
#else
info.enabledLayerCount = 0;
info.ppEnabledLayerNames = nullptr;
#endif
info.pApplicationInfo = &app_info;
info.enabledExtensionCount = ext.size();
info.ppEnabledExtensionNames = ext.data();
info.flags = 0;
info.pNext = nullptr;
vkCreateInstance(&info, nullptr, &(vulk->inst));
}
void getGPU(vulkan *vulk) {
uint32_t dev_c=0;
vkEnumeratePhysicalDevices(vulk->inst,&dev_c,nullptr);
VkPhysicalDevice *gpus=(VkPhysicalDevice*)malloc(sizeof(VkPhysicalDevice)*dev_c);
vkEnumeratePhysicalDevices(vulk->inst, &dev_c, gpus);
vulk->gpu = gpus[0];
}
void createDevice(vulkan *vulk) {
VkPhysicalDeviceFeatures features;
vkGetPhysicalDeviceFeatures(vulk->gpu, &features);
float prior[] = { 1.0f };
uint32_t prop_c;
vkGetPhysicalDeviceQueueFamilyProperties(vulk->gpu, &prop_c, nullptr);
VkQueueFamilyProperties *props = (VkQueueFamilyProperties*)malloc(sizeof(VkQueueFamilyProperties)*prop_c);
vkGetPhysicalDeviceQueueFamilyProperties(vulk->gpu, &prop_c, props);
uint32_t index = -1;
for (int i = 0; i < prop_c; i++) {
VkBool32 supported;
vkGetPhysicalDeviceSurfaceSupportKHR(vulk->gpu, i, vulk->surface_struct.surface, &supported);
if (props[i].queueFlags&VK_QUEUE_GRAPHICS_BIT&&supported) {
index = i;
}
}
if (index == -1) {
printf("no graphic queue family found");
exit(-1);
}
#ifdef DEBUG
uint32_t count;
vkEnumerateInstanceLayerProperties(&count, nullptr);
vector<VkLayerProperties>layers_access(count);
vkEnumerateInstanceLayerProperties(&count, layers_access.data());
for (int i = 0; i < count; i++) {
printf("%s\n", layers_access[i].layerName);
}
uint32_t dev_count;
vkEnumerateDeviceLayerProperties(vulk->gpu, &dev_count, nullptr);
vector<VkLayerProperties>layers_access_dev(count);
vkEnumerateDeviceLayerProperties(vulk->gpu, &dev_count, layers_access.data());
for (int i = 0; i < dev_count; i++) {
printf("dev: %s\n", layers_access_dev[i].layerName);
}
#endif
vulk->queue_fam_ind = index;
int queue_count=1;
vector<char*> ext;
ext.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
#ifdef DEBUG
vector<char*> layers;
layers.push_back("VK_LAYER_LUNARG_object_tracker");
layers.push_back("VK_LAYER_LUNARG_core_validation");
#endif
VkDeviceQueueCreateInfo queue_info = {};
queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_info.pQueuePriorities = prior;
queue_info.queueCount = queue_count;
queue_info.queueFamilyIndex = index;
queue_info.flags = 0;
queue_info.pNext = nullptr;
VkDeviceCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
#ifdef DEBUG
info.enabledLayerCount = layers.size();
info.ppEnabledLayerNames = layers.data();
#else
info.enabledLayerCount = 0;
info.ppEnabledLayerNames = nullptr;
#endif
info.pEnabledFeatures = &features;
info.enabledExtensionCount = ext.size();
info.ppEnabledExtensionNames = ext.data();
info.pQueueCreateInfos = &queue_info;
info.queueCreateInfoCount = 1;
info.pNext = nullptr;
if (VK_SUCCESS != vkCreateDevice(vulk->gpu, &info, nullptr, &(vulk->device))) {
exit(-1);
}
vulk->queue = (VkQueue*)malloc(sizeof(VkQueue)*queue_count);
vkGetDeviceQueue(vulk->device, index, 0, &(vulk->queue[0]));
}
void createSwapchain(vulkan *vulk) {
VkSurfaceCapabilitiesKHR capabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulk->gpu, vulk->surface_struct.surface,&capabilities);
uint32_t format_c;
vkGetPhysicalDeviceSurfaceFormatsKHR(vulk->gpu, vulk->surface_struct.surface, &format_c, nullptr);
VkSurfaceFormatKHR *formats = (VkSurfaceFormatKHR*)malloc(sizeof(VkSurfaceFormatKHR)*format_c);
vkGetPhysicalDeviceSurfaceFormatsKHR(vulk->gpu, vulk->surface_struct.surface, &format_c, formats);
uint32_t pres_mode_c;
vkGetPhysicalDeviceSurfacePresentModesKHR(vulk->gpu, vulk->surface_struct.surface, &pres_mode_c, nullptr);
VkPresentModeKHR *pres_modes = (VkPresentModeKHR*)malloc(sizeof(VkPresentModeKHR)*pres_mode_c);
vkGetPhysicalDeviceSurfacePresentModesKHR(vulk->gpu, vulk->surface_struct.surface, &pres_mode_c, pres_modes);
int pres_mode_i = 0;
for (int i = 0; i < pres_mode_c; i++) {
if (pres_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
pres_mode_i = i;
}
}
vulk->surface_struct.extent = capabilities.currentExtent;
vulk->image.color_format= formats[0].format;
VkSwapchainCreateInfoKHR info = {};
info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
info.clipped = VK_TRUE;
info.compositeAlpha = (VkCompositeAlphaFlagBitsKHR)capabilities.supportedCompositeAlpha;
info.flags = 0;
info.imageArrayLayers=1;
info.imageColorSpace = formats[0].colorSpace;
info.imageExtent = capabilities.currentExtent;
info.imageFormat = formats[0].format;
info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
info.minImageCount = capabilities.minImageCount;
info.oldSwapchain =VK_NULL_HANDLE;
info.pNext = nullptr;
info.pQueueFamilyIndices =&(vulk->queue_fam_ind);
info.presentMode = pres_modes[pres_mode_i];
info.preTransform = capabilities.currentTransform;
info.queueFamilyIndexCount = 1;
info.surface = vulk->surface_struct.surface;
VkResult not_VK_SUCCESS = vkCreateSwapchainKHR(vulk->device, &info, nullptr, &(vulk->swapchain_struct.swapchain));
if (not_VK_SUCCESS != VK_SUCCESS) {
exit(-1);
}
vulk->swapchain_struct.format = formats[0].format;
}
void createImages(vulkan *vulk,Memory *depth_img_memory) {
uint32_t img_c;
vkGetSwapchainImagesKHR(vulk->device, vulk->swapchain_struct.swapchain, &img_c, nullptr);
vulk->image.color_images = (VkImage*)malloc(sizeof(VkImage)*img_c);
vkGetSwapchainImagesKHR(vulk->device, vulk->swapchain_struct.swapchain, &img_c, vulk->image.color_images);
vulk->image_c = img_c;
vulk->image.depth_images = (VkImage*)malloc(sizeof(VkImage));
vulk->image.color_image_views=(VkImageView*)malloc(sizeof(VkImageView)*img_c);
vulk->image.depth_image_views=(VkImageView*)malloc(sizeof(VkImageView));
VkComponentMapping mapping = {};
mapping.r = VK_COMPONENT_SWIZZLE_R;
mapping.g = VK_COMPONENT_SWIZZLE_G;
mapping.b = VK_COMPONENT_SWIZZLE_B;
mapping.a = VK_COMPONENT_SWIZZLE_A;
vulk->image.color_range = (VkImageSubresourceRange*)malloc(sizeof(VkImageSubresourceRange)*img_c);
VkImageSubresourceRange range = {};
range.aspectMask =VK_IMAGE_ASPECT_COLOR_BIT;
range.baseArrayLayer = 0;
range.baseMipLevel = 0;
range.layerCount = 1;
range.levelCount = 1;
for (int i = 0; i < img_c; i++) {
VkImageViewCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.components = mapping;
info.flags = 0;
info.format = vulk->swapchain_struct.format;
info.image = (vulk->image.color_images)[i];
info.pNext = nullptr;
info.subresourceRange = range;
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
vulk->image.color_range[i] = range;
vkCreateImageView(vulk->device, &info, nullptr, &(vulk->image.color_image_views)[i]);
}
vulk->image.depth_range = (VkImageSubresourceRange*)malloc(sizeof(VkImageSubresourceRange));
vulk->image.depth_range[0] = range;
vector<VkFormat> depth_formats{
VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D32_SFLOAT,
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D16_UNORM_S8_UINT,
VK_FORMAT_D16_UNORM
};
VkFormat depth_format;
for (int i = 0; i < depth_formats.size(); i++) {
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(vulk->gpu, depth_formats[i], &props);
if (props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {
depth_format = depth_formats[i];
break;
}
}
vulk->image.depth_format = depth_format;
VkImageCreateInfo img_info = {};
img_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
img_info.arrayLayers = 1;
img_info.extent.width = vulk->surface_struct.extent.width;
img_info.extent.height = vulk->surface_struct.extent.height;
img_info.extent.depth = 1;
img_info.flags = 0;
img_info.format = depth_format;
img_info.imageType = VK_IMAGE_TYPE_2D;
img_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
img_info.mipLevels = 1;
img_info.pNext = nullptr;
img_info.pQueueFamilyIndices = &(vulk->queue_fam_ind);
img_info.queueFamilyIndexCount = 1;
img_info.samples = VK_SAMPLE_COUNT_1_BIT;
img_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
img_info.tiling = VK_IMAGE_TILING_OPTIMAL;
img_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
if (VK_SUCCESS!=vkCreateImage(vulk->device, &img_info, nullptr, &(vulk->image.depth_images)[0])) {
printf("It not works");
}
VkMemoryRequirements req;
vkGetImageMemoryRequirements(vulk->device, (vulk->image.depth_images)[0], &req);
vkGetPhysicalDeviceMemoryProperties(vulk->gpu, &(depth_img_memory->props));
uint32_t mem_index=-2;
for (int i = 0; i < depth_img_memory->props.memoryTypeCount; i++) {
if (req.memoryTypeBits & (1 << i)) {
if ((depth_img_memory->props.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
mem_index = i;
break;
}
}
}
if (mem_index == -2) {
printf("no supported memorytype");
exit(-2);
}
VkMemoryAllocateInfo mem_info = {};
mem_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
mem_info.pNext = nullptr;
mem_info.allocationSize = req.size;
mem_info.memoryTypeIndex = mem_index;
vkAllocateMemory(vulk->device, &mem_info, nullptr, &(depth_img_memory->dev_mem));
vkBindImageMemory(vulk->device, (vulk->image.depth_images)[0], depth_img_memory->dev_mem, 0);
VkComponentMapping mapping_d = {
VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY,
};
range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
VkImageViewCreateInfo img_view_info_d = {};
img_view_info_d.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
img_view_info_d.components = mapping_d;
img_view_info_d.flags = 0;
img_view_info_d.format = depth_format;
img_view_info_d.image = (vulk->image.depth_images)[0];
img_view_info_d.pNext = nullptr;
img_view_info_d.subresourceRange = range;
img_view_info_d.viewType = VK_IMAGE_VIEW_TYPE_2D;
if (VK_SUCCESS != vkCreateImageView(vulk->device, &img_view_info_d, nullptr, &(vulk->image.depth_image_views)[0])) {
printf("huge pile of shit!!!");
exit(-1);
}
}
void createCommandPool(vulkan vulk,cmd_pool *pool, uint32_t cmd_buff_c) {
VkCommandPoolCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT|VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
info.pNext = nullptr;
info.queueFamilyIndex = vulk.queue_fam_ind;
vkCreateCommandPool(vulk.device, &info, nullptr, &(pool->pool));
pool->cmd_buff_c = cmd_buff_c;
pool->cmd_buffs = (VkCommandBuffer*)malloc(sizeof(VkCommandBuffer) * cmd_buff_c);
VkCommandBufferAllocateInfo cmd_info = {};
cmd_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmd_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
cmd_info.commandPool = pool->pool;
cmd_info.commandBufferCount = cmd_buff_c ;
cmd_info.pNext = nullptr;
vkAllocateCommandBuffers(vulk.device, &cmd_info, pool->cmd_buffs);
}
VkClearValue *clear;
void createFramebuffer(vulkan *vulk,VkExtent2D extent) {
vulk->fbo = (VkFramebuffer*)malloc(sizeof(VkFramebuffer)*vulk->image_c);
for (int i = 0; i < vulk->image_c; i++) {
VkImageView *img_views = (VkImageView*)malloc(sizeof(VkImageView) * 2);
img_views[0] = vulk->image.color_image_views[i];
img_views[1] = vulk->image.depth_image_views[0];
VkFramebufferCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
info.attachmentCount = 2;
info.pAttachments = img_views;
info.width = extent.height;
info.height = extent.width;
info.layers = 1;
info.renderPass = vulk->render_pass;
info.flags = 0;
info.pNext = nullptr;
if (VK_SUCCESS != vkCreateFramebuffer(vulk->device, &info, nullptr, &(vulk->fbo[i]))) {
printf("could not create framebuffer");
}
}
}
VkSemaphore *semaphores;
void createSemaphore(vulkan *vulk ,VkSemaphore *semaphore) {
VkSemaphoreCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
vkCreateSemaphore(vulk->device, &info, nullptr, semaphore);
}
void createRenderPass(vulkan *vulk) {
VkAttachmentDescription *descr = (VkAttachmentDescription*)malloc(sizeof(VkAttachmentDescription) * 2);
VkAttachmentDescription color_descr = {};
color_descr.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
color_descr.format = vulk->image.color_format;
color_descr.samples = VK_SAMPLE_COUNT_1_BIT;
color_descr.initialLayout= VK_IMAGE_LAYOUT_UNDEFINED;
color_descr.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
color_descr.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
color_descr.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
color_descr.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
color_descr.flags = 0;
VkAttachmentDescription depth_descr = {};
depth_descr.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
depth_descr.format = vulk->image.depth_format;
depth_descr.samples = VK_SAMPLE_COUNT_1_BIT;
depth_descr.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
depth_descr.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depth_descr.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
depth_descr.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
depth_descr.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
depth_descr.flags = 0;
*descr = color_descr;
*(descr + 1) = depth_descr;
VkAttachmentReference color_ref = {};
color_ref.attachment = 0;
color_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference depth_ref = {};
depth_ref.attachment = 1;
depth_ref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkSubpassDescription subp_descr = {};
subp_descr.colorAttachmentCount = 1;
subp_descr.pColorAttachments=&color_ref;
subp_descr.pDepthStencilAttachment = &depth_ref;
subp_descr.inputAttachmentCount = 0;
subp_descr.pInputAttachments = nullptr;
subp_descr.preserveAttachmentCount = 0;
subp_descr.pPreserveAttachments = nullptr;
subp_descr.pResolveAttachments = nullptr;
subp_descr.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subp_descr.flags = 0;
VkRenderPassCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
info.attachmentCount = 2;
info.pAttachments = descr;
info.dependencyCount = 0;
info.pDependencies = VK_NULL_HANDLE;
info.subpassCount = 1;
info.pSubpasses = &subp_descr;
info.flags = 0;
info.pNext = nullptr;
if (VK_SUCCESS != vkCreateRenderPass(vulk->device, &info, nullptr, &(vulk->render_pass))) {
printf("Could not create render pass.");
}
}
VkFence fence;
void createFences(vulkan *vulk) {
VkFenceCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
vkCreateFence(vulk->device, &info, nullptr, &fence);
}
int main(int argc,char** argv) {
vulkan vulk;
Memory depth_memory;
cmd_pool pool;
initInstance(&vulk);
getGPU(&vulk);
Window window = Window();
window.open(&vulk);
createDevice(&vulk);
VkViewport viewport = {};
viewport.width = window.extent.width;
viewport.height = window.extent.height;
viewport.x = 0;
viewport.y = 0;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
init_debug(&vulk);
createSwapchain(&vulk);
createImages(&vulk, &depth_memory);
createRenderPass(&vulk);
createFramebuffer(&vulk,window.extent);
semaphores= (VkSemaphore*) malloc(sizeof(VkSemaphore)*2);
createSemaphore(&vulk, &semaphores[0]);
createSemaphore(&vulk, &semaphores[1]);
createFences(&vulk);
createCommandPool(vulk,&pool,2);
uint32_t img_pres;
VkResult result;
VkPresentInfoKHR info = {};
info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
info.pImageIndices = &img_pres;
info.pResults = &result;
info.swapchainCount = 1;
info.pSwapchains = &vulk.swapchain_struct.swapchain;
info.waitSemaphoreCount =0;
info.pWaitSemaphores = nullptr;
info.pNext = nullptr;
VkCommandBufferBeginInfo beg = {};
beg.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beg.pInheritanceInfo = nullptr;
beg.pNext = nullptr;
beg.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
VkClearValue val[2];
val[1] = { 0.0f,1.0f,1.0f,1.0f };
val[0] = { 0.0f,0 };
VkRenderPassBeginInfo render = {};
render.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render.clearValueCount = 2;
render.framebuffer = vulk.fbo[0];
render.pClearValues = val;
render.pNext = nullptr;
render.renderArea.offset = { 0,0 };
render.renderArea.extent = { window.extent.height, window.extent.width };
render.renderPass = vulk.render_pass;
vkBeginCommandBuffer(pool.cmd_buffs[0], &beg);
vkCmdBeginRenderPass(pool.cmd_buffs[0], &render, VK_SUBPASS_CONTENTS_INLINE);
vkCmdEndRenderPass(pool.cmd_buffs[0]);
vkEndCommandBuffer(pool.cmd_buffs[0]);
VkRenderPassBeginInfo render_2 = {};
render_2.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_2.clearValueCount = 2;
render_2.framebuffer = vulk.fbo[1];
render_2.pClearValues = val;
render_2.pNext = nullptr;
render_2.renderArea = { 0,0,window.extent.height,window.extent.width };
render_2.renderPass = vulk.render_pass;
vkBeginCommandBuffer(pool.cmd_buffs[1], &beg);
vkCmdBeginRenderPass(pool.cmd_buffs[1], &render_2, VK_SUBPASS_CONTENTS_INLINE);
vkCmdEndRenderPass(pool.cmd_buffs[1]);
vkEndCommandBuffer(pool.cmd_buffs[1]);
VkSubmitInfo sub = {};
sub.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
sub.commandBufferCount = 1;
sub.pNext = nullptr;
sub.pSignalSemaphores = nullptr;
sub.pWaitDstStageMask = nullptr;
sub.pWaitSemaphores = nullptr;
sub.signalSemaphoreCount = 0;
sub.waitSemaphoreCount = 0;
VkResult res=VK_ERROR_DEVICE_LOST;
sub.pCommandBuffers = &pool.cmd_buffs[0];
vkQueueSubmit(vulk.queue[0], 1, &sub, VK_NULL_HANDLE);
sub.pCommandBuffers = &pool.cmd_buffs[1];
vkQueueSubmit(vulk.queue[0], 1, &sub, VK_NULL_HANDLE);
while (window.running) {
if (VK_SUCCESS != vkAcquireNextImageKHR(vulk.device, vulk.swapchain_struct.swapchain, UINT64_MAX, VK_NULL_HANDLE, fence, &img_pres)) {
return -2;
}
vkWaitForFences(vulk.device, 1, &fence, VK_TRUE, UINT64_MAX);
vkResetFences(vulk.device, 1, &fence);vkQueueWaitIdle(vulk.queue[0]);
sub.pCommandBuffers = &pool.cmd_buffs[img_pres];
if (res == vkQueueSubmit(vulk.queue[0], 1, &sub, VK_NULL_HANDLE)) {
printf("img: %d\n",res);
}
cout << hex << vulk.image.depth_images[0] << endl;
vkQueuePresentKHR(vulk.queue[0], &info);
window.run();
}
return 0;
}
1) Update your SDK. There is even no VK_LAYER_LUNARG_image layer since 1.0.42.0. And you know, there was probably a bunchload of bugfixes.
2) You are enabling layers in wrong order. Use the VK_LAYER_LUNARG_standard_validation meta-layer instead of doing it manually (also avoids problem in 1 trying to use obsoleted layer).
3) I see many errors in your code. Layers do not necessarily have full coverage yet (and another uncought error may cause to show another error down the road that does not make sense without the context).
E.g. no synchronization (your semaphores are unused there), many memory leaks (due to C style programming), assuming there will be at least two swapchain images, not checking VkResults...
4) I cannot reproduce it with your code. Firstly I get a problem with having only one swapchain image and the code not expecting it (mentioned in 3). After fixing that I get error about vkAcquireNextImageKHR getting more images than allowed (driver layer bug if using VkPresentInfoKHR::pResults). Workarounding that I get no error messages.
I haven't checked your code exhaustively, but the renderpass says that both attachments are initially in UNDEFINED layout, transition to COLOR_ATTACHMENT_OPTIMAL/DEPTH_STENCIL_ATTACHMENT_OPTIMAL, and then finally to PRESENT_SRC/DEPTH_STENCIL_ATTACHMENT_OPTIMAL. That seems correct, and the validation layers appear to be ignoring the renderpass initialLayout settings. If you're seeing this on the most recent version of the SDK, please file a bug at https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers.
I'm working on video encoding that will be used in a Unity plugin. I have made image encoding work, but now I'm at the audio. So trying only with the audio in to a mp4 file with AAC encoding. And I'm stuck. The resulting file does not contain anything. Also, from what I understand, AAC in ffmpeg only supports AV_SAMPLE_FMT_FLTP, that's why I use it. Here's my code:
Setup:
int initialize_encoding_audio(const char *filename)
{
int ret;
AVCodecID aud_codec_id = AV_CODEC_ID_AAC;
AVSampleFormat sample_fmt = AV_SAMPLE_FMT_FLTP;
avcodec_register_all();
av_register_all();
aud_codec = avcodec_find_encoder(aud_codec_id);
avcodec_register(aud_codec);
if (!aud_codec)
return COULD_NOT_FIND_AUD_CODEC;
aud_codec_context = avcodec_alloc_context3(aud_codec);
if (!aud_codec_context)
return CONTEXT_CREATION_ERROR;
aud_codec_context->bit_rate = 192000;
aud_codec_context->sample_rate = select_sample_rate(aud_codec);
aud_codec_context->sample_fmt = sample_fmt;
aud_codec_context->channel_layout = AV_CH_LAYOUT_STEREO;
aud_codec_context->channels = av_get_channel_layout_nb_channels(aud_codec_context->channel_layout);
aud_codec_context->codec = aud_codec;
aud_codec_context->codec_id = aud_codec_id;
ret = avcodec_open2(aud_codec_context, aud_codec, NULL);
if (ret < 0)
return COULD_NOT_OPEN_AUD_CODEC;
outctx = avformat_alloc_context();
ret = avformat_alloc_output_context2(&outctx, NULL, "mp4", filename);
outctx->audio_codec = aud_codec;
outctx->audio_codec_id = aud_codec_id;
audio_st = avformat_new_stream(outctx, aud_codec);
audio_st->codecpar->bit_rate = aud_codec_context->bit_rate;
audio_st->codecpar->sample_rate = aud_codec_context->sample_rate;
audio_st->codecpar->channels = aud_codec_context->channels;
audio_st->codecpar->channel_layout = aud_codec_context->channel_layout;
audio_st->codecpar->codec_id = aud_codec_id;
audio_st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
audio_st->codecpar->format = sample_fmt;
audio_st->codecpar->frame_size = aud_codec_context->frame_size;
audio_st->codecpar->block_align = aud_codec_context->block_align;
audio_st->codecpar->initial_padding = aud_codec_context->initial_padding;
outctx->streams = new AVStream*[1];
outctx->streams[0] = audio_st;
av_dump_format(outctx, 0, filename, 1);
if (!(outctx->oformat->flags & AVFMT_NOFILE))
{
if (avio_open(&outctx->pb, filename, AVIO_FLAG_WRITE) < 0)
return COULD_NOT_OPEN_FILE;
}
ret = avformat_write_header(outctx, NULL);
aud_frame = av_frame_alloc();
aud_frame->nb_samples = aud_codec_context->frame_size;
aud_frame->format = aud_codec_context->sample_fmt;
aud_frame->channel_layout = aud_codec_context->channel_layout;
int buffer_size = av_samples_get_buffer_size(NULL, aud_codec_context->channels, aud_codec_context->frame_size,
aud_codec_context->sample_fmt, 0);
av_frame_get_buffer(aud_frame, buffer_size / aud_codec_context->channels);
if (!aud_frame)
return COULD_NOT_ALLOCATE_FRAME;
aud_frame_counter = 0;
return 0;
}
Encoding:
int encode_audio_samples(uint8_t **aud_samples)
{
int ret;
int buffer_size = av_samples_get_buffer_size(NULL, aud_codec_context->channels, aud_codec_context->frame_size,
aud_codec_context->sample_fmt, 0);
for (size_t i = 0; i < buffer_size / aud_codec_context->channels; i++)
{
aud_frame->data[0][i] = aud_samples[0][i];
aud_frame->data[1][i] = aud_samples[1][i];
}
aud_frame->pts = aud_frame_counter++;
ret = avcodec_send_frame(aud_codec_context, aud_frame);
if (ret < 0)
return ERROR_ENCODING_SAMPLES_SEND;
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
fflush(stdout);
while (true)
{
ret = avcodec_receive_packet(aud_codec_context, &pkt);
if (!ret)
{
av_packet_rescale_ts(&pkt, aud_codec_context->time_base, audio_st->time_base);
pkt.stream_index = audio_st->index;
av_write_frame(outctx, &pkt);
av_packet_unref(&pkt);
}
if (ret == AVERROR(EAGAIN))
break;
else if (ret < 0)
return ERROR_ENCODING_SAMPLES_RECEIVE;
else
break;
}
return 0;
}
Finish encoding:
int finish_audio_encoding()
{
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
fflush(stdout);
int ret = avcodec_send_frame(aud_codec_context, NULL);
if (ret < 0)
return ERROR_ENCODING_FRAME_SEND;
while (true)
{
ret = avcodec_receive_packet(aud_codec_context, &pkt);
if (!ret)
{
if (pkt.pts != AV_NOPTS_VALUE)
pkt.pts = av_rescale_q(pkt.pts, aud_codec_context->time_base, audio_st->time_base);
if (pkt.dts != AV_NOPTS_VALUE)
pkt.dts = av_rescale_q(pkt.dts, aud_codec_context->time_base, audio_st->time_base);
av_write_frame(outctx, &pkt);
av_packet_unref(&pkt);
}
if (ret == -AVERROR(AVERROR_EOF))
break;
else if (ret < 0)
return ERROR_ENCODING_FRAME_RECEIVE;
}
av_write_trailer(outctx);
}
Main:
void get_audio_frame(float_t *left_samples, float_t *right_samples, int frame_size, float* t, float* tincr, float* tincr2)
{
int j, i;
float v;
for (j = 0; j < frame_size; j++)
{
v = sin(*t);
*left_samples = v;
*right_samples = v;
left_samples++;
right_samples++;
*t += *tincr;
*tincr += *tincr2;
}
}
int main()
{
int frame_rate = 30; // this should be like 96000 / 1024 or somthing i guess?
float t, tincr, tincr2;
initialize_encoding_audio("audio.mp4");
int sec = 50;
float_t** aud_samples;
int src_samples_linesize;
int src_nb_samples = 1024;
int src_channels = 2;
int ret = av_samples_alloc_array_and_samples((uint8_t***)&aud_samples, &src_samples_linesize, src_channels,
src_nb_samples, AV_SAMPLE_FMT_FLTP, 0);
t = 0;
tincr = 0;
tincr2 = 0;
for (size_t i = 0; i < frame_rate * sec; i++)
{
get_audio_frame(aud_samples[0], aud_samples[1], src_nb_samples, &t, &tincr, &tincr2);
encode_audio_samples((uint8_t **)aud_samples);
}
finish_audio_encoding();
//cleanup();
return 0;
}
I guess the first thing that I would want to make sure I got right is the synthetic sound generation and how I transfer that to the AVFrame. Are my conversions correct? But feel free to point out anything that might be wrong.
Thanks in advance!
Edit: the whole source: http://pastebin.com/jYtmkhek
Edit2: Added initialization of tincr & tincr2
Unless I'm missing something from the pastebin, you forgot to initialize a few variables. You're using garbage to generate your samples.
float t, tincr, tincr2;
[...]
get_audio_frame(aud_samples[0], aud_samples[1], src_nb_samples, &t, &tincr, &tincr2);
You probably want to start with t=0 and increment by 2 * PI * frequency / sample rate for a sine wave.
Also, avformat_new_stream() creates the stream for you, don't do it with new.
Update:
I removed all the c++ stuff to test this. Here's the code that works: pastebin
And here's the resulting file: audio.mp4
ffmpeg -i audio.mp4 -filter_complex "showwaves=s=640x120:mode=line:colors=white" -frames:v 1 wave.jpg
Diff:
1,6d0
< #include "encoder.h"
< #include <algorithm>
< #include <iterator>
<
< extern "C"
< {
14a9
> #include <math.h>
40,41c35,36
< SwsContext *sws_ctx;
< SwrContext *swr_ctx = NULL;
---
> struct SwsContext *sws_ctx;
> struct SwrContext *swr_ctx = NULL;
76,77c71,72
< AVCodecID aud_codec_id = AV_CODEC_ID_AAC;
< AVSampleFormat sample_fmt = AV_SAMPLE_FMT_FLTP;
---
> enum AVCodecID aud_codec_id = AV_CODEC_ID_AAC;
> enum AVSampleFormat sample_fmt = AV_SAMPLE_FMT_FLTP;
125,126c120,121
< outctx->streams = new AVStream*[1];
< outctx->streams[0] = audio_st;
---
> //outctx->streams = new AVStream*[1];
> //outctx->streams[0] = audio_st;
182c177
< while (true)
---
> while (1)
216c211
< while (true)
---
> while (1)
291c286
< float t, tincr, tincr2;
---
> float t = 0, tincr = 2 * M_PI * 440.0 / 96000, tincr2 = 0;
317d311
< }
Im having some hard time figuring out where to find about this..
Im building a simple recorder to learn about this video compression universe and Im facing some weird behaviors..
Before all I need to explain the scenario...
Its very simple... everytime I call av_read_frame( input_context, input_packet ) I save the pts into the last_pts variable...
So...
Whats bothering me is the fact that about 10% of my calls to av_read_frame I get
input_packet.pts > last_pts
Resulting in a error message from the encoder when I try to do it...
Having it in mind I decided to just drop those frames when it happens....
I think it is not a good idea to just drop frames because if I get them, its needed somehow...
So... what to do when last_pts > current_pts ?
I will paste my test code that Im using capturing the video from webcam and saving to mp4 file with libx264 encoder
#include <QCoreApplication>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/avutil.h>
#include <libavdevice/avdevice.h>
}
#include <QTime>
#include <QThread>
#include <QDebug>
#define SM_DEBUG
static const double max_fps = 30;
static const double min_loop_duration = 1000 / max_fps;
static const double max_duration = 5; // in seconds
static void sleep_if_needed(const int &elapsed) {
int sleep_duration = min_loop_duration - elapsed;
if (sleep_duration > 0) {
QThread::msleep(sleep_duration);
}
}
#ifdef SM_DEBUG
static void log_packet(const AVPacket *pkt,
const AVRational &time_base,
int is_input=0)
{
qDebug() << ((is_input) ? QString(">>") : QString("<<")) << "Size:" << QString::number(pkt->size) <<
"pts:" << QString::number(pkt->pts) <<
"pts_time:" << QString::number(av_q2d(time_base) * pkt->pts) <<
"dts:" << QString::number(pkt->dts) <<
"dts_time:" << QString::number(av_q2d(time_base) * pkt->dts);
}
#endif
int main()
{
int input_w, input_h, output_w = 640, output_h = 480;
av_register_all();
avdevice_register_all();
avcodec_register_all();
#ifdef SM_DEBUG
av_log_set_level(AV_LOG_DEBUG);
#else
av_log_set_level(AV_LOG_ERROR);
#endif
AVFormatContext *ic;
AVFormatContext *oc;
AVInputFormat *ifmt;
AVDictionary *opts = 0;
AVCodecContext* dec_ctx;
AVCodecContext* enc_ctx;
AVCodec *dec;
AVCodec *enc;
AVStream* ist;
AVStream* ost;
ifmt = av_find_input_format("v4l2");
av_dict_set(&opts, "tune", "zerolatency", AV_DICT_APPEND);
ic = avformat_alloc_context();
ic->flags |= AVFMT_FLAG_NONBLOCK;
avformat_open_input(&ic, "/dev/video0", ifmt, &opts);
avformat_find_stream_info(ic, NULL);
av_dump_format(ic, 0, ic->filename, 0);
AVFrame *frame;
AVFrame *tmp_frame;
ist = ic->streams[0];
dec_ctx = ist->codec;
input_w = dec_ctx->width;
input_h = dec_ctx->height;
dec_ctx->flags |= CODEC_FLAG_LOW_DELAY;
dec = avcodec_find_decoder(dec_ctx->codec_id);
av_format_set_video_codec(ic, dec);
avcodec_open2(dec_ctx, dec, NULL);
// output
avformat_alloc_output_context2(&oc, NULL, "MP4", "/home/poste9/grava.mp4");
enc = avcodec_find_encoder(AV_CODEC_ID_H264);
ost = avformat_new_stream(oc, enc);
enc_ctx = ost->codec;
enc_ctx->codec_id = AV_CODEC_ID_H264;
enc_ctx->width = output_w;
enc_ctx->height = output_h;
ost->time_base.num = ist->time_base.num;
ost->time_base.den = ist->time_base.den;
enc_ctx->time_base = ost->time_base;
enc_ctx->gop_size = 250;
enc_ctx->keyint_min = 25;
enc_ctx->qmax = 51;
enc_ctx->qmin = 30;
enc_ctx->pix_fmt = AV_PIX_FMT_YUV422P;
enc_ctx->max_b_frames = 6;
enc_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
enc_ctx->flags |= CODEC_FLAG_LOW_DELAY;
avcodec_open2(enc_ctx, enc, NULL);
avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
&oc->interrupt_callback, NULL);
av_dump_format(oc, 0, oc->filename, 1);
avformat_write_header(oc, NULL);
struct SwsContext *sws_ctx;
sws_ctx = sws_getContext(input_w, input_h,
dec_ctx->pix_fmt,
output_w, output_h, enc_ctx->pix_fmt,
SWS_BICUBIC, NULL, NULL, NULL);
frame = av_frame_alloc();
tmp_frame = av_frame_alloc();
frame->format = enc_ctx->pix_fmt;
frame->width = output_w;
frame->height = output_h;
frame->pts = AV_NOPTS_VALUE;
av_frame_get_buffer(frame, 32);
av_frame_make_writable(frame);
int got_picture=0;
int got_packet=0;
double recording_duration = 0;
QTime timer;
AVPacket pkt_out;
av_init_packet(&pkt_out);
timer.start();
bool started_recording = false;
int64_t start_time = 0;
int64_t last_pts = INT64_MIN;
while(1) {
timer.restart();
AVPacket pkt_in;
av_read_frame(ic, &pkt_in);
if (pkt_in.size == 0) {
sleep_if_needed(timer.elapsed());
continue;
}
avcodec_decode_video2(dec_ctx, tmp_frame, &got_picture, &pkt_in);
#ifdef SM_DEBUG
log_packet(&pkt_in, ist->time_base, 1);
#endif
if (!started_recording) {
start_time = pkt_in.dts;
started_recording = true;
}
if (pkt_in.pts < last_pts) {
sleep_if_needed(timer.elapsed());
continue;
}
last_pts = pkt_in.pts;
frame->pts = (pkt_in.dts - start_time);
if (!got_picture) {
av_free_packet(&pkt_in);
sleep_if_needed(timer.elapsed());
continue;
} else {
sws_scale(sws_ctx, tmp_frame->data, tmp_frame->linesize,
0, input_h, frame->data, frame->linesize);
av_free_packet(&pkt_in);
}
av_init_packet(&pkt_out);
avcodec_encode_video2(enc_ctx, &pkt_out, frame, &got_packet);
if (got_packet) {
if (pkt_out.pts < pkt_out.dts) {
pkt_out.dts = pkt_out.pts;
}
pkt_out.stream_index = 0;
recording_duration = pkt_out.pts * av_q2d(ost->time_base);
#ifdef SM_DEBUG
log_packet(&pkt_out, ost->time_base, 0);
#endif
av_interleaved_write_frame(oc, &pkt_out);
av_free_packet(&pkt_out);
}
if (recording_duration >= max_duration) {
break;
} else {
sleep_if_needed(timer.elapsed());
}
}
av_write_trailer(oc);
av_dict_free(&opts);
av_frame_free(&frame);
av_frame_free(&tmp_frame);
sws_freeContext(sws_ctx);
avcodec_close(dec_ctx);
avcodec_close(enc_ctx);
avio_close(oc->pb);
avformat_free_context(oc);
avformat_close_input(&ic);
return 0;
}
These frames are B frames. B frames are saved to the stream in decoding order, not presentation order. If you look at the DTS it will probablly look ok. it is the job of the decoder to reorder frames into presentation order after they are decoded.
EDIT. to fix your code, use the PTS from the decoded frame, not the packet.
i am having problems understanding how the audio part of the sdl library works
now, i know that when you initialize it, you have to specify the frequency and a >>callback<< function, which i think is then called automatically at the given frequency.
can anyone who worked with the sdl library write a simple example that would use sdl_audio to generate a 440 hz square wave (since it is the simplest waveform) at a sampling frequency of 44000 hz?
The Introduction to SDL (2011 cached version: 2) has got a neat example of using SDL Sound library that should get you started: http://www.libsdl.org/intro.en/usingsound.html
EDIT: Here is a working program that does what you asked for. I modified a bit the code found here: http://www.dgames.org/beep-sound-with-sdl/
#include <SDL/SDL.h>
#include <SDL/SDL_audio.h>
#include <queue>
#include <cmath>
const int AMPLITUDE = 28000;
const int FREQUENCY = 44100;
struct BeepObject
{
double freq;
int samplesLeft;
};
class Beeper
{
private:
double v;
std::queue<BeepObject> beeps;
public:
Beeper();
~Beeper();
void beep(double freq, int duration);
void generateSamples(Sint16 *stream, int length);
void wait();
};
void audio_callback(void*, Uint8*, int);
Beeper::Beeper()
{
SDL_AudioSpec desiredSpec;
desiredSpec.freq = FREQUENCY;
desiredSpec.format = AUDIO_S16SYS;
desiredSpec.channels = 1;
desiredSpec.samples = 2048;
desiredSpec.callback = audio_callback;
desiredSpec.userdata = this;
SDL_AudioSpec obtainedSpec;
// you might want to look for errors here
SDL_OpenAudio(&desiredSpec, &obtainedSpec);
// start play audio
SDL_PauseAudio(0);
}
Beeper::~Beeper()
{
SDL_CloseAudio();
}
void Beeper::generateSamples(Sint16 *stream, int length)
{
int i = 0;
while (i < length) {
if (beeps.empty()) {
while (i < length) {
stream[i] = 0;
i++;
}
return;
}
BeepObject& bo = beeps.front();
int samplesToDo = std::min(i + bo.samplesLeft, length);
bo.samplesLeft -= samplesToDo - i;
while (i < samplesToDo) {
stream[i] = AMPLITUDE * std::sin(v * 2 * M_PI / FREQUENCY);
i++;
v += bo.freq;
}
if (bo.samplesLeft == 0) {
beeps.pop();
}
}
}
void Beeper::beep(double freq, int duration)
{
BeepObject bo;
bo.freq = freq;
bo.samplesLeft = duration * FREQUENCY / 1000;
SDL_LockAudio();
beeps.push(bo);
SDL_UnlockAudio();
}
void Beeper::wait()
{
int size;
do {
SDL_Delay(20);
SDL_LockAudio();
size = beeps.size();
SDL_UnlockAudio();
} while (size > 0);
}
void audio_callback(void *_beeper, Uint8 *_stream, int _length)
{
Sint16 *stream = (Sint16*) _stream;
int length = _length / 2;
Beeper* beeper = (Beeper*) _beeper;
beeper->generateSamples(stream, length);
}
int main(int argc, char* argv[])
{
SDL_Init(SDL_INIT_AUDIO);
int duration = 1000;
double Hz = 440;
Beeper b;
b.beep(Hz, duration);
b.wait();
return 0;
}
Good luck.
A boiled-down variant of the beeper-example, reduced to the bare minimum (with error-handling).
#include <math.h>
#include <SDL.h>
#include <SDL_audio.h>
const int AMPLITUDE = 28000;
const int SAMPLE_RATE = 44100;
void audio_callback(void *user_data, Uint8 *raw_buffer, int bytes)
{
Sint16 *buffer = (Sint16*)raw_buffer;
int length = bytes / 2; // 2 bytes per sample for AUDIO_S16SYS
int &sample_nr(*(int*)user_data);
for(int i = 0; i < length; i++, sample_nr++)
{
double time = (double)sample_nr / (double)SAMPLE_RATE;
buffer[i] = (Sint16)(AMPLITUDE * sin(2.0f * M_PI * 441.0f * time)); // render 441 HZ sine wave
}
}
int main(int argc, char *argv[])
{
if(SDL_Init(SDL_INIT_AUDIO) != 0) SDL_Log("Failed to initialize SDL: %s", SDL_GetError());
int sample_nr = 0;
SDL_AudioSpec want;
want.freq = SAMPLE_RATE; // number of samples per second
want.format = AUDIO_S16SYS; // sample type (here: signed short i.e. 16 bit)
want.channels = 1; // only one channel
want.samples = 2048; // buffer-size
want.callback = audio_callback; // function SDL calls periodically to refill the buffer
want.userdata = &sample_nr; // counter, keeping track of current sample number
SDL_AudioSpec have;
if(SDL_OpenAudio(&want, &have) != 0) SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Failed to open audio: %s", SDL_GetError());
if(want.format != have.format) SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Failed to get the desired AudioSpec");
SDL_PauseAudio(0); // start playing sound
SDL_Delay(1000); // wait while sound is playing
SDL_PauseAudio(1); // stop playing sound
SDL_CloseAudio();
return 0;
}
SDL 2 C example
The following code produces a sinusoidal sound, it is adapted from: https://codereview.stackexchange.com/questions/41086/play-some-sine-waves-with-sdl2
main.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <SDL2/SDL.h>
const double ChromaticRatio = 1.059463094359295264562;
const double Tao = 6.283185307179586476925;
Uint32 sampleRate = 48000;
Uint32 frameRate = 60;
Uint32 floatStreamLength = 1024;
Uint32 samplesPerFrame;
Uint32 msPerFrame;
double practicallySilent = 0.001;
Uint32 audioBufferLength = 48000;
float *audioBuffer;
SDL_atomic_t audioCallbackLeftOff;
Sint32 audioMainLeftOff;
Uint8 audioMainAccumulator;
SDL_AudioDeviceID AudioDevice;
SDL_AudioSpec audioSpec;
SDL_Event event;
SDL_bool running = SDL_TRUE;
typedef struct {
float *waveform;
Uint32 waveformLength;
double volume;
double pan;
double frequency;
double phase;
} voice;
void speak(voice *v) {
float sample;
Uint32 sourceIndex;
double phaseIncrement = v->frequency/sampleRate;
Uint32 i;
if (v->volume > practicallySilent) {
for (i = 0; (i + 1) < samplesPerFrame; i += 2) {
v->phase += phaseIncrement;
if (v->phase > 1)
v->phase -= 1;
sourceIndex = v->phase*v->waveformLength;
sample = v->waveform[sourceIndex]*v->volume;
audioBuffer[audioMainLeftOff+i] += sample*(1-v->pan);
audioBuffer[audioMainLeftOff+i+1] += sample*v->pan;
}
}
else {
for (i=0; i<samplesPerFrame; i+=1)
audioBuffer[audioMainLeftOff+i] = 0;
}
audioMainAccumulator++;
}
double getFrequency(double pitch) {
return pow(ChromaticRatio, pitch-57)*440;
}
int getWaveformLength(double pitch) {
return sampleRate / getFrequency(pitch)+0.5f;
}
void buildSineWave(float *data, Uint32 length) {
Uint32 i;
for (i=0; i < length; i++)
data[i] = sin(i*(Tao/length));
}
void logSpec(SDL_AudioSpec *as) {
printf(
" freq______%5d\n"
" format____%5d\n"
" channels__%5d\n"
" silence___%5d\n"
" samples___%5d\n"
" size______%5d\n\n",
(int) as->freq,
(int) as->format,
(int) as->channels,
(int) as->silence,
(int) as->samples,
(int) as->size
);
}
void logVoice(voice *v) {
printf(
" waveformLength__%d\n"
" volume__________%f\n"
" pan_____________%f\n"
" frequency_______%f\n"
" phase___________%f\n",
v->waveformLength,
v->volume,
v->pan,
v->frequency,
v->phase
);
}
void logWavedata(float *floatStream, Uint32 floatStreamLength, Uint32 increment) {
printf("\n\nwaveform data:\n\n");
Uint32 i=0;
for (i = 0; i < floatStreamLength; i += increment)
printf("%4d:%2.16f\n", i, floatStream[i]);
printf("\n\n");
}
void audioCallback(void *unused, Uint8 *byteStream, int byteStreamLength) {
float* floatStream = (float*) byteStream;
Sint32 localAudioCallbackLeftOff = SDL_AtomicGet(&audioCallbackLeftOff);
Uint32 i;
for (i = 0; i < floatStreamLength; i++) {
floatStream[i] = audioBuffer[localAudioCallbackLeftOff];
localAudioCallbackLeftOff++;
if (localAudioCallbackLeftOff == audioBufferLength)
localAudioCallbackLeftOff = 0;
}
SDL_AtomicSet(&audioCallbackLeftOff, localAudioCallbackLeftOff);
}
int init(void) {
SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER);
SDL_AudioSpec want;
SDL_zero(want);
want.freq = sampleRate;
want.format = AUDIO_F32;
want.channels = 2;
want.samples = floatStreamLength;
want.callback = audioCallback;
AudioDevice = SDL_OpenAudioDevice(NULL, 0, &want, &audioSpec, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
if (AudioDevice == 0) {
printf("\nFailed to open audio: %s\n", SDL_GetError());
return 1;
}
printf("want:\n");
logSpec(&want);
printf("audioSpec:\n");
logSpec(&audioSpec);
if (audioSpec.format != want.format) {
printf("\nCouldn't get Float32 audio format.\n");
return 2;
}
sampleRate = audioSpec.freq;
floatStreamLength = audioSpec.size / 4;
samplesPerFrame = sampleRate / frameRate;
msPerFrame = 1000 / frameRate;
audioMainLeftOff = samplesPerFrame * 8;
SDL_AtomicSet(&audioCallbackLeftOff, 0);
if (audioBufferLength % samplesPerFrame)
audioBufferLength += samplesPerFrame - (audioBufferLength % samplesPerFrame);
audioBuffer = malloc(sizeof(float) * audioBufferLength);
return 0;
}
int onExit(void) {
SDL_CloseAudioDevice(AudioDevice);
SDL_Quit();
return 0;
}
int main(int argc, char *argv[]) {
float syncCompensationFactor = 0.0016;
Sint32 mainAudioLead;
Uint32 i;
voice testVoiceA;
voice testVoiceB;
voice testVoiceC;
testVoiceA.volume = 1;
testVoiceB.volume = 1;
testVoiceC.volume = 1;
testVoiceA.pan = 0.5;
testVoiceB.pan = 0;
testVoiceC.pan = 1;
testVoiceA.phase = 0;
testVoiceB.phase = 0;
testVoiceC.phase = 0;
testVoiceA.frequency = getFrequency(45);
testVoiceB.frequency = getFrequency(49);
testVoiceC.frequency = getFrequency(52);
Uint16 C0waveformLength = getWaveformLength(0);
testVoiceA.waveformLength = C0waveformLength;
testVoiceB.waveformLength = C0waveformLength;
testVoiceC.waveformLength = C0waveformLength;
float sineWave[C0waveformLength];
buildSineWave(sineWave, C0waveformLength);
testVoiceA.waveform = sineWave;
testVoiceB.waveform = sineWave;
testVoiceC.waveform = sineWave;
if (init())
return 1;
SDL_Delay(42);
SDL_PauseAudioDevice(AudioDevice, 0);
while (running) {
while (SDL_PollEvent(&event) != 0) {
if (event.type == SDL_QUIT) {
running = SDL_FALSE;
}
}
for (i = 0; i < samplesPerFrame; i++)
audioBuffer[audioMainLeftOff+i] = 0;
speak(&testVoiceA);
speak(&testVoiceB);
speak(&testVoiceC);
if (audioMainAccumulator > 1) {
for (i=0; i<samplesPerFrame; i++) {
audioBuffer[audioMainLeftOff+i] /= audioMainAccumulator;
}
}
audioMainAccumulator = 0;
audioMainLeftOff += samplesPerFrame;
if (audioMainLeftOff == audioBufferLength)
audioMainLeftOff = 0;
mainAudioLead = audioMainLeftOff - SDL_AtomicGet(&audioCallbackLeftOff);
if (mainAudioLead < 0)
mainAudioLead += audioBufferLength;
if (mainAudioLead < floatStreamLength)
printf("An audio collision may have occured!\n");
SDL_Delay(mainAudioLead * syncCompensationFactor);
}
onExit();
return 0;
}
Compile and run:
gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c -lSDL2 -lm
./main.out
Should be easy to turn this into a simple piano with: https://github.com/cirosantilli/cpp-cheat/blob/f734a2e76fbcfc67f707ae06be7a2a2ef5db47d1/c/interactive/audio_gen.c#L44
For wav manipulation, also check the official examples:
http://hg.libsdl.org/SDL/file/e12c38730512/test/testresample.c
http://hg.libsdl.org/SDL/file/e12c38730512/test/loopwave.c
Tested on Ubuntu 19.10, SDL 2.0.10.
This is a minimal example of how to play a sine wave in SDL2.
Make sure to call SDL_Init(SDL_INIT_AUDIO) before creating an instance of Sound.
Sound.h
#include <cstdint>
#include <SDL2/SDL.h>
class Sound
{
public:
Sound();
~Sound();
void play();
void stop();
const double m_sineFreq;
const double m_sampleFreq;
const double m_samplesPerSine;
uint32_t m_samplePos;
private:
static void SDLAudioCallback(void *data, Uint8 *buffer, int length);
SDL_AudioDeviceID m_device;
};
Sound.cpp
#include "Sound.h"
#include <cmath>
#include <iostream>
Sound::Sound()
: m_sineFreq(1000),
m_sampleFreq(44100),
m_samplesPerSine(m_sampleFreq / m_sineFreq),
m_samplePos(0)
{
SDL_AudioSpec wantSpec, haveSpec;
SDL_zero(wantSpec);
wantSpec.freq = m_sampleFreq;
wantSpec.format = AUDIO_U8;
wantSpec.channels = 1;
wantSpec.samples = 2048;
wantSpec.callback = SDLAudioCallback;
wantSpec.userdata = this;
m_device = SDL_OpenAudioDevice(NULL, 0, &wantSpec, &haveSpec, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
if (m_device == 0)
{
std::cout << "Failed to open audio: " << SDL_GetError() << std::endl;
}
}
Sound::~Sound()
{
SDL_CloseAudioDevice(m_device);
}
void Sound::play()
{
SDL_PauseAudioDevice(m_device, 0);
}
void Sound::stop()
{
SDL_PauseAudioDevice(m_device, 1);
}
void Sound::SDLAudioCallback(void *data, Uint8 *buffer, int length)
{
Sound *sound = reinterpret_cast<Sound*>(data);
for(int i = 0; i < length; ++i)
{
buffer[i] = (std::sin(sound->m_samplePos / sound->m_samplesPerSine * M_PI * 2) + 1) * 127.5;
++sound->m_samplePos;
}
}