blob: 32c7a0c363805d6d3b7263aef4e400b702bfdd67 [file] [log] [blame]
// Copyright 2017 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "dawn/native/SwapChain.h"
#include <utility>
#include <vector>
#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 SurfaceConfiguration* config)
: SwapChainBase(device, config, ObjectBase::kError) {}
private:
ResultOrError<SwapChainTextureInfo> GetCurrentTextureImpl() override { DAWN_UNREACHABLE(); }
MaybeError PresentImpl() override { DAWN_UNREACHABLE(); }
void DetachFromSurfaceImpl() override { DAWN_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)) {
PhysicalDeviceSurfaceCapabilities caps;
DAWN_TRY_ASSIGN(caps, device->GetPhysicalDevice()->GetSurfaceCapabilities(
device->GetInstance(), surface));
wgpu::TextureUsage validUsage = caps.usages;
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.viewFormatCount = swapChain->GetViewFormats().size();
desc.viewFormats = swapChain->GetViewFormats().data();
desc.mipLevelCount = 1;
desc.sampleCount = 1;
return desc;
}
SwapChainBase::SwapChainBase(DeviceBase* device,
Surface* surface,
const SurfaceConfiguration* config)
: ApiObjectBase(device, kLabelNotImplemented),
mWidth(config->width),
mHeight(config->height),
mFormat(config->format),
mUsage(config->usage),
mPresentMode(config->presentMode),
mAlphaMode(config->alphaMode),
mSurface(surface) {
GetObjectTrackingList()->Track(this);
for (uint32_t i = 0; i < config->viewFormatCount; ++i) {
if (config->viewFormats[i] == config->format) {
// Skip our own format, like texture creations does.
continue;
}
mViewFormats.push_back(config->viewFormats[i]);
}
}
FormatSet SwapChainBase::ComputeViewFormatSet() const {
FormatSet viewFormatSet;
for (wgpu::TextureFormat format : mViewFormats) {
viewFormatSet[GetDevice()->GetValidInternalFormat(format)] = true;
}
return viewFormatSet;
}
SwapChainBase::~SwapChainBase() {
if (mCurrentTextureInfo.texture != nullptr) {
DAWN_ASSERT(mCurrentTextureInfo.texture->IsDestroyed());
}
DAWN_ASSERT(!mAttached);
}
SwapChainBase::SwapChainBase(DeviceBase* device,
const SurfaceConfiguration* config,
ObjectBase::ErrorTag tag)
: ApiObjectBase(device, tag),
mWidth(config->width),
mHeight(config->height),
mFormat(config->format),
mUsage(config->usage),
mPresentMode(config->presentMode),
mAlphaMode(config->alphaMode) {}
// static
Ref<SwapChainBase> SwapChainBase::MakeError(DeviceBase* device,
const SurfaceConfiguration* config) {
return AcquireRef(new ErrorSwapChain(device, config));
}
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() {
SurfaceTexture result;
if (GetDevice()->ConsumedError(GetCurrentTexture(), &result, "calling %s.GetCurrentTexture()",
this)) {
TextureDescriptor desc = GetSwapChainBaseTextureDescriptor(this);
Ref<TextureBase> errorTexture = TextureBase::MakeError(GetDevice(), &desc);
SetChildLabel(errorTexture.Get());
result.texture = ReturnToAPI(std::move(errorTexture));
}
return result.texture;
}
TextureViewBase* SwapChainBase::APIGetCurrentTextureView() {
Ref<TextureViewBase> result;
if (GetDevice()->ConsumedError(GetCurrentTextureView(), &result,
"calling %s.GetCurrentTextureView()", this)) {
result = TextureViewBase::MakeError(GetDevice());
SetChildLabel(result.Get());
}
return ReturnToAPI(std::move(result));
}
ResultOrError<SurfaceTexture> SwapChainBase::GetCurrentTexture() {
DAWN_TRY(ValidateGetCurrentTexture());
SurfaceTexture surfaceTexture;
if (mCurrentTextureInfo.texture == nullptr) {
DAWN_TRY_ASSIGN(mCurrentTextureInfo, GetCurrentTextureImpl());
SetChildLabel(mCurrentTextureInfo.texture.Get());
// Check that the return texture matches exactly what was given for this descriptor.
DAWN_ASSERT(mCurrentTextureInfo.texture->GetFormat().format == mFormat);
DAWN_ASSERT(IsSubset(mUsage, mCurrentTextureInfo.texture->GetUsage()));
DAWN_ASSERT(mCurrentTextureInfo.texture->GetDimension() == wgpu::TextureDimension::e2D);
DAWN_ASSERT(mCurrentTextureInfo.texture->GetWidth(Aspect::Color) == mWidth);
DAWN_ASSERT(mCurrentTextureInfo.texture->GetHeight(Aspect::Color) == mHeight);
DAWN_ASSERT(mCurrentTextureInfo.texture->GetNumMipLevels() == 1);
DAWN_ASSERT(mCurrentTextureInfo.texture->GetArrayLayers() == 1);
DAWN_ASSERT(mCurrentTextureInfo.texture->GetViewFormats() == ComputeViewFormatSet());
}
// Calling GetCurrentTexture always returns a new reference.
surfaceTexture.texture = Ref<TextureBase>(mCurrentTextureInfo.texture).Detach();
surfaceTexture.suboptimal = mCurrentTextureInfo.suboptimal;
surfaceTexture.status = mCurrentTextureInfo.status;
return surfaceTexture;
}
ResultOrError<Ref<TextureViewBase>> SwapChainBase::GetCurrentTextureView() {
SurfaceTexture surfaceTexture;
DAWN_TRY_ASSIGN(surfaceTexture, GetCurrentTexture());
return surfaceTexture.texture->CreateView();
}
void SwapChainBase::APIPresent() {
if (GetDevice()->ConsumedError(ValidatePresent())) {
return;
}
if (GetDevice()->ConsumedError(PresentImpl())) {
return;
}
DAWN_ASSERT(mCurrentTextureInfo.texture->IsDestroyed());
mCurrentTextureInfo.texture = nullptr;
}
uint32_t SwapChainBase::GetWidth() const {
return mWidth;
}
uint32_t SwapChainBase::GetHeight() const {
return mHeight;
}
wgpu::TextureFormat SwapChainBase::GetFormat() const {
return mFormat;
}
const std::vector<wgpu::TextureFormat>& SwapChainBase::GetViewFormats() const {
return mViewFormats;
}
wgpu::TextureUsage SwapChainBase::GetUsage() const {
return mUsage;
}
wgpu::PresentMode SwapChainBase::GetPresentMode() const {
return mPresentMode;
}
wgpu::CompositeAlphaMode SwapChainBase::GetAlphaMode() const {
return mAlphaMode;
}
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(mCurrentTextureInfo.texture == 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