Merge pull request 'fix(vulkan): clear startup validation errors on native triangle' (#6) from claude/issue-5 into master
This commit is contained in:
commit
26a41ac528
3 changed files with 41 additions and 94 deletions
|
|
@ -721,30 +721,11 @@ void Device::Initialize() {
|
|||
deviceCreateInfo.ppEnabledExtensionNames = enabledDeviceExtensions.data();
|
||||
deviceCreateInfo.pNext = &physical_features2;
|
||||
|
||||
uint32_t deviceLayerCount;
|
||||
CheckVkResult(vkEnumerateDeviceLayerProperties(physDevice, &deviceLayerCount, NULL));
|
||||
|
||||
std::vector<VkLayerProperties> deviceLayerProperties(deviceLayerCount);
|
||||
CheckVkResult(vkEnumerateDeviceLayerProperties(physDevice, &deviceLayerCount, deviceLayerProperties.data()));
|
||||
|
||||
size_t foundDeviceLayers = 0;
|
||||
|
||||
for (uint32_t i = 0; i < deviceLayerCount; i++)
|
||||
{
|
||||
for (size_t j = 0; j < sizeof(layerNames) / sizeof(const char*); j++)
|
||||
{
|
||||
if (std::strcmp(deviceLayerProperties[i].layerName, layerNames[j]) == 0)
|
||||
{
|
||||
foundDeviceLayers++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundDeviceLayers >= sizeof(layerNames) / sizeof(const char*))
|
||||
{
|
||||
deviceCreateInfo.enabledLayerCount = sizeof(layerNames) / sizeof(const char*);
|
||||
deviceCreateInfo.ppEnabledLayerNames = layerNames;
|
||||
}
|
||||
// Device layers are deprecated and have been ignored since Vulkan 1.0;
|
||||
// enabling them is a validation error. Layers are enabled at instance
|
||||
// creation only, so leave enabledLayerCount at 0.
|
||||
deviceCreateInfo.enabledLayerCount = 0;
|
||||
deviceCreateInfo.ppEnabledLayerNames = nullptr;
|
||||
|
||||
CheckVkResult(vkCreateDevice(physDevice, &deviceCreateInfo, NULL, &device));
|
||||
vkGetDeviceQueue(device, queueFamilyIndex, 0, &queue);
|
||||
|
|
|
|||
|
|
@ -451,56 +451,13 @@ void Window::Resize(std::uint32_t newWidth, std::uint32_t newHeight) {
|
|||
}
|
||||
|
||||
void Window::RecreateSwapchainAndImages() {
|
||||
// CreateSwapchain leaves the new swapchain images in
|
||||
// VK_IMAGE_LAYOUT_UNDEFINED and resets imageInitialised. The images are
|
||||
// NOT transitioned here: a presentable image may only be touched after
|
||||
// vkAcquireNextImageKHR returns it, so Render() performs each image's
|
||||
// first layout transition lazily (from UNDEFINED) once it has been
|
||||
// acquired. Pre-transitioning unacquired images is a validation error.
|
||||
CreateSwapchain();
|
||||
|
||||
// CreateSwapchain leaves new swapchain images in VK_IMAGE_LAYOUT_UNDEFINED.
|
||||
// Render() barriers from PRESENT_SRC_KHR, so transition them now to
|
||||
// match. Mirrors the StartInit logic, on a one-shot command buffer.
|
||||
{
|
||||
VkCommandBufferAllocateInfo cba{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.commandPool = Device::commandPool,
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandBufferCount = 1,
|
||||
};
|
||||
VkCommandBuffer cmd = VK_NULL_HANDLE;
|
||||
Device::CheckVkResult(vkAllocateCommandBuffers(Device::device, &cba, &cmd));
|
||||
|
||||
VkCommandBufferBeginInfo cbi{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
};
|
||||
Device::CheckVkResult(vkBeginCommandBuffer(cmd, &cbi));
|
||||
|
||||
VkImageSubresourceRange range{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
};
|
||||
for (std::uint32_t i = 0; i < numFrames; i++) {
|
||||
image_layout_transition(cmd,
|
||||
images[i],
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||||
0, 0,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
range);
|
||||
}
|
||||
|
||||
Device::CheckVkResult(vkEndCommandBuffer(cmd));
|
||||
|
||||
VkSubmitInfo si{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &cmd,
|
||||
};
|
||||
Device::CheckVkResult(vkQueueSubmit(Device::queue, 1, &si, VK_NULL_HANDLE));
|
||||
Device::CheckVkResult(vkQueueWaitIdle(Device::queue));
|
||||
vkFreeCommandBuffers(Device::device, Device::commandPool, 1, &cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::SetTitle(const std::string_view title) {
|
||||
|
|
@ -724,11 +681,20 @@ void Window::Render() {
|
|||
range.baseArrayLayer = 0;
|
||||
range.layerCount = VK_REMAINING_ARRAY_LAYERS;
|
||||
|
||||
// On an image's first use after (re)creating the swapchain it is still in
|
||||
// VK_IMAGE_LAYOUT_UNDEFINED; every subsequent frame leaves it in
|
||||
// PRESENT_SRC_KHR. Transitioning from the wrong oldLayout is a validation
|
||||
// error, so pick based on whether this image has been rendered before.
|
||||
// The image was just acquired above, so touching it here is legal.
|
||||
const bool firstUse = !imageInitialised[currentBuffer];
|
||||
imageInitialised[currentBuffer] = true;
|
||||
|
||||
VkImageMemoryBarrier image_memory_barrier {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = 0,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
.oldLayout = firstUse ? VK_IMAGE_LAYOUT_UNDEFINED
|
||||
: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
|
|
@ -980,6 +946,11 @@ void Window::CreateSwapchain()
|
|||
// Get the swap chain images
|
||||
Device::CheckVkResult(vkGetSwapchainImagesKHR(Device::device, swapChain, &imageCount, images));
|
||||
|
||||
// Brand-new swapchain images are in VK_IMAGE_LAYOUT_UNDEFINED; none have
|
||||
// been rendered/presented yet. Render() consults this to pick the correct
|
||||
// oldLayout for each image's first post-acquire transition.
|
||||
imageInitialised.fill(false);
|
||||
|
||||
for (std::uint8_t i = 0; i < numFrames; i++) {
|
||||
imageViews[i] = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
|
|
@ -1009,26 +980,13 @@ VkCommandBuffer Window::StartInit() {
|
|||
cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
Device::CheckVkResult(vkBeginCommandBuffer(drawCmdBuffers[currentBuffer], &cmdBufInfo));
|
||||
|
||||
VkImageSubresourceRange range{};
|
||||
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
range.baseMipLevel = 0;
|
||||
range.levelCount = VK_REMAINING_MIP_LEVELS;
|
||||
range.baseArrayLayer = 0;
|
||||
range.layerCount = VK_REMAINING_ARRAY_LAYERS;
|
||||
|
||||
for(std::uint32_t i = 0; i < numFrames; i++) {
|
||||
image_layout_transition(drawCmdBuffers[currentBuffer],
|
||||
images[i],
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||||
0,
|
||||
0,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
range
|
||||
);
|
||||
}
|
||||
|
||||
// The swapchain images are deliberately NOT transitioned here. They are
|
||||
// presentable images and may only be touched after vkAcquireNextImageKHR
|
||||
// returns them; transitioning an unacquired presentable image is a
|
||||
// validation error. Render() instead transitions each image from
|
||||
// VK_IMAGE_LAYOUT_UNDEFINED on its first acquired use (see
|
||||
// imageInitialised). This command buffer is for the caller's one-time
|
||||
// setup work (uploads, acceleration-structure builds, etc.).
|
||||
return drawCmdBuffers[currentBuffer];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -239,6 +239,14 @@ export namespace Crafter {
|
|||
VkColorSpaceKHR colorSpace;
|
||||
VkImage images[numFrames];
|
||||
VkImageViewCreateInfo imageViews[numFrames];
|
||||
// Tracks whether each swapchain image has been rendered (and thus
|
||||
// left in PRESENT_SRC_KHR) at least once. Freshly created swapchain
|
||||
// images start in VK_IMAGE_LAYOUT_UNDEFINED, so the first per-frame
|
||||
// barrier must transition from UNDEFINED, not PRESENT_SRC_KHR.
|
||||
// Reset in CreateSwapchain(). A presentable image may only be touched
|
||||
// after it has been acquired, so this initial transition happens
|
||||
// lazily in Render() (post-acquire) rather than up front.
|
||||
std::array<bool, numFrames> imageInitialised{};
|
||||
std::thread thread;
|
||||
VkCommandBuffer drawCmdBuffers[numFrames];
|
||||
VkSubmitInfo submitInfo;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue