diff --git a/src/dawn/native/vulkan/SwapChainVk.cpp b/src/dawn/native/vulkan/SwapChainVk.cpp index 7aeb73bc47..aae18ffae5 100644 --- a/src/dawn/native/vulkan/SwapChainVk.cpp +++ b/src/dawn/native/vulkan/SwapChainVk.cpp @@ -195,6 +195,19 @@ MaybeError SwapChain::Initialize(SwapChainBase* previousSwapChain) { createInfo.clipped = VK_FALSE; createInfo.oldSwapchain = previousVkSwapChain; + // Create the swapchain images with VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT so they can be + // reinterpreted to the configuration's viewFormats. VK_KHR_swapchain_mutable_format + // requires the full list of formats to be provided, including the image format itself. + VkImageFormatListCreateInfo imageFormatListInfo; + if (!mConfig.viewFormats.empty()) { + createInfo.flags |= VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR; + imageFormatListInfo.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO; + imageFormatListInfo.pNext = nullptr; + imageFormatListInfo.viewFormatCount = static_cast(mConfig.viewFormats.size()); + imageFormatListInfo.pViewFormats = mConfig.viewFormats.data(); + createInfo.pNext = &imageFormatListInfo; + } + DAWN_TRY(CheckVkSuccess( device->fn.CreateSwapchainKHR(device->GetVkDevice(), &createInfo, nullptr, &*mSwapChain), "CreateSwapChain")); @@ -272,7 +285,13 @@ ResultOrError SwapChain::ChooseConfig( VkImageUsageFlags targetUsages = VulkanImageUsage(GetDevice(), GetUsage(), GetDevice()->GetValidInternalFormat(GetFormat())); VkImageUsageFlags supportedUsages = surfaceInfo.capabilities.supportedUsageFlags; - if (!IsSubset(targetUsages, supportedUsages)) { + // Without VK_KHR_swapchain_mutable_format the swapchain images are created + // without VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, so they cannot be reinterpreted + // to the configuration's viewFormats. The blit texture is a regular texture and can. + const bool viewFormatsRequireBlit = + !GetViewFormats().empty() && + !ToBackend(GetDevice())->GetDeviceInfo().HasExt(DeviceExt::SwapchainMutableFormat); + if (!IsSubset(targetUsages, supportedUsages) || viewFormatsRequireBlit) { config.needsBlit = true; } else { config.usage = targetUsages; @@ -296,6 +315,18 @@ ResultOrError SwapChain::ChooseConfig( "Vulkan SwapChain must support %s with sRGB colorspace.", config.wgpuFormat)); } + // If the swapchain images are exposed to the user directly and need to be reinterpreted + // to the configuration's viewFormats, gather the full list of formats used to create the + // swapchain with VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR. + if (!config.needsBlit && !GetViewFormats().empty()) { + DAWN_ASSERT( + ToBackend(GetDevice())->GetDeviceInfo().HasExt(DeviceExt::SwapchainMutableFormat)); + config.viewFormats.push_back(config.format); + for (wgpu::TextureFormat viewFormat : GetViewFormats()) { + config.viewFormats.push_back(VulkanImageFormat(ToBackend(GetDevice()), viewFormat)); + } + } + // Only the identity transform with opaque alpha is supported for now. DAWN_INVALID_IF( (surfaceInfo.capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) == 0, @@ -551,6 +582,11 @@ ResultOrError SwapChain::GetCurrentTextureInternal(bool is textureDesc.size.height = mConfig.extent.height; textureDesc.format = mConfig.wgpuFormat; textureDesc.usage = mConfig.wgpuUsage; + if (!mConfig.needsBlit) { + // The swapchain images were created mutable so the user-facing texture supports the + // configuration's viewFormats. In the blit case they stay on the blit texture instead. + textureDesc.viewFormats = GetViewFormats(); + } mTexture = SwapChainTexture::Create(device, Unpack(&textureDesc), lastImage.image); diff --git a/src/dawn/native/vulkan/SwapChainVk.h b/src/dawn/native/vulkan/SwapChainVk.h index 3dc1106ec7..0fc8aa6dbb 100644 --- a/src/dawn/native/vulkan/SwapChainVk.h +++ b/src/dawn/native/vulkan/SwapChainVk.h @@ -66,6 +66,11 @@ class SwapChain : public SwapChainBase { uint32_t targetImageCount; VkSurfaceTransformFlagBitsKHR transform; VkCompositeAlphaFlagBitsKHR alphaMode; + // When non-empty, the swapchain is created with + // VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR and this list (which includes `format`) + // is chained as a VkImageFormatListCreateInfo so the images can be reinterpreted to + // the configuration's viewFormats. + std::vector viewFormats; // Redundant information but as WebGPU enums to create the wgpu::Texture that // encapsulates the native swapchain texture. diff --git a/src/dawn/native/vulkan/VulkanExtensions.cpp b/src/dawn/native/vulkan/VulkanExtensions.cpp index 8ce610dc4f..9f232221c6 100644 --- a/src/dawn/native/vulkan/VulkanExtensions.cpp +++ b/src/dawn/native/vulkan/VulkanExtensions.cpp @@ -149,6 +149,7 @@ static constexpr std::array sDeviceExtInfos{{ {DeviceExt::DepthClipEnable, "VK_EXT_depth_clip_enable"}, {DeviceExt::ImageDrmFormatModifier, "VK_EXT_image_drm_format_modifier"}, {DeviceExt::Swapchain, "VK_KHR_swapchain"}, + {DeviceExt::SwapchainMutableFormat, "VK_KHR_swapchain_mutable_format"}, {DeviceExt::QueueFamilyForeign, "VK_EXT_queue_family_foreign"}, {DeviceExt::Robustness2, "VK_EXT_robustness2"}, {DeviceExt::DisplayTiming, "VK_GOOGLE_display_timing"}, @@ -253,6 +254,12 @@ DeviceExtSet EnsureDependencies(const DeviceExtSet& advertisedExts, hasDependencies = instanceExts[InstanceExt::Surface]; break; + // Also requires VK_KHR_maintenance2 which is core in Vulkan 1.1. + case DeviceExt::SwapchainMutableFormat: + hasDependencies = + HasDep(DeviceExt::Swapchain) && HasDep(DeviceExt::ImageFormatList); + break; + case DeviceExt::ExternalMemoryAndroidHardwareBuffer: hasDependencies = HasDep(DeviceExt::QueueFamilyForeign); break; diff --git a/src/dawn/native/vulkan/VulkanExtensions.h b/src/dawn/native/vulkan/VulkanExtensions.h index 78d79a4c64..830782263d 100644 --- a/src/dawn/native/vulkan/VulkanExtensions.h +++ b/src/dawn/native/vulkan/VulkanExtensions.h @@ -108,6 +108,7 @@ enum class DeviceExt : uint32_t { DepthClipEnable, ImageDrmFormatModifier, Swapchain, + SwapchainMutableFormat, QueueFamilyForeign, Robustness2, DisplayTiming, diff --git a/src/dawn/tests/end2end/SurfaceTests.cpp b/src/dawn/tests/end2end/SurfaceTests.cpp index 0fbeeaf024..32e13801c4 100644 --- a/src/dawn/tests/end2end/SurfaceTests.cpp +++ b/src/dawn/tests/end2end/SurfaceTests.cpp @@ -719,6 +719,49 @@ TEST_P(SurfaceTests, Storage) { ASSERT_EQ(wgpu::Status::Success, surface.Present()); } +// Test acquiring a texture from a surface configured with viewFormats. +TEST_P(SurfaceTests, ConfigureWithViewFormats) { + wgpu::Surface surface = CreateTestSurface(); + wgpu::SurfaceConfiguration config = GetPreferredConfiguration(surface); + + // Reinterpretation between a format and its srgb counterpart is always + // allowed; pick the counterpart of whatever the surface prefers. + wgpu::TextureFormat viewFormat; + switch (config.format) { + case wgpu::TextureFormat::BGRA8Unorm: + viewFormat = wgpu::TextureFormat::BGRA8UnormSrgb; + break; + case wgpu::TextureFormat::RGBA8Unorm: + viewFormat = wgpu::TextureFormat::RGBA8UnormSrgb; + break; + default: + GTEST_SKIP() << "Preferred surface format has no srgb counterpart"; + } + config.viewFormatCount = 1; + config.viewFormats = &viewFormat; + surface.Configure(&config); + + wgpu::SurfaceTexture surfaceTexture; + surface.GetCurrentTexture(&surfaceTexture); // aborts on Vulkan before the fix + + // Render through a view using the reinterpreted format to check the texture really + // supports its viewFormats. + wgpu::TextureViewDescriptor viewDesc; + viewDesc.format = viewFormat; + utils::ComboRenderPassDescriptor renderPassDesc( + {surfaceTexture.texture.CreateView(&viewDesc)}); + renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; + renderPassDesc.cColorAttachments[0].clearValue = {1.0, 0.0, 0.0, 1.0}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); + pass.End(); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + surface.Present(); +} + // TODO(crbug.com/465183957): Implement swap chain for WebGPUBackend. DAWN_INSTANTIATE_TEST(SurfaceTests, D3D11Backend(),