Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 36 additions & 6 deletions src/dawn/native/vulkan/SwapChainVk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<VkFormat> 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<uint32_t>(viewFormats.size());
imageFormatListInfo.pViewFormats = viewFormats.data();
createInfo.pNext = &imageFormatListInfo;
}

DAWN_TRY(CheckVkSuccess(
device->fn.CreateSwapchainKHR(device->GetVkDevice(), &createInfo, nullptr, &*mSwapChain),
"CreateSwapChain"));
Expand Down Expand Up @@ -272,11 +291,20 @@ ResultOrError<SwapChain::Config> 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.
Expand Down Expand Up @@ -545,12 +573,13 @@ ResultOrError<SwapChainTextureInfo> 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);

Expand All @@ -560,8 +589,9 @@ ResultOrError<SwapChainTextureInfo> 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));
Expand Down
4 changes: 4 additions & 0 deletions src/dawn/native/vulkan/SwapChainVk.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<wgpu::TextureFormat> wgpuViewFormats;

// Information about the blit workarounds we need to do (if any)
bool needsBlit = false;
Expand Down
7 changes: 7 additions & 0 deletions src/dawn/native/vulkan/VulkanExtensions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ static constexpr std::array<DeviceExtInfo, kDeviceExtCount> 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"},
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/dawn/native/vulkan/VulkanExtensions.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ enum class DeviceExt : uint32_t {
DepthClipEnable,
ImageDrmFormatModifier,
Swapchain,
SwapchainMutableFormat,
QueueFamilyForeign,
Robustness2,
DisplayTiming,
Expand Down
56 changes: 56 additions & 0 deletions src/dawn/tests/end2end/SurfaceTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
Loading