blob: 7726d501dc6b1269f5e3061bf82d3e73497401a9 [file] [log] [blame]
// 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.
#include "dawn/native/SwapChain.h"
#include "dawn/common/Constants.h"
#include "dawn/native/Device.h"
#include "dawn/native/ObjectType_autogen.h"
#include "dawn/native/PhysicalDevice.h"
#include "dawn/native/Surface.h"
#include "dawn/native/Texture.h"
#include "dawn/native/ValidationUtils_autogen.h"
namespace dawn::native {
namespace {
class ErrorSwapChain final : public SwapChainBase {
public:
explicit ErrorSwapChain(DeviceBase* device, const SwapChainDescriptor* desc)
: SwapChainBase(device, desc, ObjectBase::kError) {}
private:
ResultOrError<Ref<TextureBase>> GetCurrentTextureImpl() override { UNREACHABLE(); }
MaybeError PresentImpl() override { UNREACHABLE(); }
void DetachFromSurfaceImpl() override { UNREACHABLE(); }
};
} // anonymous namespace
MaybeError ValidateSwapChainDescriptor(const DeviceBase* device,
const Surface* surface,
const SwapChainDescriptor* descriptor) {
DAWN_INVALID_IF(surface->IsError(), "[Surface] is invalid.");
DAWN_TRY(ValidatePresentMode(descriptor->presentMode));
// TODO(crbug.com/dawn/160): Lift this restriction once wgpu::Instance::GetPreferredSurfaceFormat is
// implemented.
// TODO(dawn:286):
#if DAWN_PLATFORM_IS(ANDROID)
constexpr wgpu::TextureFormat kRequireSwapChainFormat = wgpu::TextureFormat::RGBA8Unorm;
#else
constexpr wgpu::TextureFormat kRequireSwapChainFormat = wgpu::TextureFormat::BGRA8Unorm;
#endif // !DAWN_PLATFORM_IS(ANDROID)
DAWN_INVALID_IF(descriptor->format != kRequireSwapChainFormat,
"Format (%s) is not %s, which is (currently) the only accepted format.",
descriptor->format, kRequireSwapChainFormat);
if (device->HasFeature(Feature::SurfaceCapabilities)) {
wgpu::TextureUsage validUsage;
DAWN_TRY_ASSIGN(validUsage, device->GetSupportedSurfaceUsage(surface));
DAWN_INVALID_IF(
(descriptor->usage | validUsage) != validUsage,
"Usage (%s) is not supported, %s are (currently) the only accepted usage flags.",
descriptor->usage, validUsage);
} else {
DAWN_INVALID_IF(descriptor->usage != wgpu::TextureUsage::RenderAttachment,
"Usage (%s) is not %s, which is (currently) the only accepted usage. Other "
"usage flags require enabling %s",
descriptor->usage, wgpu::TextureUsage::RenderAttachment,
wgpu::FeatureName::SurfaceCapabilities);
}
DAWN_INVALID_IF(descriptor->width == 0 || descriptor->height == 0,
"Swap Chain size (width: %u, height: %u) is empty.", descriptor->width,
descriptor->height);
DAWN_INVALID_IF(
descriptor->width > device->GetLimits().v1.maxTextureDimension2D ||
descriptor->height > device->GetLimits().v1.maxTextureDimension2D,
"Swap Chain size (width: %u, height: %u) is greater than the maximum 2D texture "
"size (width: %u, height: %u).",
descriptor->width, descriptor->height, device->GetLimits().v1.maxTextureDimension2D,
device->GetLimits().v1.maxTextureDimension2D);
return {};
}
TextureDescriptor GetSwapChainBaseTextureDescriptor(SwapChainBase* 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(DeviceBase* device,
Surface* surface,
const SwapChainDescriptor* descriptor)
: ApiObjectBase(device, kLabelNotImplemented),
mWidth(descriptor->width),
mHeight(descriptor->height),
mFormat(descriptor->format),
mUsage(descriptor->usage),
mPresentMode(descriptor->presentMode),
mSurface(surface) {
GetObjectTrackingList()->Track(this);
}
SwapChainBase::~SwapChainBase() {
if (mCurrentTexture != nullptr) {
ASSERT(mCurrentTexture->GetTextureState() == TextureBase::TextureState::Destroyed);
}
ASSERT(!mAttached);
}
SwapChainBase::SwapChainBase(DeviceBase* device,
const SwapChainDescriptor* descriptor,
ObjectBase::ErrorTag tag)
: ApiObjectBase(device, tag),
mWidth(descriptor->width),
mHeight(descriptor->height),
mFormat(descriptor->format),
mUsage(descriptor->usage),
mPresentMode(descriptor->presentMode) {}
// static
SwapChainBase* SwapChainBase::MakeError(DeviceBase* device, const SwapChainDescriptor* desc) {
return new ErrorSwapChain(device, desc);
}
void SwapChainBase::DestroyImpl() {}
ObjectType SwapChainBase::GetType() const {
return ObjectType::SwapChain;
}
void SwapChainBase::DetachFromSurface() {
if (mAttached) {
DetachFromSurfaceImpl();
mSurface = nullptr;
mAttached = false;
}
}
void SwapChainBase::SetIsAttached() {
mAttached = true;
}
void SwapChainBase::APIConfigure(wgpu::TextureFormat format,
wgpu::TextureUsage allowedUsage,
uint32_t width,
uint32_t height) {
GetDevice()->HandleError(
DAWN_VALIDATION_ERROR("Configure is invalid for surface-based swapchains."));
}
TextureBase* SwapChainBase::APIGetCurrentTexture() {
Ref<TextureBase> result;
if (GetDevice()->ConsumedError(GetCurrentTexture(), &result, "calling %s.GetCurrentTexture()",
this)) {
TextureDescriptor desc = GetSwapChainBaseTextureDescriptor(this);
TextureBase* errorTexture = TextureBase::MakeError(GetDevice(), &desc);
SetChildLabel(errorTexture);
return errorTexture;
}
return result.Detach();
}
TextureViewBase* SwapChainBase::APIGetCurrentTextureView() {
Ref<TextureViewBase> result;
if (GetDevice()->ConsumedError(GetCurrentTextureView(), &result,
"calling %s.GetCurrentTextureView()", this)) {
TextureViewBase* errorView = TextureViewBase::MakeError(GetDevice());
SetChildLabel(errorView);
return errorView;
}
return result.Detach();
}
ResultOrError<Ref<TextureBase>> SwapChainBase::GetCurrentTexture() {
DAWN_TRY(ValidateGetCurrentTexture());
if (mCurrentTexture != nullptr) {
// Calling GetCurrentTexture always returns a new reference.
return mCurrentTexture;
}
DAWN_TRY_ASSIGN(mCurrentTexture, GetCurrentTextureImpl());
SetChildLabel(mCurrentTexture.Get());
// Check that the return texture matches exactly what was given for this descriptor.
ASSERT(mCurrentTexture->GetFormat().format == mFormat);
ASSERT(IsSubset(mUsage, mCurrentTexture->GetUsage()));
ASSERT(mCurrentTexture->GetDimension() == wgpu::TextureDimension::e2D);
ASSERT(mCurrentTexture->GetWidth() == mWidth);
ASSERT(mCurrentTexture->GetHeight() == mHeight);
ASSERT(mCurrentTexture->GetNumMipLevels() == 1);
ASSERT(mCurrentTexture->GetArrayLayers() == 1);
return mCurrentTexture;
}
ResultOrError<Ref<TextureViewBase>> SwapChainBase::GetCurrentTextureView() {
Ref<TextureBase> currentTexture;
DAWN_TRY_ASSIGN(currentTexture, GetCurrentTexture());
return currentTexture->CreateView();
}
void SwapChainBase::APIPresent() {
if (GetDevice()->ConsumedError(ValidatePresent())) {
return;
}
if (GetDevice()->ConsumedError(PresentImpl())) {
return;
}
ASSERT(mCurrentTexture->GetTextureState() == TextureBase::TextureState::Destroyed);
mCurrentTexture = nullptr;
}
uint32_t SwapChainBase::GetWidth() const {
return mWidth;
}
uint32_t SwapChainBase::GetHeight() const {
return mHeight;
}
wgpu::TextureFormat SwapChainBase::GetFormat() const {
return mFormat;
}
wgpu::TextureUsage SwapChainBase::GetUsage() const {
return mUsage;
}
wgpu::PresentMode SwapChainBase::GetPresentMode() const {
return mPresentMode;
}
Surface* SwapChainBase::GetSurface() const {
return mSurface;
}
bool SwapChainBase::IsAttached() const {
return mAttached;
}
wgpu::BackendType SwapChainBase::GetBackendType() const {
return GetDevice()->GetPhysicalDevice()->GetBackendType();
}
MaybeError SwapChainBase::ValidatePresent() const {
DAWN_TRY(GetDevice()->ValidateIsAlive());
DAWN_TRY(GetDevice()->ValidateObject(this));
DAWN_INVALID_IF(!mAttached, "Cannot call Present called on detached %s.", this);
DAWN_INVALID_IF(mCurrentTexture == nullptr,
"GetCurrentTexture was not called on %s this frame prior to calling Present.",
this);
return {};
}
MaybeError SwapChainBase::ValidateGetCurrentTexture() const {
DAWN_TRY(GetDevice()->ValidateIsAlive());
DAWN_TRY(GetDevice()->ValidateObject(this));
DAWN_INVALID_IF(!mAttached, "Cannot call GetCurrentTexture on detached %s.", this);
return {};
}
void SwapChainBase::SetChildLabel(ApiObjectBase* child) const {
child->SetLabel(absl::StrFormat("of %s", this));
}
} // namespace dawn::native