Переглянути джерело

vulkan: improve error messages

Sasha Szpakowski 7 місяців тому
батько
коміт
324b0f831b

+ 11 - 8
src/modules/graphics/vulkan/Buffer.cpp

@@ -39,7 +39,7 @@ static VkBufferUsageFlags getUsageBit(BufferUsage mode)
 	case BUFFERUSAGE_SHADER_STORAGE: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
 	case BUFFERUSAGE_INDIRECT_ARGUMENTS: return VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
 	default:
-		throw love::Exception("unsupported BufferUsage mode");
+		throw love::Exception("Unsupported BufferUsage mode: %d", mode);
 	}
 }
 
@@ -111,7 +111,7 @@ bool Buffer::loadVolatile()
 
 	auto result = vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo);
 	if (result != VK_SUCCESS)
-		throw love::Exception("failed to create buffer");
+		throw love::Exception("Failed to create Vulkan buffer: %s", Vulkan::getErrorString(result));
 
 	if (zeroInitialize)
 	{
@@ -131,8 +131,9 @@ bool Buffer::loadVolatile()
 		bufferViewInfo.format = Vulkan::getVulkanVertexFormat(getDataMember(0).decl.format);
 		bufferViewInfo.range = VK_WHOLE_SIZE;
 
-		if (vkCreateBufferView(vgfx->getDevice(), &bufferViewInfo, nullptr, &bufferView) != VK_SUCCESS)
-			throw love::Exception("failed to create texel buffer view");
+		result = vkCreateBufferView(vgfx->getDevice(), &bufferViewInfo, nullptr, &bufferView);
+		if (result != VK_SUCCESS)
+			throw love::Exception("Failed to create Vulkan texel buffer view: %s", Vulkan::getErrorString(result));
 	}
 
 	VkMemoryPropertyFlags memoryProperties;
@@ -226,8 +227,9 @@ void *Buffer::map(MapType map, size_t offset, size_t size)
 		allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
 		allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
 
-		if (vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &stagingBuffer, &stagingAllocation, &stagingAllocInfo) != VK_SUCCESS)
-			throw love::Exception("failed to create staging buffer");
+		VkResult result = vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &stagingBuffer, &stagingAllocation, &stagingAllocInfo);
+		if (result != VK_SUCCESS)
+			throw love::Exception("Failed to create Vulkan staging buffer: %s", Vulkan::getErrorString(result));
 
 		return stagingAllocInfo.pMappedData;
 	}
@@ -254,8 +256,9 @@ bool Buffer::fill(size_t offset, size_t size, const void *data)
 	VmaAllocation fillAllocation;
 	VmaAllocationInfo fillAllocInfo;
 
-	if (vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &fillBuffer, &fillAllocation, &fillAllocInfo) != VK_SUCCESS)
-		throw love::Exception("failed to create fill buffer");
+	VkResult result = vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &fillBuffer, &fillAllocation, &fillAllocInfo);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to create Vulkan fill buffer: %s", Vulkan::getErrorString(result));
 
 	memcpy(fillAllocInfo.pMappedData, data, size);
 

+ 53 - 36
src/modules/graphics/vulkan/Graphics.cpp

@@ -1426,8 +1426,9 @@ void Graphics::startRecordingGraphicsCommands()
 	beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
 	beginInfo.pInheritanceInfo = nullptr;
 
-	if (vkBeginCommandBuffer(commandBuffers.at(currentFrame), &beginInfo) != VK_SUCCESS)
-		throw love::Exception("failed to begin recording command buffer");
+	VkResult result = vkBeginCommandBuffer(commandBuffers.at(currentFrame), &beginInfo);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to begin recording Vulkan command buffer: %s", Vulkan::getErrorString(result));
 
 	initDynamicState();
 
@@ -1460,8 +1461,9 @@ void Graphics::endRecordingGraphicsCommands()
 	if (renderPassState.active)
 		endRenderPass();
 
-	if (vkEndCommandBuffer(commandBuffers.at(currentFrame)) != VK_SUCCESS)
-		throw love::Exception("failed to record command buffer");
+	VkResult result = vkEndCommandBuffer(commandBuffers.at(currentFrame));
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to record Vulkan command buffer: %s", Vulkan::getErrorString(result));
 }
 
 VkCommandBuffer Graphics::getCommandBufferForDataTransfer()
@@ -1808,8 +1810,9 @@ void Graphics::createLogicalDevice()
 	if (optionalDeviceExtensions.extendedDynamicState)
 		createInfo.pNext = &extendedDynamicStateFeatures;
 
-	if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS)
-		throw love::Exception("failed to create logical device");
+	VkResult result = vkCreateDevice(physicalDevice, &createInfo, nullptr, &device);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to create Vulkan logical device: %s", Vulkan::getErrorString(result));
 
 	volkLoadDevice(device);
 
@@ -1822,8 +1825,9 @@ void Graphics::createPipelineCache()
 	VkPipelineCacheCreateInfo cacheInfo{};
 	cacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
 
-	if (vkCreatePipelineCache(device, &cacheInfo, nullptr, &pipelineCache) != VK_SUCCESS)
-		throw love::Exception("could not create pipeline cache");
+	VkResult result = vkCreatePipelineCache(device, &cacheInfo, nullptr, &pipelineCache);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Could not create Vulkan pipeline cache: %s", Vulkan::getErrorString(result));
 }
 
 void Graphics::initVMA()
@@ -1876,8 +1880,9 @@ void Graphics::initVMA()
 	if (optionalDeviceExtensions.memoryBudget)
 		allocatorCreateInfo.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;
 
-	if (vmaCreateAllocator(&allocatorCreateInfo, &vmaAllocator) != VK_SUCCESS)
-		throw love::Exception("failed to create vma allocator");
+	VkResult result = vmaCreateAllocator(&allocatorCreateInfo, &vmaAllocator);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to create Vulkan VMA allocator: %s", Vulkan::getErrorString(result));
 }
 
 void Graphics::createSurface()
@@ -1885,7 +1890,7 @@ void Graphics::createSurface()
 	auto window = Module::getInstance<love::window::Window>(M_WINDOW);
 	const void *handle = window->getHandle();
 	if (!SDL_Vulkan_CreateSurface((SDL_Window*)handle, instance, nullptr, &surface))
-		throw love::Exception("failed to create window surface");
+		throw love::Exception("Failed to create Vulkan window surface: %s", SDL_GetError());
 }
 
 SwapChainSupportDetails Graphics::querySwapChainSupport(VkPhysicalDevice device)
@@ -1962,8 +1967,9 @@ void Graphics::createSwapChain()
 		createInfo.clipped = VK_TRUE;
 		createInfo.oldSwapchain = VK_NULL_HANDLE;
 
-		if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS)
-			throw love::Exception("failed to create swap chain");
+		VkResult result = vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain);
+		if (result != VK_SUCCESS)
+			throw love::Exception("Failed to create Vulkan swap chain: %s", Vulkan::getErrorString(result));
 
 		vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
 		swapChainImages.resize(imageCount);
@@ -2135,8 +2141,9 @@ void Graphics::createImageViews()
 		createInfo.subresourceRange.baseArrayLayer = 0;
 		createInfo.subresourceRange.layerCount = 1;
 
-		if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews.at(i)) != VK_SUCCESS)
-			throw love::Exception("failed to create image views");
+		VkResult result = vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews.at(i));
+		if (result != VK_SUCCESS)
+			throw love::Exception("Failed to create Vulkan swap chain image views: %s", Vulkan::getErrorString(result));
 	}
 }
 
@@ -2164,8 +2171,9 @@ VkFramebuffer Graphics::createFramebuffer(FramebufferConfiguration &configuratio
 	createInfo.layers = 1;
 
 	VkFramebuffer frameBuffer;
-	if (vkCreateFramebuffer(device, &createInfo, nullptr, &frameBuffer) != VK_SUCCESS)
-		throw love::Exception("failed to create framebuffer");
+	VkResult result = vkCreateFramebuffer(device, &createInfo, nullptr, &frameBuffer);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to create Vulkan framebuffer: %s", Vulkan::getErrorString(result));
 	return frameBuffer;
 }
 
@@ -2392,8 +2400,9 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration &configuration)
 	createInfo.pDependencies = dependencies.data();
 
 	VkRenderPass renderPass;
-	if (vkCreateRenderPass(device, &createInfo, nullptr, &renderPass) != VK_SUCCESS)
-		throw love::Exception("failed to create render pass");
+	VkResult result = vkCreateRenderPass(device, &createInfo, nullptr, &renderPass);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to create Vulkan render pass: %s", Vulkan::getErrorString(result));
 
 	return renderPass;
 }
@@ -2814,8 +2823,9 @@ VkSampler Graphics::createSampler(const SamplerState &samplerState)
 	samplerInfo.maxLod = static_cast<float>(samplerState.maxLod);
 
 	VkSampler sampler;
-	if (vkCreateSampler(device, &samplerInfo, nullptr, &sampler) != VK_SUCCESS)
-		throw love::Exception("failed to create sampler");
+	VkResult result = vkCreateSampler(device, &samplerInfo, nullptr, &sampler);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to create Vulkan sampler: %s", Vulkan::getErrorString(result));
 
 	return sampler;
 }
@@ -3006,8 +3016,9 @@ VkPipeline Graphics::createGraphicsPipeline(Shader *shader, const GraphicsPipeli
 	pipelineInfo.renderPass = configuration.renderPass;
 
 	VkPipeline graphicsPipeline;
-	if (vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS)
-		throw love::Exception("failed to create graphics pipeline");
+	VkResult result = vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineInfo, nullptr, &graphicsPipeline);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to create Vulkan graphics pipeline: %s", Vulkan::getErrorString(result));
 	return graphicsPipeline;
 }
 
@@ -3101,8 +3112,9 @@ void Graphics::createColorResources()
 		allocationInfo.usage = VMA_MEMORY_USAGE_AUTO;
 		allocationInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
 
-		if (vmaCreateImage(vmaAllocator, &imageInfo, &allocationInfo, &colorImage, &colorImageAllocation, nullptr))
-			throw love::Exception("failed to create color image");
+		VkResult result = vmaCreateImage(vmaAllocator, &imageInfo, &allocationInfo, &colorImage, &colorImageAllocation, nullptr);
+		if (result != VK_SUCCESS)
+			throw love::Exception("Failed to create Vulkan MSAA color image: %s", Vulkan::getErrorString(result));
 
 		VkImageViewCreateInfo imageViewInfo{};
 		imageViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
@@ -3119,8 +3131,9 @@ void Graphics::createColorResources()
 		imageViewInfo.subresourceRange.baseArrayLayer = 0;
 		imageViewInfo.subresourceRange.layerCount = 1;
 
-		if (vkCreateImageView(device, &imageViewInfo, nullptr, &colorImageView) != VK_SUCCESS)
-			throw love::Exception("failed to create color image view");
+		result = vkCreateImageView(device, &imageViewInfo, nullptr, &colorImageView);
+		if (result != VK_SUCCESS)
+			throw love::Exception("Failed to create Vulkan MSAA color image view: %s", Vulkan::getErrorString(result));
 	}
 }
 
@@ -3176,8 +3189,9 @@ void Graphics::createDepthResources()
 	allocationInfo.usage = VMA_MEMORY_USAGE_AUTO;
 	allocationInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
 
-	if (vmaCreateImage(vmaAllocator, &imageInfo, &allocationInfo, &depthImage, &depthImageAllocation, nullptr) != VK_SUCCESS)
-		throw love::Exception("failed to create depth image");
+	VkResult result = vmaCreateImage(vmaAllocator, &imageInfo, &allocationInfo, &depthImage, &depthImageAllocation, nullptr);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to create Vulkan backbuffer depth image: %s", Vulkan::getErrorString(result));
 
 	VkImageViewCreateInfo imageViewInfo{};
 	imageViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
@@ -3197,8 +3211,9 @@ void Graphics::createDepthResources()
 	imageViewInfo.subresourceRange.baseArrayLayer = 0;
 	imageViewInfo.subresourceRange.layerCount = 1;
 
-	if (vkCreateImageView(device, &imageViewInfo, nullptr, &depthImageView) != VK_SUCCESS)
-		throw love::Exception("failed to create depth image view");
+	result = vkCreateImageView(device, &imageViewInfo, nullptr, &depthImageView);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to create Vulkan backbuffer depth image view: %s", Vulkan::getErrorString(result));
 }
 
 void Graphics::createCommandPool()
@@ -3210,8 +3225,9 @@ void Graphics::createCommandPool()
 	poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value;
 	poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
 
-	if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS)
-		throw love::Exception("failed to create command pool");
+	VkResult result = vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to create Vulkan command pool: %s", Vulkan::getErrorString(result));
 }
 
 void Graphics::createCommandBuffers()
@@ -3224,8 +3240,9 @@ void Graphics::createCommandBuffers()
 	allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
 	allocInfo.commandBufferCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
 
-	if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS)
-		throw love::Exception("failed to allocate command buffers");
+	VkResult result = vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data());
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to allocate Vulkan command buffers: %s", Vulkan::getErrorString(result));
 }
 
 void Graphics::createSyncObjects()
@@ -3246,7 +3263,7 @@ void Graphics::createSyncObjects()
 		if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores.at(i)) != VK_SUCCESS ||
 			vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores.at(i)) != VK_SUCCESS ||
 			vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences.at(i)) != VK_SUCCESS)
-			throw love::Exception("failed to create synchronization objects for a frame!");
+			throw love::Exception("Failed to create Vulkan synchronization objects for a frame!");
 }
 
 void Graphics::cleanup()

+ 18 - 13
src/modules/graphics/vulkan/Shader.cpp

@@ -125,7 +125,7 @@ static VkShaderStageFlagBits getStageBit(ShaderStageType type)
 	case SHADERSTAGE_COMPUTE:
 		return VK_SHADER_STAGE_COMPUTE_BIT;
 	default:
-		throw love::Exception("invalid type");
+		throw love::Exception("Invalid shader stage type: %d", type);
 	}
 }
 
@@ -152,7 +152,7 @@ static EShLanguage getGlslShaderType(ShaderStageType stage)
 	case SHADERSTAGE_COMPUTE:
 		return EShLangCompute;
 	default:
-		throw love::Exception("unkonwn shader stage type");
+		throw love::Exception("Unknown shader stage type: %d", stage);
 	}
 }
 
@@ -708,8 +708,9 @@ void Shader::compileShaders()
 
 		VkShaderModule shaderModule;
 
-		if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS)
-			throw love::Exception("failed to create shader module");
+		VkResult result = vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule);
+		if (result != VK_SUCCESS)
+			throw love::Exception("Failed to create Vulkan shader module: %s", Vulkan::getErrorString(result));
 
 		std::string debugname = getShaderStageDebugName(shaderStage);
 		if (!debugname.empty() && vgfx->getEnabledOptionalInstanceExtensions().debugInfo)
@@ -963,8 +964,9 @@ void Shader::createDescriptorSetLayout()
 	layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
 	layoutInfo.pBindings = bindings.data();
 
-	if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS)
-		throw love::Exception("failed to create descriptor set layout");
+	VkResult result = vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to create Vulkan descriptor set layout: %s", Vulkan::getErrorString(result));
 }
 
 void Shader::createPipelineLayout()
@@ -975,8 +977,9 @@ void Shader::createPipelineLayout()
 	pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
 	pipelineLayoutInfo.pushConstantRangeCount = 0;
 
-	if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS)
-		throw love::Exception("failed to create pipeline layout");
+	VkResult result = vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to create Vulkan pipeline layout: %s", Vulkan::getErrorString(result));
 
 	if (isCompute)
 	{
@@ -987,8 +990,9 @@ void Shader::createPipelineLayout()
 		computeInfo.stage = shaderStages.at(0);
 		computeInfo.layout = pipelineLayout;
 
-		if (vkCreateComputePipelines(device, VK_NULL_HANDLE, 1, &computeInfo, nullptr, &computePipeline) != VK_SUCCESS)
-			throw love::Exception("failed to create compute pipeline");
+		result = vkCreateComputePipelines(device, VK_NULL_HANDLE, 1, &computeInfo, nullptr, &computePipeline);
+		if (result != VK_SUCCESS)
+			throw love::Exception("Failed to create Vulkan compute pipeline: %s", Vulkan::getErrorString(result));
 	}
 }
 
@@ -1090,8 +1094,9 @@ void Shader::createDescriptorPool()
 	createInfo.pPoolSizes = descriptorPoolSizes.data();
 
 	VkDescriptorPool pool;
-	if (vkCreateDescriptorPool(device, &createInfo, nullptr, &pool) != VK_SUCCESS)
-		throw love::Exception("failed to create descriptor pool");
+	VkResult result = vkCreateDescriptorPool(device, &createInfo, nullptr, &pool);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to create Vulkan descriptor pool: %s", Vulkan::getErrorString(result));
 
 	descriptorPools[currentFrame].push_back(pool);
 }
@@ -1122,7 +1127,7 @@ VkDescriptorSet Shader::allocateDescriptorSet()
 				createDescriptorPool();
 			continue;
 		default:
-			throw love::Exception("failed to allocate descriptor set");
+			throw love::Exception("Failed to allocate Vulkan descriptor set: %s", Vulkan::getErrorString(result));
 		}
 	}
 }

+ 4 - 3
src/modules/graphics/vulkan/StreamBuffer.cpp

@@ -37,7 +37,7 @@ static VkBufferUsageFlags getUsageFlags(BufferUsage mode)
 	case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
 	case BUFFERUSAGE_UNIFORM: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
 	default:
-		throw love::Exception("unsupported BufferUsage mode");
+		throw love::Exception("Unsupported BufferUsage mode: %d", mode);
 	}
 }
 
@@ -62,8 +62,9 @@ bool StreamBuffer::loadVolatile()
 	allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
 	allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
 
-	if (vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo) != VK_SUCCESS)
-		throw love::Exception("Cannot create stream buffer: out of graphics memory.");
+	VkResult result = vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Cannot create stream buffer: %s", Vulkan::getErrorString(result));
 
 	VkMemoryPropertyFlags properties;
 	vmaGetAllocationMemoryProperties(allocator, allocation, &properties);

+ 15 - 9
src/modules/graphics/vulkan/Texture.cpp

@@ -156,15 +156,17 @@ bool Texture::loadVolatile()
 
 		if ((msaaSamples & VK_SAMPLE_COUNT_1_BIT) != 0 || readable)
 		{
-			if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &imageData.image, &imageData.allocation, nullptr) != VK_SUCCESS)
-				throw love::Exception("failed to create image");
+			VkResult result = vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &imageData.image, &imageData.allocation, nullptr);
+			if (result != VK_SUCCESS)
+				throw love::Exception("Failed to create Vulkan image: %s", Vulkan::getErrorString(result));
 		}
 
 		if ((msaaSamples & VK_SAMPLE_COUNT_1_BIT) == 0)
 		{
 			imageInfo.samples = msaaSamples;
-			if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &msaaImageData.image, &msaaImageData.allocation, nullptr) != VK_SUCCESS)
-				throw love::Exception("failed to create MSAA image");
+			VkResult result = vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &msaaImageData.image, &msaaImageData.allocation, nullptr);
+			if (result != VK_SUCCESS)
+				throw love::Exception("Failed to create Vulkan MSAA image: %s", Vulkan::getErrorString(result));
 		}
 
 		auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
@@ -410,8 +412,9 @@ void Texture::createTextureImageView()
 	imageData.imageView = VK_NULL_HANDLE;
 	if (imageData.image != VK_NULL_HANDLE && readable)
 	{
-		if (vkCreateImageView(device, &viewInfo, nullptr, &imageData.imageView) != VK_SUCCESS)
-			throw love::Exception("could not create texture image view");
+		VkResult result = vkCreateImageView(device, &viewInfo, nullptr, &imageData.imageView);
+		if (result != VK_SUCCESS)
+			throw love::Exception("Could not create Vulkan texture image view: %s", Vulkan::getErrorString(result));
 	}
 }
 
@@ -450,8 +453,9 @@ void Texture::createRenderTargetImageViews(VulkanImageData &data)
 			viewInfo.components.b = vulkanFormat.swizzleB;
 			viewInfo.components.a = vulkanFormat.swizzleA;
 
-			if (vkCreateImageView(device, &viewInfo, nullptr, &data.renderTargetImageViews.at(mip).at(slice)) != VK_SUCCESS)
-				throw love::Exception("could not create render target image view");
+			VkResult result = vkCreateImageView(device, &viewInfo, nullptr, &data.renderTargetImageViews.at(mip).at(slice));
+			if (result != VK_SUCCESS)
+				throw love::Exception("Could not create Vulkan render target image view: %s", Vulkan::getErrorString(result));
 		}
 	}
 }
@@ -650,7 +654,9 @@ void Texture::uploadByteData(const void *data, size_t size, int level, int slice
 	allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
 
 	VmaAllocationInfo allocInfo;
-	vmaCreateBuffer(allocator, &bufferCreateInfo, &allocCreateInfo, &stagingBuffer, &vmaAllocation, &allocInfo);
+	VkResult result = vmaCreateBuffer(allocator, &bufferCreateInfo, &allocCreateInfo, &stagingBuffer, &vmaAllocation, &allocInfo);
+	if (result != VK_SUCCESS)
+		throw love::Exception("Failed to create Vulkan staging buffer for texture data upload: %s", Vulkan::getErrorString(result));
 
 	memcpy(allocInfo.pMappedData, data, size);
 

+ 32 - 0
src/modules/graphics/vulkan/Vulkan.cpp

@@ -47,6 +47,38 @@ void Vulkan::resetShaderSwitches()
 	numShaderSwitches = 0;
 }
 
+const char *Vulkan::getErrorString(VkResult result)
+{
+	switch (result)
+	{
+	case VK_SUCCESS: return "success";
+	case VK_ERROR_OUT_OF_HOST_MEMORY: return "out of host memory";
+	case VK_ERROR_OUT_OF_DEVICE_MEMORY: return "out of device graphics memory";
+	case VK_ERROR_INITIALIZATION_FAILED: return "initialization failed";
+	case VK_ERROR_DEVICE_LOST: return "device lost";
+	case VK_ERROR_MEMORY_MAP_FAILED: return "memory map failed";
+	case VK_ERROR_LAYER_NOT_PRESENT: return "layer not present";
+	case VK_ERROR_EXTENSION_NOT_PRESENT: return "extension not present";
+	case VK_ERROR_FEATURE_NOT_PRESENT: return "feature not present";
+	case VK_ERROR_INCOMPATIBLE_DRIVER: return "incompatible driver";
+	case VK_ERROR_TOO_MANY_OBJECTS: return "too many objects";
+	case VK_ERROR_FORMAT_NOT_SUPPORTED: return "format not supported";
+	case VK_ERROR_FRAGMENTED_POOL: return "fragmented pool";
+	case VK_ERROR_UNKNOWN: return "unknown error";
+	case VK_ERROR_OUT_OF_POOL_MEMORY: return "out of pool memory";
+	case VK_ERROR_INVALID_EXTERNAL_HANDLE: return "invalid external handle";
+	case VK_ERROR_FRAGMENTATION: return "fragmentation";
+	case VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS: return "invalid opaque capture address";
+	case VK_ERROR_SURFACE_LOST_KHR: return "surface lost";
+	case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: return "native window in use";
+	case VK_ERROR_OUT_OF_DATE_KHR: return "out of date";
+	case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: return "incompatible display";
+	case VK_ERROR_VALIDATION_FAILED_EXT: return "validation failed";
+	case VK_ERROR_INVALID_SHADER_NV: return "invalid shader";
+	default: return "unhandled error code";
+	}
+}
+
 VkFormat Vulkan::getVulkanVertexFormat(DataFormat format)
 {
 	switch (format)

+ 1 - 0
src/modules/graphics/vulkan/Vulkan.h

@@ -58,6 +58,7 @@ public:
 	static uint32_t getNumShaderSwitches();
 	static void resetShaderSwitches();
 
+	static const char *getErrorString(VkResult result);
 	static VkFormat getVulkanVertexFormat(DataFormat format);
 	static TextureFormat getTextureFormat(PixelFormat format);
 	static std::string getVendorName(uint32_t vendorId);