diff --git a/src/dawn/native/vulkan/SwapChainVk.cpp b/src/dawn/native/vulkan/SwapChainVk.cpp index 7aeb73bc475..6312db22b39 100644 --- a/src/dawn/native/vulkan/SwapChainVk.cpp +++ b/src/dawn/native/vulkan/SwapChainVk.cpp @@ -195,6 +195,25 @@ 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; + std::vector viewFormats; + if (!mConfig.wgpuViewFormats.empty()) { + DAWN_ASSERT(device->GetDeviceInfo().HasExt(DeviceExt::SwapchainMutableFormat)); + createInfo.flags |= VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR; + viewFormats.push_back(mConfig.format); + for (wgpu::TextureFormat viewFormat : mConfig.wgpuViewFormats) { + viewFormats.push_back(VulkanImageFormat(device, viewFormat)); + } + imageFormatListInfo.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO; + imageFormatListInfo.pNext = nullptr; + imageFormatListInfo.viewFormatCount = static_cast(viewFormats.size()); + imageFormatListInfo.pViewFormats = viewFormats.data(); + createInfo.pNext = &imageFormatListInfo; + } + DAWN_TRY(CheckVkSuccess( device->fn.CreateSwapchainKHR(device->GetVkDevice(), &createInfo, nullptr, &*mSwapChain), "CreateSwapChain")); @@ -272,11 +291,20 @@ ResultOrError SwapChain::ChooseConfig( VkImageUsageFlags targetUsages = VulkanImageUsage(GetDevice(), GetUsage(), GetDevice()->GetValidInternalFormat(GetFormat())); VkImageUsageFlags supportedUsages = surfaceInfo.capabilities.supportedUsageFlags; - if (!IsSubset(targetUsages, supportedUsages)) { - config.needsBlit = true; - } else { + // The swapchain images support the configuration's viewFormats only if they are created + // with VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, which requires VK_KHR_swapchain_mutable_format. + // Otherwise the blit texture, a regular texture, is used to support them. + const bool viewFormatsSupported = + GetViewFormats().empty() || + ToBackend(GetDevice())->GetDeviceInfo().HasExt(DeviceExt::SwapchainMutableFormat); + if (IsSubset(targetUsages, supportedUsages) && viewFormatsSupported) { config.usage = targetUsages; config.wgpuUsage = GetUsage(); + // The swapchain will be created with VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR so the + // images can be reinterpreted to these formats. + config.wgpuViewFormats = GetViewFormats(); + } else { + config.needsBlit = true; } // Only support BGRA8Unorm (and RGBA8Unorm on android) with SRGB color space for now. @@ -545,12 +573,13 @@ ResultOrError SwapChain::GetCurrentTextureInternal(bool is } lastImage.lastAcquireDoneFence = std::move(acquireFence); - // Wait on the previous fence and destroy it. + // Wrap the swapchain texture. TextureDescriptor textureDesc; textureDesc.size.width = mConfig.extent.width; textureDesc.size.height = mConfig.extent.height; textureDesc.format = mConfig.wgpuFormat; textureDesc.usage = mConfig.wgpuUsage; + textureDesc.viewFormats = mConfig.wgpuViewFormats; mTexture = SwapChainTexture::Create(device, Unpack(&textureDesc), lastImage.image); @@ -560,8 +589,9 @@ ResultOrError SwapChain::GetCurrentTextureInternal(bool is return swapChainTextureInfo; } - // The blit texture always perfectly matches what the user requested for the swapchain. - // We need to add the Vulkan TRANSFER_SRC flag for the vkCmdBlitImage call. + // The blit texture always perfectly matches what the user requested for the swapchain, + // including the viewFormats which GetSwapChainBaseTextureDescriptor() carries over. We need + // to add the Vulkan TRANSFER_SRC flag for the vkCmdBlitImage call. TextureDescriptor desc = GetSwapChainBaseTextureDescriptor(this); DAWN_TRY_ASSIGN(mBlitTexture, InternalTexture::Create(device, Unpack(&desc), VK_IMAGE_USAGE_TRANSFER_SRC_BIT)); diff --git a/src/dawn/native/vulkan/SwapChainVk.h b/src/dawn/native/vulkan/SwapChainVk.h index 3dc1106ec76..49cfcf8a5a8 100644 --- a/src/dawn/native/vulkan/SwapChainVk.h +++ b/src/dawn/native/vulkan/SwapChainVk.h @@ -71,6 +71,10 @@ class SwapChain : public SwapChainBase { // encapsulates the native swapchain texture. wgpu::TextureUsage wgpuUsage; wgpu::TextureFormat wgpuFormat; + // When non-empty, the swapchain is created with + // VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR so its images can be reinterpreted to + // these formats, and the wrapped texture exposes them as its viewFormats. + std::vector wgpuViewFormats; // Information about the blit workarounds we need to do (if any) bool needsBlit = false; diff --git a/src/dawn/native/vulkan/VulkanExtensions.cpp b/src/dawn/native/vulkan/VulkanExtensions.cpp index 8ce610dc4f4..9f232221c6c 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 78d79a4c644..830782263df 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 0fbeeaf0249..d2b81fe5fe4 100644 --- a/src/dawn/tests/end2end/SurfaceTests.cpp +++ b/src/dawn/tests/end2end/SurfaceTests.cpp @@ -719,6 +719,62 @@ 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::SurfaceCapabilities caps; + surface.GetCapabilities(adapter, &caps); + 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; + // When supported, also request CopySrc so the reinterpreted values can be read back. + if (caps.usages & wgpu::TextureUsage::CopySrc) { + config.usage |= wgpu::TextureUsage::CopySrc; + } + surface.Configure(&config); + + wgpu::SurfaceTexture surfaceTexture; + surface.GetCurrentTexture(&surfaceTexture); // aborts on Vulkan before the fix + + // Clear 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 = {0.5, 0.5, 0.5, 1.0}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); + pass.End(); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + if (surfaceTexture.texture.GetUsage() & wgpu::TextureUsage::CopySrc) { + // The sRGB view encodes the linear 0.5 clear value to ~0.735 on store, so the raw + // non-sRGB pixels read back as ~187.5 instead of 128 if the reinterpretation took + // effect. A gray value keeps the check independent of the BGRA/RGBA channel order. + EXPECT_PIXEL_RGBA8_BETWEEN(utils::RGBA8(187, 187, 187, 255), + utils::RGBA8(188, 188, 188, 255), surfaceTexture.texture, 0, 0); + } + + surface.Present(); +} + // TODO(crbug.com/465183957): Implement swap chain for WebGPUBackend. DAWN_INSTANTIATE_TEST(SurfaceTests, D3D11Backend(),