mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-06 14:43:31 +00:00
Implement the webgpu.h swapchains in the frontend and Null backend.
The state-tracking of the webgpu.h swapchain is a bit complicated because contrary to implementation-based swapchains, they have more guarantees and a "replacing mechanism". For example instead of hoping the implementation-based swapchain resize automatically, the surface-based swapchain needs to be replaced by a new swapchain and invalidated. This mechanism of invalidation also needs to be triggered when the last reference to the surface is lost because we don't want to risk the application destroying the window from under us. Adds tests for all the cases of invalidation I could think of apart from device loss. Bug: dawn:269 Change-Id: Id515dbb640e13c6e30bb1f1e93b8e54f1e2bba4b Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/15400 Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Kai Ninomiya <kainino@chromium.org>
This commit is contained in:
parent
0df4753ba6
commit
d26ee85fba
@ -35,6 +35,7 @@
|
||||
#include "dawn_native/RenderPipeline.h"
|
||||
#include "dawn_native/Sampler.h"
|
||||
#include "dawn_native/ShaderModule.h"
|
||||
#include "dawn_native/Surface.h"
|
||||
#include "dawn_native/SwapChain.h"
|
||||
#include "dawn_native/Texture.h"
|
||||
#include "dawn_native/ValidationUtils_autogen.h"
|
||||
@ -833,7 +834,19 @@ namespace dawn_native {
|
||||
DAWN_TRY_ASSIGN(*result, CreateSwapChainImpl(descriptor));
|
||||
} else {
|
||||
ASSERT(descriptor->implementation == 0);
|
||||
DAWN_TRY_ASSIGN(*result, CreateSwapChainImpl(surface, descriptor));
|
||||
|
||||
NewSwapChainBase* previousSwapChain = surface->GetAttachedSwapChain();
|
||||
NewSwapChainBase* newSwapChain;
|
||||
DAWN_TRY_ASSIGN(newSwapChain,
|
||||
CreateSwapChainImpl(surface, previousSwapChain, descriptor));
|
||||
|
||||
if (previousSwapChain != nullptr) {
|
||||
ASSERT(!previousSwapChain->IsAttached());
|
||||
}
|
||||
ASSERT(newSwapChain->IsAttached());
|
||||
|
||||
surface->SetAttachedSwapChain(newSwapChain);
|
||||
*result = newSwapChain;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
@ -224,8 +224,10 @@ namespace dawn_native {
|
||||
const ShaderModuleDescriptor* descriptor) = 0;
|
||||
virtual ResultOrError<SwapChainBase*> CreateSwapChainImpl(
|
||||
const SwapChainDescriptor* descriptor) = 0;
|
||||
virtual ResultOrError<SwapChainBase*> CreateSwapChainImpl(
|
||||
// Note that previousSwapChain may be nullptr, or come from a different backend.
|
||||
virtual ResultOrError<NewSwapChainBase*> CreateSwapChainImpl(
|
||||
Surface* surface,
|
||||
NewSwapChainBase* previousSwapChain,
|
||||
const SwapChainDescriptor* descriptor) = 0;
|
||||
virtual ResultOrError<TextureBase*> CreateTextureImpl(
|
||||
const TextureDescriptor* descriptor) = 0;
|
||||
|
@ -42,6 +42,7 @@ namespace dawn_native {
|
||||
class ShaderModuleBase;
|
||||
class StagingBufferBase;
|
||||
class SwapChainBase;
|
||||
class NewSwapChainBase;
|
||||
class TextureBase;
|
||||
class TextureViewBase;
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "common/Platform.h"
|
||||
#include "dawn_native/Instance.h"
|
||||
#include "dawn_native/SwapChain.h"
|
||||
|
||||
#if defined(DAWN_PLATFORM_WINDOWS)
|
||||
# include "common/windows_with_undefs.h"
|
||||
@ -137,7 +138,20 @@ namespace dawn_native {
|
||||
}
|
||||
}
|
||||
|
||||
Surface::~Surface() = default;
|
||||
Surface::~Surface() {
|
||||
if (mSwapChain != nullptr) {
|
||||
mSwapChain->DetachFromSurface();
|
||||
mSwapChain = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NewSwapChainBase* Surface::GetAttachedSwapChain() const {
|
||||
return mSwapChain;
|
||||
}
|
||||
|
||||
void Surface::SetAttachedSwapChain(NewSwapChainBase* swapChain) {
|
||||
mSwapChain = swapChain;
|
||||
}
|
||||
|
||||
InstanceBase* Surface::GetInstance() {
|
||||
return mInstance.Get();
|
||||
|
@ -29,11 +29,16 @@ namespace dawn_native {
|
||||
// A surface is a sum types of all the kind of windows Dawn supports. The OS-specific types
|
||||
// aren't used because they would cause compilation errors on other OSes (or require
|
||||
// ObjectiveC).
|
||||
// The surface is also used to store the current swapchain so that we can detach it when it is
|
||||
// replaced.
|
||||
class Surface final : public RefCounted {
|
||||
public:
|
||||
Surface(InstanceBase* instance, const SurfaceDescriptor* descriptor);
|
||||
~Surface();
|
||||
|
||||
void SetAttachedSwapChain(NewSwapChainBase* swapChain);
|
||||
NewSwapChainBase* GetAttachedSwapChain() const;
|
||||
|
||||
// These are valid to call on all Surfaces.
|
||||
enum class Type { MetalLayer, WindowsHWND, Xlib };
|
||||
Type GetType() const;
|
||||
@ -54,6 +59,9 @@ namespace dawn_native {
|
||||
Ref<InstanceBase> mInstance;
|
||||
Type mType;
|
||||
|
||||
// The swapchain will set this to null when it is destroyed.
|
||||
NewSwapChainBase* mSwapChain = nullptr;
|
||||
|
||||
// MetalLayer
|
||||
void* mMetalLayer = nullptr;
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "dawn_native/SwapChain.h"
|
||||
|
||||
#include "common/Constants.h"
|
||||
#include "dawn_native/Adapter.h"
|
||||
#include "dawn_native/Device.h"
|
||||
#include "dawn_native/Surface.h"
|
||||
#include "dawn_native/Texture.h"
|
||||
@ -96,6 +97,19 @@ namespace dawn_native {
|
||||
return {};
|
||||
}
|
||||
|
||||
TextureDescriptor GetSwapChainBaseTextureDescriptor(NewSwapChainBase* swapChain) {
|
||||
TextureDescriptor desc;
|
||||
desc.usage = swapChain->GetUsage();
|
||||
desc.dimension = wgpu::TextureDimension::e2D;
|
||||
desc.size = {swapChain->GetWidth(), swapChain->GetHeight(), 1};
|
||||
desc.arrayLayerCount = 1;
|
||||
desc.format = swapChain->GetFormat();
|
||||
desc.mipLevelCount = 1;
|
||||
desc.sampleCount = 1;
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
// SwapChainBase
|
||||
|
||||
SwapChainBase::SwapChainBase(DeviceBase* device) : ObjectBase(device) {
|
||||
@ -246,6 +260,7 @@ namespace dawn_native {
|
||||
Surface* surface,
|
||||
const SwapChainDescriptor* descriptor)
|
||||
: SwapChainBase(device),
|
||||
mAttached(true),
|
||||
mWidth(descriptor->width),
|
||||
mHeight(descriptor->height),
|
||||
mFormat(descriptor->format),
|
||||
@ -253,6 +268,25 @@ namespace dawn_native {
|
||||
mSurface(surface) {
|
||||
}
|
||||
|
||||
NewSwapChainBase::~NewSwapChainBase() {
|
||||
if (mCurrentTextureView.Get() != nullptr) {
|
||||
ASSERT(mCurrentTextureView->GetTexture()->GetTextureState() ==
|
||||
TextureBase::TextureState::Destroyed);
|
||||
}
|
||||
|
||||
ASSERT(!mAttached);
|
||||
ASSERT(mSurface == nullptr);
|
||||
}
|
||||
|
||||
void NewSwapChainBase::DetachFromSurface() {
|
||||
if (mAttached) {
|
||||
DetachFromSurfaceImpl();
|
||||
GetSurface()->SetAttachedSwapChain(nullptr);
|
||||
mSurface = nullptr;
|
||||
mAttached = false;
|
||||
}
|
||||
}
|
||||
|
||||
void NewSwapChainBase::Configure(wgpu::TextureFormat format,
|
||||
wgpu::TextureUsage allowedUsage,
|
||||
uint32_t width,
|
||||
@ -262,14 +296,48 @@ namespace dawn_native {
|
||||
}
|
||||
|
||||
TextureViewBase* NewSwapChainBase::GetCurrentTextureView() {
|
||||
GetDevice()->ConsumedError(DAWN_VALIDATION_ERROR(
|
||||
"GetCurrentTextureView not implemented yet for surface-based swapchains"));
|
||||
return TextureViewBase::MakeError(GetDevice());
|
||||
if (GetDevice()->ConsumedError(ValidateGetCurrentTextureView())) {
|
||||
return TextureViewBase::MakeError(GetDevice());
|
||||
}
|
||||
|
||||
if (mCurrentTextureView.Get() != nullptr) {
|
||||
// Calling GetCurrentTextureView always returns a new reference so add it even when
|
||||
// reusing the existing texture view.
|
||||
mCurrentTextureView->Reference();
|
||||
return mCurrentTextureView.Get();
|
||||
}
|
||||
|
||||
TextureViewBase* view = nullptr;
|
||||
if (GetDevice()->ConsumedError(GetCurrentTextureViewImpl(), &view)) {
|
||||
return TextureViewBase::MakeError(GetDevice());
|
||||
}
|
||||
|
||||
// Check that the return texture view matches exactly what was given for this descriptor.
|
||||
ASSERT(view->GetTexture()->GetFormat().format == mFormat);
|
||||
ASSERT((view->GetTexture()->GetUsage() & mUsage) == mUsage);
|
||||
ASSERT(view->GetLevelCount() == 1);
|
||||
ASSERT(view->GetLayerCount() == 1);
|
||||
ASSERT(view->GetDimension() == wgpu::TextureViewDimension::e2D);
|
||||
ASSERT(view->GetTexture()->GetMipLevelVirtualSize(view->GetBaseMipLevel()).width == mWidth);
|
||||
ASSERT(view->GetTexture()->GetMipLevelVirtualSize(view->GetBaseMipLevel()).height ==
|
||||
mHeight);
|
||||
|
||||
mCurrentTextureView = view;
|
||||
return view;
|
||||
}
|
||||
|
||||
void NewSwapChainBase::Present() {
|
||||
GetDevice()->ConsumedError(
|
||||
DAWN_VALIDATION_ERROR("Present not implemented yet for surface-based swapchains"));
|
||||
if (GetDevice()->ConsumedError(ValidatePresent())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetDevice()->ConsumedError(PresentImpl())) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(mCurrentTextureView->GetTexture()->GetTextureState() ==
|
||||
TextureBase::TextureState::Destroyed);
|
||||
mCurrentTextureView = nullptr;
|
||||
}
|
||||
|
||||
uint32_t NewSwapChainBase::GetWidth() const {
|
||||
@ -289,7 +357,41 @@ namespace dawn_native {
|
||||
}
|
||||
|
||||
Surface* NewSwapChainBase::GetSurface() {
|
||||
return mSurface.Get();
|
||||
return mSurface;
|
||||
}
|
||||
|
||||
bool NewSwapChainBase::IsAttached() const {
|
||||
return mAttached;
|
||||
}
|
||||
|
||||
wgpu::BackendType NewSwapChainBase::GetBackendType() const {
|
||||
return GetDevice()->GetAdapter()->GetBackendType();
|
||||
}
|
||||
|
||||
MaybeError NewSwapChainBase::ValidatePresent() const {
|
||||
DAWN_TRY(GetDevice()->ValidateIsAlive());
|
||||
DAWN_TRY(GetDevice()->ValidateObject(this));
|
||||
|
||||
if (!mAttached) {
|
||||
return DAWN_VALIDATION_ERROR("Presenting on detached swapchain");
|
||||
}
|
||||
|
||||
if (mCurrentTextureView.Get() == nullptr) {
|
||||
return DAWN_VALIDATION_ERROR("Presenting without prior GetCurrentTextureView");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
MaybeError NewSwapChainBase::ValidateGetCurrentTextureView() const {
|
||||
DAWN_TRY(GetDevice()->ValidateIsAlive());
|
||||
DAWN_TRY(GetDevice()->ValidateObject(this));
|
||||
|
||||
if (!mAttached) {
|
||||
return DAWN_VALIDATION_ERROR("Getting view on detached swapchain");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace dawn_native
|
||||
|
@ -28,6 +28,8 @@ namespace dawn_native {
|
||||
const Surface* surface,
|
||||
const SwapChainDescriptor* descriptor);
|
||||
|
||||
TextureDescriptor GetSwapChainBaseTextureDescriptor(NewSwapChainBase* swapChain);
|
||||
|
||||
class SwapChainBase : public ObjectBase {
|
||||
public:
|
||||
SwapChainBase(DeviceBase* device);
|
||||
@ -91,7 +93,24 @@ namespace dawn_native {
|
||||
NewSwapChainBase(DeviceBase* device,
|
||||
Surface* surface,
|
||||
const SwapChainDescriptor* descriptor);
|
||||
~NewSwapChainBase() override;
|
||||
|
||||
// This is called when the swapchain is detached for any reason:
|
||||
//
|
||||
// - The swapchain is being destroyed.
|
||||
// - The surface it is attached to is being destroyed.
|
||||
// - The swapchain is being replaced by another one on the surface.
|
||||
//
|
||||
// The call for the old swapchain being replaced should be called inside the backend
|
||||
// implementation of SwapChains. This is to allow them to acquire any resources before
|
||||
// calling detach to make a seamless transition from the previous swapchain.
|
||||
//
|
||||
// Likewise the call for the swapchain being destroyed must be done in the backend's
|
||||
// swapchain's destructor since C++ says it is UB to call virtual methods in the base class
|
||||
// destructor.
|
||||
void DetachFromSurface();
|
||||
|
||||
// Dawn API
|
||||
void Configure(wgpu::TextureFormat format,
|
||||
wgpu::TextureUsage allowedUsage,
|
||||
uint32_t width,
|
||||
@ -104,14 +123,36 @@ namespace dawn_native {
|
||||
wgpu::TextureFormat GetFormat() const;
|
||||
wgpu::TextureUsage GetUsage() const;
|
||||
Surface* GetSurface();
|
||||
bool IsAttached() const;
|
||||
wgpu::BackendType GetBackendType() const;
|
||||
|
||||
private:
|
||||
bool mAttached;
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
wgpu::TextureFormat mFormat;
|
||||
wgpu::TextureUsage mUsage;
|
||||
|
||||
Ref<Surface> mSurface;
|
||||
// This is a weak reference to the surface. If the surface is destroyed it will call
|
||||
// DetachFromSurface and mSurface will be updated to nullptr.
|
||||
Surface* mSurface = nullptr;
|
||||
Ref<TextureViewBase> mCurrentTextureView;
|
||||
|
||||
MaybeError ValidatePresent() const;
|
||||
MaybeError ValidateGetCurrentTextureView() const;
|
||||
|
||||
// GetCurrentTextureViewImpl and PresentImpl are guaranteed to be called in an interleaved
|
||||
// manner, starting with GetCurrentTextureViewImpl.
|
||||
|
||||
// The returned texture view must match the swapchain descriptor exactly.
|
||||
virtual ResultOrError<TextureViewBase*> GetCurrentTextureViewImpl() = 0;
|
||||
// The call to present must destroy the current view's texture so further access to it are
|
||||
// invalid.
|
||||
virtual MaybeError PresentImpl() = 0;
|
||||
|
||||
// Guaranteed to be called exactly once during the lifetime of the SwapChain. After it is
|
||||
// called no other virtual method can be called.
|
||||
virtual void DetachFromSurfaceImpl() = 0;
|
||||
};
|
||||
|
||||
} // namespace dawn_native
|
||||
|
@ -254,8 +254,9 @@ namespace dawn_native { namespace d3d12 {
|
||||
const SwapChainDescriptor* descriptor) {
|
||||
return new SwapChain(this, descriptor);
|
||||
}
|
||||
ResultOrError<SwapChainBase*> Device::CreateSwapChainImpl(
|
||||
ResultOrError<NewSwapChainBase*> Device::CreateSwapChainImpl(
|
||||
Surface* surface,
|
||||
NewSwapChainBase* previousSwapChain,
|
||||
const SwapChainDescriptor* descriptor) {
|
||||
return DAWN_VALIDATION_ERROR("New swapchains not implemented.");
|
||||
}
|
||||
|
@ -122,8 +122,9 @@ namespace dawn_native { namespace d3d12 {
|
||||
const ShaderModuleDescriptor* descriptor) override;
|
||||
ResultOrError<SwapChainBase*> CreateSwapChainImpl(
|
||||
const SwapChainDescriptor* descriptor) override;
|
||||
ResultOrError<SwapChainBase*> CreateSwapChainImpl(
|
||||
ResultOrError<NewSwapChainBase*> CreateSwapChainImpl(
|
||||
Surface* surface,
|
||||
NewSwapChainBase* previousSwapChain,
|
||||
const SwapChainDescriptor* descriptor) override;
|
||||
ResultOrError<TextureBase*> CreateTextureImpl(const TextureDescriptor* descriptor) override;
|
||||
ResultOrError<TextureViewBase*> CreateTextureViewImpl(
|
||||
|
@ -85,8 +85,9 @@ namespace dawn_native { namespace metal {
|
||||
const ShaderModuleDescriptor* descriptor) override;
|
||||
ResultOrError<SwapChainBase*> CreateSwapChainImpl(
|
||||
const SwapChainDescriptor* descriptor) override;
|
||||
ResultOrError<SwapChainBase*> CreateSwapChainImpl(
|
||||
ResultOrError<NewSwapChainBase*> CreateSwapChainImpl(
|
||||
Surface* surface,
|
||||
NewSwapChainBase* previousSwapChain,
|
||||
const SwapChainDescriptor* descriptor) override;
|
||||
ResultOrError<TextureBase*> CreateTextureImpl(const TextureDescriptor* descriptor) override;
|
||||
ResultOrError<TextureViewBase*> CreateTextureViewImpl(
|
||||
|
@ -116,8 +116,9 @@ namespace dawn_native { namespace metal {
|
||||
const SwapChainDescriptor* descriptor) {
|
||||
return new SwapChain(this, descriptor);
|
||||
}
|
||||
ResultOrError<SwapChainBase*> Device::CreateSwapChainImpl(
|
||||
ResultOrError<NewSwapChainBase*> Device::CreateSwapChainImpl(
|
||||
Surface* surface,
|
||||
NewSwapChainBase* previousSwapChain,
|
||||
const SwapChainDescriptor* descriptor) {
|
||||
return DAWN_VALIDATION_ERROR("New swapchains not implemented.");
|
||||
}
|
||||
|
@ -153,10 +153,11 @@ namespace dawn_native { namespace null {
|
||||
const SwapChainDescriptor* descriptor) {
|
||||
return new OldSwapChain(this, descriptor);
|
||||
}
|
||||
ResultOrError<SwapChainBase*> Device::CreateSwapChainImpl(
|
||||
ResultOrError<NewSwapChainBase*> Device::CreateSwapChainImpl(
|
||||
Surface* surface,
|
||||
NewSwapChainBase* previousSwapChain,
|
||||
const SwapChainDescriptor* descriptor) {
|
||||
return new SwapChain(this, surface, descriptor);
|
||||
return new SwapChain(this, surface, previousSwapChain, descriptor);
|
||||
}
|
||||
ResultOrError<TextureBase*> Device::CreateTextureImpl(const TextureDescriptor* descriptor) {
|
||||
return new Texture(this, descriptor, TextureBase::TextureState::OwnedInternal);
|
||||
@ -358,11 +359,42 @@ namespace dawn_native { namespace null {
|
||||
|
||||
// SwapChain
|
||||
|
||||
SwapChain::SwapChain(Device* device, Surface* surface, const SwapChainDescriptor* descriptor)
|
||||
SwapChain::SwapChain(Device* device,
|
||||
Surface* surface,
|
||||
NewSwapChainBase* previousSwapChain,
|
||||
const SwapChainDescriptor* descriptor)
|
||||
: NewSwapChainBase(device, surface, descriptor) {
|
||||
if (previousSwapChain != nullptr) {
|
||||
// TODO(cwallez@chromium.org): figure out what should happen when surfaces are used by
|
||||
// multiple backends one after the other. It probably needs to block until the backend
|
||||
// and GPU are completely finished with the previous swapchain.
|
||||
ASSERT(previousSwapChain->GetBackendType() == wgpu::BackendType::Null);
|
||||
previousSwapChain->DetachFromSurface();
|
||||
}
|
||||
}
|
||||
|
||||
SwapChain::~SwapChain() {
|
||||
DetachFromSurface();
|
||||
}
|
||||
|
||||
MaybeError SwapChain::PresentImpl() {
|
||||
mTexture->Destroy();
|
||||
mTexture = nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
ResultOrError<TextureViewBase*> SwapChain::GetCurrentTextureViewImpl() {
|
||||
TextureDescriptor textureDesc = GetSwapChainBaseTextureDescriptor(this);
|
||||
mTexture = AcquireRef(
|
||||
new Texture(GetDevice(), &textureDesc, TextureBase::TextureState::OwnedInternal));
|
||||
return mTexture->CreateView(nullptr);
|
||||
}
|
||||
|
||||
void SwapChain::DetachFromSurfaceImpl() {
|
||||
if (mTexture.Get() != nullptr) {
|
||||
mTexture->Destroy();
|
||||
mTexture = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// OldSwapChain
|
||||
|
@ -125,8 +125,9 @@ namespace dawn_native { namespace null {
|
||||
const ShaderModuleDescriptor* descriptor) override;
|
||||
ResultOrError<SwapChainBase*> CreateSwapChainImpl(
|
||||
const SwapChainDescriptor* descriptor) override;
|
||||
ResultOrError<SwapChainBase*> CreateSwapChainImpl(
|
||||
ResultOrError<NewSwapChainBase*> CreateSwapChainImpl(
|
||||
Surface* surface,
|
||||
NewSwapChainBase* previousSwapChain,
|
||||
const SwapChainDescriptor* descriptor) override;
|
||||
ResultOrError<TextureBase*> CreateTextureImpl(const TextureDescriptor* descriptor) override;
|
||||
ResultOrError<TextureViewBase*> CreateTextureViewImpl(
|
||||
@ -202,8 +203,18 @@ namespace dawn_native { namespace null {
|
||||
|
||||
class SwapChain : public NewSwapChainBase {
|
||||
public:
|
||||
SwapChain(Device* device, Surface* surface, const SwapChainDescriptor* descriptor);
|
||||
~SwapChain();
|
||||
SwapChain(Device* device,
|
||||
Surface* surface,
|
||||
NewSwapChainBase* previousSwapChain,
|
||||
const SwapChainDescriptor* descriptor);
|
||||
~SwapChain() override;
|
||||
|
||||
private:
|
||||
Ref<Texture> mTexture;
|
||||
|
||||
MaybeError PresentImpl() override;
|
||||
ResultOrError<TextureViewBase*> GetCurrentTextureViewImpl() override;
|
||||
void DetachFromSurfaceImpl() override;
|
||||
};
|
||||
|
||||
class OldSwapChain : public OldSwapChainBase {
|
||||
|
@ -96,8 +96,9 @@ namespace dawn_native { namespace opengl {
|
||||
const SwapChainDescriptor* descriptor) {
|
||||
return new SwapChain(this, descriptor);
|
||||
}
|
||||
ResultOrError<SwapChainBase*> Device::CreateSwapChainImpl(
|
||||
ResultOrError<NewSwapChainBase*> Device::CreateSwapChainImpl(
|
||||
Surface* surface,
|
||||
NewSwapChainBase* previousSwapChain,
|
||||
const SwapChainDescriptor* descriptor) {
|
||||
return DAWN_VALIDATION_ERROR("New swapchains not implemented.");
|
||||
}
|
||||
|
@ -80,8 +80,9 @@ namespace dawn_native { namespace opengl {
|
||||
const ShaderModuleDescriptor* descriptor) override;
|
||||
ResultOrError<SwapChainBase*> CreateSwapChainImpl(
|
||||
const SwapChainDescriptor* descriptor) override;
|
||||
ResultOrError<SwapChainBase*> CreateSwapChainImpl(
|
||||
ResultOrError<NewSwapChainBase*> CreateSwapChainImpl(
|
||||
Surface* surface,
|
||||
NewSwapChainBase* previousSwapChain,
|
||||
const SwapChainDescriptor* descriptor) override;
|
||||
ResultOrError<TextureBase*> CreateTextureImpl(const TextureDescriptor* descriptor) override;
|
||||
ResultOrError<TextureViewBase*> CreateTextureViewImpl(
|
||||
|
@ -161,8 +161,9 @@ namespace dawn_native { namespace vulkan {
|
||||
const SwapChainDescriptor* descriptor) {
|
||||
return SwapChain::Create(this, descriptor);
|
||||
}
|
||||
ResultOrError<SwapChainBase*> Device::CreateSwapChainImpl(
|
||||
ResultOrError<NewSwapChainBase*> Device::CreateSwapChainImpl(
|
||||
Surface* surface,
|
||||
NewSwapChainBase* previousSwapChain,
|
||||
const SwapChainDescriptor* descriptor) {
|
||||
return DAWN_VALIDATION_ERROR("New swapchains not implemented.");
|
||||
}
|
||||
|
@ -119,8 +119,9 @@ namespace dawn_native { namespace vulkan {
|
||||
const ShaderModuleDescriptor* descriptor) override;
|
||||
ResultOrError<SwapChainBase*> CreateSwapChainImpl(
|
||||
const SwapChainDescriptor* descriptor) override;
|
||||
ResultOrError<SwapChainBase*> CreateSwapChainImpl(
|
||||
ResultOrError<NewSwapChainBase*> CreateSwapChainImpl(
|
||||
Surface* surface,
|
||||
NewSwapChainBase* previousSwapChain,
|
||||
const SwapChainDescriptor* descriptor) override;
|
||||
ResultOrError<TextureBase*> CreateTextureImpl(const TextureDescriptor* descriptor) override;
|
||||
ResultOrError<TextureViewBase*> CreateTextureViewImpl(
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "common/Constants.h"
|
||||
#include "common/Log.h"
|
||||
#include "utils/ComboRenderPipelineDescriptor.h"
|
||||
#include "utils/GLFWUtils.h"
|
||||
#include "utils/WGPUHelpers.h"
|
||||
|
||||
@ -69,11 +70,26 @@ class SwapChainValidationTests : public ValidationTest {
|
||||
pass.EndPass();
|
||||
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||
}
|
||||
|
||||
// Checks that an OutputAttachment view is an error by trying to create a render pass on it.
|
||||
void CheckTextureViewIsDestroyed(wgpu::TextureView view) {
|
||||
utils::ComboRenderPassDescriptor renderPassDesc({view});
|
||||
|
||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
|
||||
pass.EndPass();
|
||||
wgpu::CommandBuffer commands = encoder.Finish();
|
||||
|
||||
wgpu::Queue queue = device.CreateQueue();
|
||||
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
|
||||
}
|
||||
};
|
||||
|
||||
// Control case for a successful swapchain creation.
|
||||
// Control case for a successful swapchain creation and presenting.
|
||||
TEST_F(SwapChainValidationTests, CreationSuccess) {
|
||||
device.CreateSwapChain(surface, &goodDescriptor);
|
||||
wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
wgpu::TextureView view = swapchain.GetCurrentTextureView();
|
||||
swapchain.Present();
|
||||
}
|
||||
|
||||
// Checks that the creation size must be a valid 2D texture size.
|
||||
@ -144,3 +160,133 @@ TEST_F(SwapChainValidationTests, OperationsOnErrorSwapChain) {
|
||||
|
||||
ASSERT_DEVICE_ERROR(swapchain.Present());
|
||||
}
|
||||
|
||||
// Check it is invalid to call present without getting a current view.
|
||||
TEST_F(SwapChainValidationTests, PresentWithoutCurrentView) {
|
||||
wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
|
||||
// Check it is invalid if we never called GetCurrentTextureView
|
||||
ASSERT_DEVICE_ERROR(swapchain.Present());
|
||||
|
||||
// Check it is invalid if we never called since the last present.
|
||||
swapchain.GetCurrentTextureView();
|
||||
swapchain.Present();
|
||||
ASSERT_DEVICE_ERROR(swapchain.Present());
|
||||
}
|
||||
|
||||
// Check that the current view is in the destroyed state after the swapchain is destroyed.
|
||||
TEST_F(SwapChainValidationTests, ViewDestroyedAfterSwapChainDestruction) {
|
||||
wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
wgpu::TextureView view = swapchain.GetCurrentTextureView();
|
||||
swapchain = nullptr;
|
||||
|
||||
CheckTextureViewIsDestroyed(view);
|
||||
}
|
||||
|
||||
// Check that the current view is the destroyed state after present.
|
||||
TEST_F(SwapChainValidationTests, ViewDestroyedAfterPresent) {
|
||||
wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
wgpu::TextureView view = swapchain.GetCurrentTextureView();
|
||||
swapchain.Present();
|
||||
|
||||
CheckTextureViewIsDestroyed(view);
|
||||
}
|
||||
|
||||
// Check that returned view is of the current format / usage / dimension / size / sample count
|
||||
TEST_F(SwapChainValidationTests, ReturnedViewCharacteristics) {
|
||||
utils::ComboRenderPipelineDescriptor pipelineDesc(device);
|
||||
pipelineDesc.vertexStage.module =
|
||||
utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(
|
||||
#version 450
|
||||
void main() {
|
||||
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
})");
|
||||
pipelineDesc.cFragmentStage.module =
|
||||
utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
|
||||
#version 450
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
})");
|
||||
// Validation will check that the sample count of the view matches this format.
|
||||
pipelineDesc.sampleCount = 1;
|
||||
pipelineDesc.colorStateCount = 2;
|
||||
// Validation will check that the format of the view matches this format.
|
||||
pipelineDesc.cColorStates[0].format = wgpu::TextureFormat::BGRA8Unorm;
|
||||
pipelineDesc.cColorStates[1].format = wgpu::TextureFormat::R8Unorm;
|
||||
device.CreateRenderPipeline(&pipelineDesc);
|
||||
|
||||
// Create a second texture to be used as render pass attachment. Validation will check that the
|
||||
// size of the view matches the size of this texture.
|
||||
wgpu::TextureDescriptor textureDesc;
|
||||
textureDesc.usage = wgpu::TextureUsage::OutputAttachment;
|
||||
textureDesc.dimension = wgpu::TextureDimension::e2D;
|
||||
textureDesc.size = {1, 1, 1};
|
||||
textureDesc.format = wgpu::TextureFormat::R8Unorm;
|
||||
textureDesc.sampleCount = 1;
|
||||
wgpu::Texture secondTexture = device.CreateTexture(&textureDesc);
|
||||
|
||||
// Get the swapchain view and try to use it in the render pass to trigger all the validation.
|
||||
wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
wgpu::TextureView view = swapchain.GetCurrentTextureView();
|
||||
|
||||
// Validation will also check the dimension of the view is 2D, and it's usage contains
|
||||
// OutputAttachment
|
||||
utils::ComboRenderPassDescriptor renderPassDesc({view, secondTexture.CreateView()});
|
||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
|
||||
pass.EndPass();
|
||||
wgpu::CommandBuffer commands = encoder.Finish();
|
||||
|
||||
wgpu::Queue queue = device.CreateQueue();
|
||||
queue.Submit(1, &commands);
|
||||
|
||||
// Check that view doesn't have extra formats like Sampled.
|
||||
// TODO(cwallez@chromium.org): also check for [Readonly]Storage once that's implemented.
|
||||
wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
|
||||
device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture}});
|
||||
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, bgl, {{0, view}}));
|
||||
}
|
||||
|
||||
// Check that failing to create a new swapchain doesn't replace the previous one.
|
||||
TEST_F(SwapChainValidationTests, ErrorSwapChainDoesntReplacePreviousOne) {
|
||||
wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &badDescriptor));
|
||||
|
||||
wgpu::TextureView view = swapchain.GetCurrentTextureView();
|
||||
swapchain.Present();
|
||||
}
|
||||
|
||||
// Check that after replacement, all swapchain operations are errors and the view is destroyed.
|
||||
TEST_F(SwapChainValidationTests, ReplacedSwapChainIsInvalid) {
|
||||
{
|
||||
wgpu::SwapChain replacedSwapChain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
device.CreateSwapChain(surface, &goodDescriptor);
|
||||
ASSERT_DEVICE_ERROR(replacedSwapChain.GetCurrentTextureView());
|
||||
}
|
||||
|
||||
{
|
||||
wgpu::SwapChain replacedSwapChain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
wgpu::TextureView view = replacedSwapChain.GetCurrentTextureView();
|
||||
device.CreateSwapChain(surface, &goodDescriptor);
|
||||
|
||||
CheckTextureViewIsDestroyed(view);
|
||||
ASSERT_DEVICE_ERROR(replacedSwapChain.Present());
|
||||
}
|
||||
}
|
||||
|
||||
// Check that after surface destruction, all swapchain operations are errors and the view is
|
||||
// destroyed. The test is split in two to reset the wgpu::Surface in the middle.
|
||||
TEST_F(SwapChainValidationTests, SwapChainIsInvalidAfterSurfaceDestruction_GetView) {
|
||||
wgpu::SwapChain replacedSwapChain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
surface = nullptr;
|
||||
ASSERT_DEVICE_ERROR(replacedSwapChain.GetCurrentTextureView());
|
||||
}
|
||||
TEST_F(SwapChainValidationTests, SwapChainIsInvalidAfterSurfaceDestruction_AfterGetView) {
|
||||
wgpu::SwapChain replacedSwapChain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
wgpu::TextureView view = replacedSwapChain.GetCurrentTextureView();
|
||||
surface = nullptr;
|
||||
|
||||
CheckTextureViewIsDestroyed(view);
|
||||
ASSERT_DEVICE_ERROR(replacedSwapChain.Present());
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user