dawn-cmake/src/dawn_native/SwapChain.cpp

407 lines
14 KiB
C++
Raw Normal View History

// Copyright 2017 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2018-07-24 11:53:51 +00:00
#include "dawn_native/SwapChain.h"
#include "common/Constants.h"
#include "dawn_native/Adapter.h"
2018-07-24 11:53:51 +00:00
#include "dawn_native/Device.h"
#include "dawn_native/Surface.h"
2018-07-24 11:53:51 +00:00
#include "dawn_native/Texture.h"
#include "dawn_native/ValidationUtils_autogen.h"
namespace dawn_native {
namespace {
class ErrorSwapChain final : public SwapChainBase {
public:
ErrorSwapChain(DeviceBase* device) : SwapChainBase(device, ObjectBase::kError) {
}
private:
void APIConfigure(wgpu::TextureFormat format,
wgpu::TextureUsage allowedUsage,
uint32_t width,
uint32_t height) override {
GetDevice()->ConsumedError(DAWN_VALIDATION_ERROR("error swapchain"));
}
TextureViewBase* APIGetCurrentTextureView() override {
GetDevice()->ConsumedError(DAWN_VALIDATION_ERROR("error swapchain"));
return TextureViewBase::MakeError(GetDevice());
}
void APIPresent() override {
GetDevice()->ConsumedError(DAWN_VALIDATION_ERROR("error swapchain"));
}
};
} // anonymous namespace
MaybeError ValidateSwapChainDescriptor(const DeviceBase* device,
const Surface* surface,
const SwapChainDescriptor* descriptor) {
if (descriptor->implementation != 0) {
if (surface != nullptr) {
return DAWN_VALIDATION_ERROR(
"Exactly one of surface or implementation must be set");
}
DawnSwapChainImplementation* impl =
reinterpret_cast<DawnSwapChainImplementation*>(descriptor->implementation);
if (!impl->Init || !impl->Destroy || !impl->Configure || !impl->GetNextTexture ||
!impl->Present) {
return DAWN_VALIDATION_ERROR("Implementation is incomplete");
}
} else {
if (surface == nullptr) {
return DAWN_VALIDATION_ERROR(
"At least one of surface or implementation must be set");
}
DAWN_TRY(ValidatePresentMode(descriptor->presentMode));
// TODO(cwallez@chromium.org): Lift this restriction once
// wgpu::Instance::GetPreferredSurfaceFormat is implemented.
if (descriptor->format != wgpu::TextureFormat::BGRA8Unorm) {
return DAWN_VALIDATION_ERROR("Format must (currently) be BGRA8Unorm");
}
if (descriptor->usage != wgpu::TextureUsage::RenderAttachment) {
return DAWN_VALIDATION_ERROR("Usage must (currently) be RenderAttachment");
}
if (descriptor->width == 0 || descriptor->height == 0) {
return DAWN_VALIDATION_ERROR("Swapchain size can't be empty");
}
if (descriptor->width > kMaxTextureDimension2D ||
descriptor->height > kMaxTextureDimension2D) {
return DAWN_VALIDATION_ERROR("Swapchain size too big");
}
}
return {};
}
TextureDescriptor GetSwapChainBaseTextureDescriptor(NewSwapChainBase* swapChain) {
TextureDescriptor desc;
desc.usage = swapChain->GetUsage();
desc.dimension = wgpu::TextureDimension::e2D;
desc.size = {swapChain->GetWidth(), swapChain->GetHeight(), 1};
desc.format = swapChain->GetFormat();
desc.mipLevelCount = 1;
desc.sampleCount = 1;
return desc;
}
// SwapChainBase
SwapChainBase::SwapChainBase(DeviceBase* device) : ObjectBase(device) {
}
SwapChainBase::SwapChainBase(DeviceBase* device, ObjectBase::ErrorTag tag)
: ObjectBase(device, tag) {
}
SwapChainBase::~SwapChainBase() {
}
// static
SwapChainBase* SwapChainBase::MakeError(DeviceBase* device) {
return new ErrorSwapChain(device);
}
// OldSwapChainBase
OldSwapChainBase::OldSwapChainBase(DeviceBase* device, const SwapChainDescriptor* descriptor)
: SwapChainBase(device),
mImplementation(
*reinterpret_cast<DawnSwapChainImplementation*>(descriptor->implementation)) {
}
OldSwapChainBase::~OldSwapChainBase() {
if (!IsError()) {
const auto& im = GetImplementation();
im.Destroy(im.userData);
}
}
void OldSwapChainBase::APIConfigure(wgpu::TextureFormat format,
wgpu::TextureUsage allowedUsage,
uint32_t width,
uint32_t height) {
if (GetDevice()->ConsumedError(ValidateConfigure(format, allowedUsage, width, height))) {
return;
}
ASSERT(!IsError());
allowedUsage |= wgpu::TextureUsage::Present;
2017-11-23 18:32:51 +00:00
mFormat = format;
mAllowedUsage = allowedUsage;
mWidth = width;
mHeight = height;
mImplementation.Configure(mImplementation.userData, static_cast<WGPUTextureFormat>(format),
static_cast<WGPUTextureUsage>(allowedUsage), width, height);
}
TextureViewBase* OldSwapChainBase::APIGetCurrentTextureView() {
if (GetDevice()->ConsumedError(ValidateGetCurrentTextureView())) {
return TextureViewBase::MakeError(GetDevice());
}
ASSERT(!IsError());
// Return the same current texture view until Present is called.
if (mCurrentTextureView != nullptr) {
// Calling GetCurrentTextureView always returns a new reference so add it even when
// reuse the existing texture view.
mCurrentTextureView->Reference();
return mCurrentTextureView.Get();
}
// Create the backing texture and the view.
TextureDescriptor descriptor;
descriptor.dimension = wgpu::TextureDimension::e2D;
descriptor.size.width = mWidth;
descriptor.size.height = mHeight;
descriptor.size.depthOrArrayLayers = 1;
descriptor.sampleCount = 1;
descriptor.format = mFormat;
descriptor.mipLevelCount = 1;
descriptor.usage = mAllowedUsage;
// Get the texture but remove the external refcount because it is never passed outside
// of dawn_native
mCurrentTexture = AcquireRef(GetNextTextureImpl(&descriptor));
mCurrentTextureView = mCurrentTexture->APICreateView();
return mCurrentTextureView.Get();
}
void OldSwapChainBase::APIPresent() {
if (GetDevice()->ConsumedError(ValidatePresent())) {
return;
}
ASSERT(!IsError());
if (GetDevice()->ConsumedError(OnBeforePresent(mCurrentTextureView.Get()))) {
return;
}
2017-11-23 18:32:51 +00:00
mImplementation.Present(mImplementation.userData);
mCurrentTexture = nullptr;
mCurrentTextureView = nullptr;
}
const DawnSwapChainImplementation& OldSwapChainBase::GetImplementation() {
ASSERT(!IsError());
2017-11-23 18:32:51 +00:00
return mImplementation;
}
MaybeError OldSwapChainBase::ValidateConfigure(wgpu::TextureFormat format,
wgpu::TextureUsage allowedUsage,
uint32_t width,
uint32_t height) const {
DAWN_TRY(GetDevice()->ValidateIsAlive());
DAWN_TRY(GetDevice()->ValidateObject(this));
DAWN_TRY(ValidateTextureUsage(allowedUsage));
DAWN_TRY(ValidateTextureFormat(format));
if (width == 0 || height == 0) {
return DAWN_VALIDATION_ERROR("Swap chain cannot be configured to zero size");
}
return {};
}
MaybeError OldSwapChainBase::ValidateGetCurrentTextureView() const {
DAWN_TRY(GetDevice()->ValidateIsAlive());
DAWN_TRY(GetDevice()->ValidateObject(this));
if (mWidth == 0) {
// If width is 0, it implies swap chain has never been configured
return DAWN_VALIDATION_ERROR("Swap chain needs to be configured before GetNextTexture");
}
return {};
}
MaybeError OldSwapChainBase::ValidatePresent() const {
DAWN_TRY(GetDevice()->ValidateIsAlive());
DAWN_TRY(GetDevice()->ValidateObject(this));
if (mCurrentTextureView == nullptr) {
return DAWN_VALIDATION_ERROR(
"Cannot call present without a GetCurrentTextureView call for this frame");
}
return {};
}
// Implementation of NewSwapChainBase
NewSwapChainBase::NewSwapChainBase(DeviceBase* device,
Surface* surface,
const SwapChainDescriptor* descriptor)
: SwapChainBase(device),
Make Surface reference its attached SwapChain This solves an issues where when switching swapchains the previous one was destroyed before the new one was created, doing so detached itself from the Surface, which in turn made the new swapchain not do a graceful transition via vkSwapchainCreateInfoKHR::oldSwapchain. Keeping the reference on the surface makes sure we always have knowledge of the previous swapchain when replacing it. It requires re-working the lifetime model of NewSwapChainBase to not require a call to DetachFromSurface in the destructor, and having the Device explicitly tell a swapchain it got attached on creation (otherwise there are ASSERTs firing when swapchain creation fails). In addition, backends are changed to use a SwapChain::Create method and fail with a validation error (for now) when the previous swapchain didn't use the same API. vulkan::SwapChain is updated to use the previous swapchain's device's fenced deleter to destroy it which is important in the device switching tests. The SwapChainValidationTests are updated because with the lifetime changes the texture view can be kept alive after the application has lost the last reference to the wgpu::SwapChain. TBRing since it was reviewed in a different CL (but for the wrong branch). TBR=enga@chromium.org TBR=senorblanco@chromium.org Bug: dawn:269 Change-Id: Ie4374b5685af990d68969ab9cd7767e53c287ace Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/31041 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org>
2020-10-27 11:31:26 +00:00
mAttached(false),
mWidth(descriptor->width),
mHeight(descriptor->height),
mFormat(descriptor->format),
mUsage(descriptor->usage),
mPresentMode(descriptor->presentMode),
mSurface(surface) {
}
NewSwapChainBase::~NewSwapChainBase() {
if (mCurrentTextureView != nullptr) {
ASSERT(mCurrentTextureView->GetTexture()->GetTextureState() ==
TextureBase::TextureState::Destroyed);
}
ASSERT(!mAttached);
}
void NewSwapChainBase::DetachFromSurface() {
if (mAttached) {
DetachFromSurfaceImpl();
mSurface = nullptr;
mAttached = false;
}
}
Make Surface reference its attached SwapChain This solves an issues where when switching swapchains the previous one was destroyed before the new one was created, doing so detached itself from the Surface, which in turn made the new swapchain not do a graceful transition via vkSwapchainCreateInfoKHR::oldSwapchain. Keeping the reference on the surface makes sure we always have knowledge of the previous swapchain when replacing it. It requires re-working the lifetime model of NewSwapChainBase to not require a call to DetachFromSurface in the destructor, and having the Device explicitly tell a swapchain it got attached on creation (otherwise there are ASSERTs firing when swapchain creation fails). In addition, backends are changed to use a SwapChain::Create method and fail with a validation error (for now) when the previous swapchain didn't use the same API. vulkan::SwapChain is updated to use the previous swapchain's device's fenced deleter to destroy it which is important in the device switching tests. The SwapChainValidationTests are updated because with the lifetime changes the texture view can be kept alive after the application has lost the last reference to the wgpu::SwapChain. TBRing since it was reviewed in a different CL (but for the wrong branch). TBR=enga@chromium.org TBR=senorblanco@chromium.org Bug: dawn:269 Change-Id: Ie4374b5685af990d68969ab9cd7767e53c287ace Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/31041 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org>
2020-10-27 11:31:26 +00:00
void NewSwapChainBase::SetIsAttached() {
mAttached = true;
}
void NewSwapChainBase::APIConfigure(wgpu::TextureFormat format,
wgpu::TextureUsage allowedUsage,
uint32_t width,
uint32_t height) {
GetDevice()->ConsumedError(
DAWN_VALIDATION_ERROR("Configure is invalid for surface-based swapchains"));
}
TextureViewBase* NewSwapChainBase::APIGetCurrentTextureView() {
if (GetDevice()->ConsumedError(ValidateGetCurrentTextureView())) {
return TextureViewBase::MakeError(GetDevice());
}
if (mCurrentTextureView != 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(IsSubset(mUsage, view->GetTexture()->GetUsage()));
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::APIPresent() {
if (GetDevice()->ConsumedError(ValidatePresent())) {
return;
}
if (GetDevice()->ConsumedError(PresentImpl())) {
return;
}
ASSERT(mCurrentTextureView->GetTexture()->GetTextureState() ==
TextureBase::TextureState::Destroyed);
mCurrentTextureView = nullptr;
}
uint32_t NewSwapChainBase::GetWidth() const {
return mWidth;
}
uint32_t NewSwapChainBase::GetHeight() const {
return mHeight;
}
wgpu::TextureFormat NewSwapChainBase::GetFormat() const {
return mFormat;
}
wgpu::TextureUsage NewSwapChainBase::GetUsage() const {
return mUsage;
}
wgpu::PresentMode NewSwapChainBase::GetPresentMode() const {
return mPresentMode;
}
Surface* NewSwapChainBase::GetSurface() const {
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 == 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