blob: 1053aafefe6258dda65ff6ffc0c43d6187f8dd12 [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 "common/Constants.h"
#include "dawn_native/Adapter.h"
#include "dawn_native/Device.h"
#include "dawn_native/Surface.h"
#include "dawn_native/Texture.h"
#include "dawn_native/ValidationUtils_autogen.h"
namespace dawn_native {
namespace {
class ErrorSwapChain : public SwapChainBase {
public:
ErrorSwapChain(DeviceBase* device) : SwapChainBase(device, ObjectBase::kError) {
}
private:
void Configure(wgpu::TextureFormat format,
wgpu::TextureUsage allowedUsage,
uint32_t width,
uint32_t height) override {
GetDevice()->ConsumedError(DAWN_VALIDATION_ERROR("error swapchain"));
}
TextureViewBase* GetCurrentTextureView() override {
GetDevice()->ConsumedError(DAWN_VALIDATION_ERROR("error swapchain"));
return TextureViewBase::MakeError(GetDevice());
}
void Present() 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::OutputAttachment) {
return DAWN_VALIDATION_ERROR("Usage must (currently) be OutputAttachment");
}
if (descriptor->width == 0 || descriptor->height == 0) {
return DAWN_VALIDATION_ERROR("Swapchain size can't be empty");
}
if (descriptor->width > kMaxTextureSize || descriptor->height > kMaxTextureSize) {
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.arrayLayerCount = 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::Configure(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;
mFormat = format;
mAllowedUsage = allowedUsage;
mWidth = width;
mHeight = height;
mImplementation.Configure(mImplementation.userData, static_cast<WGPUTextureFormat>(format),
static_cast<WGPUTextureUsage>(allowedUsage), width, height);
}
TextureViewBase* OldSwapChainBase::GetCurrentTextureView() {
if (GetDevice()->ConsumedError(ValidateGetCurrentTextureView())) {
return TextureViewBase::MakeError(GetDevice());
}
ASSERT(!IsError());
// Return the same current texture view until Present is called.
if (mCurrentTextureView.Get() != 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.depth = 1;
descriptor.arrayLayerCount = 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->CreateView(nullptr);
return mCurrentTextureView.Get();
}
void OldSwapChainBase::Present() {
if (GetDevice()->ConsumedError(ValidatePresent())) {
return;
}
ASSERT(!IsError());
if (GetDevice()->ConsumedError(OnBeforePresent(mCurrentTexture.Get()))) {
return;
}
mImplementation.Present(mImplementation.userData);
mCurrentTexture = nullptr;
mCurrentTextureView = nullptr;
}
const DawnSwapChainImplementation& OldSwapChainBase::GetImplementation() {
ASSERT(!IsError());
return mImplementation;
}
MaybeError OldSwapChainBase::ValidateConfigure(wgpu::TextureFormat format,
wgpu::TextureUsage allowedUsage,
uint32_t width,
uint32_t height) const {
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()->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()->ValidateObject(this));
if (mCurrentTextureView.Get() == 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),
mAttached(true),
mWidth(descriptor->width),
mHeight(descriptor->height),
mFormat(descriptor->format),
mUsage(descriptor->usage),
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,
uint32_t height) {
GetDevice()->ConsumedError(
DAWN_VALIDATION_ERROR("Configure is invalid for surface-based swapchains"));
}
TextureViewBase* NewSwapChainBase::GetCurrentTextureView() {
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() {
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;
}
Surface* NewSwapChainBase::GetSurface() {
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