Skip to content

Commit 08b1d5c

Browse files
committed
Support VK_KHR_swapchain_mutable_format
When the device supports VK_KHR_swapchain_mutable_format, a surface configured with viewFormats no longer needs the blit fallback: the swapchain is created with VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR and a VkImageFormatListCreateInfo listing the base format and the viewFormats, and the wrapped texture descriptor carries the viewFormats so views can reinterpret the swapchain images directly. The SurfaceTests case is extended to render through the reinterpreted srgb view instead of only acquiring the texture. Change-Id: If6557d1600bb45f18c4889c794b924a8fdc49fe9
1 parent 9c46050 commit 08b1d5c

5 files changed

Lines changed: 77 additions & 10 deletions

File tree

src/dawn/native/vulkan/SwapChainVk.cpp

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,25 @@ MaybeError SwapChain::Initialize(SwapChainBase* previousSwapChain) {
195195
createInfo.clipped = VK_FALSE;
196196
createInfo.oldSwapchain = previousVkSwapChain;
197197

198+
// Create the swapchain images with VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT so they can be
199+
// reinterpreted to the configuration's viewFormats. VK_KHR_swapchain_mutable_format requires
200+
// the full list of formats to be provided, including the image format itself.
201+
VkImageFormatListCreateInfo imageFormatListInfo;
202+
std::vector<VkFormat> viewFormats;
203+
if (!mConfig.wgpuViewFormats.empty()) {
204+
DAWN_ASSERT(device->GetDeviceInfo().HasExt(DeviceExt::SwapchainMutableFormat));
205+
createInfo.flags |= VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR;
206+
viewFormats.push_back(mConfig.format);
207+
for (wgpu::TextureFormat viewFormat : mConfig.wgpuViewFormats) {
208+
viewFormats.push_back(VulkanImageFormat(device, viewFormat));
209+
}
210+
imageFormatListInfo.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO;
211+
imageFormatListInfo.pNext = nullptr;
212+
imageFormatListInfo.viewFormatCount = static_cast<uint32_t>(viewFormats.size());
213+
imageFormatListInfo.pViewFormats = viewFormats.data();
214+
createInfo.pNext = &imageFormatListInfo;
215+
}
216+
198217
DAWN_TRY(CheckVkSuccess(
199218
device->fn.CreateSwapchainKHR(device->GetVkDevice(), &createInfo, nullptr, &*mSwapChain),
200219
"CreateSwapChain"));
@@ -272,14 +291,20 @@ ResultOrError<SwapChain::Config> SwapChain::ChooseConfig(
272291
VkImageUsageFlags targetUsages =
273292
VulkanImageUsage(GetDevice(), GetUsage(), GetDevice()->GetValidInternalFormat(GetFormat()));
274293
VkImageUsageFlags supportedUsages = surfaceInfo.capabilities.supportedUsageFlags;
275-
// The swapchain images are also unable to satisfy viewFormats: they are
276-
// created without VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, so they cannot be
277-
// reinterpreted. The blit texture is a regular texture and can.
278-
if (!IsSubset(targetUsages, supportedUsages) || !GetViewFormats().empty()) {
279-
config.needsBlit = true;
280-
} else {
294+
// The swapchain images support the configuration's viewFormats only if they are created
295+
// with VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, which requires VK_KHR_swapchain_mutable_format.
296+
// Otherwise the blit texture, a regular texture, is used to support them.
297+
const bool viewFormatsSupported =
298+
GetViewFormats().empty() ||
299+
ToBackend(GetDevice())->GetDeviceInfo().HasExt(DeviceExt::SwapchainMutableFormat);
300+
if (IsSubset(targetUsages, supportedUsages) && viewFormatsSupported) {
281301
config.usage = targetUsages;
282302
config.wgpuUsage = GetUsage();
303+
// The swapchain will be created with VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR so the
304+
// images can be reinterpreted to these formats.
305+
config.wgpuViewFormats = GetViewFormats();
306+
} else {
307+
config.needsBlit = true;
283308
}
284309

285310
// Only support BGRA8Unorm (and RGBA8Unorm on android) with SRGB color space for now.
@@ -548,12 +573,13 @@ ResultOrError<SwapChainTextureInfo> SwapChain::GetCurrentTextureInternal(bool is
548573
}
549574
lastImage.lastAcquireDoneFence = std::move(acquireFence);
550575

551-
// Wait on the previous fence and destroy it.
576+
// Wrap the swapchain texture.
552577
TextureDescriptor textureDesc;
553578
textureDesc.size.width = mConfig.extent.width;
554579
textureDesc.size.height = mConfig.extent.height;
555580
textureDesc.format = mConfig.wgpuFormat;
556581
textureDesc.usage = mConfig.wgpuUsage;
582+
textureDesc.viewFormats = mConfig.wgpuViewFormats;
557583

558584
mTexture = SwapChainTexture::Create(device, Unpack(&textureDesc), lastImage.image);
559585

@@ -563,8 +589,9 @@ ResultOrError<SwapChainTextureInfo> SwapChain::GetCurrentTextureInternal(bool is
563589
return swapChainTextureInfo;
564590
}
565591

566-
// The blit texture always perfectly matches what the user requested for the swapchain.
567-
// We need to add the Vulkan TRANSFER_SRC flag for the vkCmdBlitImage call.
592+
// The blit texture always perfectly matches what the user requested for the swapchain,
593+
// including the viewFormats which GetSwapChainBaseTextureDescriptor() carries over. We need
594+
// to add the Vulkan TRANSFER_SRC flag for the vkCmdBlitImage call.
568595
TextureDescriptor desc = GetSwapChainBaseTextureDescriptor(this);
569596
DAWN_TRY_ASSIGN(mBlitTexture, InternalTexture::Create(device, Unpack(&desc),
570597
VK_IMAGE_USAGE_TRANSFER_SRC_BIT));

src/dawn/native/vulkan/SwapChainVk.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ class SwapChain : public SwapChainBase {
7171
// encapsulates the native swapchain texture.
7272
wgpu::TextureUsage wgpuUsage;
7373
wgpu::TextureFormat wgpuFormat;
74+
// When non-empty, the swapchain is created with
75+
// VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR so its images can be reinterpreted to
76+
// these formats, and the wrapped texture exposes them as its viewFormats.
77+
std::vector<wgpu::TextureFormat> wgpuViewFormats;
7478

7579
// Information about the blit workarounds we need to do (if any)
7680
bool needsBlit = false;

src/dawn/native/vulkan/VulkanExtensions.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ static constexpr std::array<DeviceExtInfo, kDeviceExtCount> sDeviceExtInfos{{
149149
{DeviceExt::DepthClipEnable, "VK_EXT_depth_clip_enable"},
150150
{DeviceExt::ImageDrmFormatModifier, "VK_EXT_image_drm_format_modifier"},
151151
{DeviceExt::Swapchain, "VK_KHR_swapchain"},
152+
{DeviceExt::SwapchainMutableFormat, "VK_KHR_swapchain_mutable_format"},
152153
{DeviceExt::QueueFamilyForeign, "VK_EXT_queue_family_foreign"},
153154
{DeviceExt::Robustness2, "VK_EXT_robustness2"},
154155
{DeviceExt::DisplayTiming, "VK_GOOGLE_display_timing"},
@@ -253,6 +254,12 @@ DeviceExtSet EnsureDependencies(const DeviceExtSet& advertisedExts,
253254
hasDependencies = instanceExts[InstanceExt::Surface];
254255
break;
255256

257+
// Also requires VK_KHR_maintenance2 which is core in Vulkan 1.1.
258+
case DeviceExt::SwapchainMutableFormat:
259+
hasDependencies =
260+
HasDep(DeviceExt::Swapchain) && HasDep(DeviceExt::ImageFormatList);
261+
break;
262+
256263
case DeviceExt::ExternalMemoryAndroidHardwareBuffer:
257264
hasDependencies = HasDep(DeviceExt::QueueFamilyForeign);
258265
break;

src/dawn/native/vulkan/VulkanExtensions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ enum class DeviceExt : uint32_t {
108108
DepthClipEnable,
109109
ImageDrmFormatModifier,
110110
Swapchain,
111+
SwapchainMutableFormat,
111112
QueueFamilyForeign,
112113
Robustness2,
113114
DisplayTiming,

src/dawn/tests/end2end/SurfaceTests.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,8 @@ TEST_P(SurfaceTests, Storage) {
722722
// Test acquiring a texture from a surface configured with viewFormats.
723723
TEST_P(SurfaceTests, ConfigureWithViewFormats) {
724724
wgpu::Surface surface = CreateTestSurface();
725+
wgpu::SurfaceCapabilities caps;
726+
surface.GetCapabilities(adapter, &caps);
725727
wgpu::SurfaceConfiguration config = GetPreferredConfiguration(surface);
726728

727729
// Reinterpretation between a format and its srgb counterpart is always
@@ -739,11 +741,37 @@ TEST_P(SurfaceTests, ConfigureWithViewFormats) {
739741
}
740742
config.viewFormatCount = 1;
741743
config.viewFormats = &viewFormat;
744+
// When supported, also request CopySrc so the reinterpreted values can be read back.
745+
if (caps.usages & wgpu::TextureUsage::CopySrc) {
746+
config.usage |= wgpu::TextureUsage::CopySrc;
747+
}
742748
surface.Configure(&config);
743749

744750
wgpu::SurfaceTexture surfaceTexture;
745751
surface.GetCurrentTexture(&surfaceTexture); // aborts on Vulkan before the fix
746-
ClearTexture(surfaceTexture.texture, {1.0, 0.0, 0.0, 1.0});
752+
753+
// Clear through a view using the reinterpreted format to check the texture really
754+
// supports its viewFormats.
755+
wgpu::TextureViewDescriptor viewDesc;
756+
viewDesc.format = viewFormat;
757+
utils::ComboRenderPassDescriptor renderPassDesc({surfaceTexture.texture.CreateView(&viewDesc)});
758+
renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
759+
renderPassDesc.cColorAttachments[0].clearValue = {0.5, 0.5, 0.5, 1.0};
760+
761+
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
762+
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
763+
pass.End();
764+
wgpu::CommandBuffer commands = encoder.Finish();
765+
queue.Submit(1, &commands);
766+
767+
if (surfaceTexture.texture.GetUsage() & wgpu::TextureUsage::CopySrc) {
768+
// The sRGB view encodes the linear 0.5 clear value to ~0.735 on store, so the raw
769+
// non-sRGB pixels read back as ~187.5 instead of 128 if the reinterpretation took
770+
// effect. A gray value keeps the check independent of the BGRA/RGBA channel order.
771+
EXPECT_PIXEL_RGBA8_BETWEEN(utils::RGBA8(187, 187, 187, 255),
772+
utils::RGBA8(188, 188, 188, 255), surfaceTexture.texture, 0, 0);
773+
}
774+
747775
surface.Present();
748776
}
749777

0 commit comments

Comments
 (0)