blob: 3386618dae5c1ad6c0cd1e54fd0a2a9c371ad7b6 [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/d3d12/SwapChainD3D12.h"
#if defined(DAWN_USE_WINDOWS_UI)
#include <windows.ui.xaml.media.dxinterop.h>
#endif // defined(DAWN_USE_WINDOWS_UI)
#include <utility>
#include "dawn/native/ChainUtils.h"
#include "dawn/native/Surface.h"
#include "dawn/native/d3d/D3DError.h"
#include "dawn/native/d3d/UtilsD3D.h"
#include "dawn/native/d3d12/DeviceD3D12.h"
#include "dawn/native/d3d12/QueueD3D12.h"
#include "dawn/native/d3d12/TextureD3D12.h"
namespace dawn::native::d3d12 {
// static
ResultOrError<Ref<SwapChain>> SwapChain::Create(Device* device,
Surface* surface,
SwapChainBase* previousSwapChain,
const SurfaceConfiguration* config) {
Ref<SwapChain> swapchain = AcquireRef(new SwapChain(device, surface, config));
DAWN_TRY(swapchain->Initialize(previousSwapChain));
return swapchain;
}
SwapChain::~SwapChain() = default;
IUnknown* SwapChain::GetD3DDeviceForCreatingSwapChain() {
return ToBackend(GetDevice()->GetQueue())->GetCommandQueue();
}
void SwapChain::ReuseBuffers(SwapChainBase* previousSwapChain) {
SwapChain* previousD3DSwapChain = ToBackend(previousSwapChain);
mBuffers = std::move(previousD3DSwapChain->mBuffers);
// Remember the current state of the ID3D12Resource for the current buffer if we didn't have
// chance to present it yet.
if (previousD3DSwapChain->mApiTexture != nullptr) {
D3D12_RESOURCE_STATES state =
previousD3DSwapChain->mApiTexture->GetCurrentStateForSwapChain();
mBuffers[previousD3DSwapChain->mCurrentBuffer].acquireState = state;
}
}
MaybeError SwapChain::CollectSwapChainBuffers() {
DAWN_ASSERT(GetDXGISwapChain() != nullptr);
DAWN_ASSERT(mBuffers.empty());
IDXGISwapChain3* dxgiSwapChain = GetDXGISwapChain();
const auto& config = GetConfig();
mBuffers.resize(config.bufferCount);
for (uint32_t i = 0; i < config.bufferCount; i++) {
DAWN_TRY(CheckHRESULT(dxgiSwapChain->GetBuffer(i, IID_PPV_ARGS(&mBuffers[i].resource)),
"Getting IDXGISwapChain buffer"));
}
return {};
}
MaybeError SwapChain::PresentImpl() {
Queue* queue = ToBackend(GetDevice()->GetQueue());
// Transition the texture to the present state as required by IDXGISwapChain1::Present()
// TODO(crbug.com/dawn/269): Remove the need for this by eagerly transitioning the
// presentable texture to present at the end of submits that use them.
CommandRecordingContext* commandContext = queue->GetPendingCommandContext();
mApiTexture->TrackUsageAndTransitionNow(commandContext, kPresentReleaseTextureUsage,
mApiTexture->GetAllSubresources());
DAWN_TRY(queue->SubmitPendingCommands());
DAWN_TRY(PresentDXGISwapChain());
// Record that "new" is the last time the buffer has been used.
DAWN_TRY(queue->NextSerial());
mBuffers[mCurrentBuffer].lastUsed = queue->GetLastSubmittedCommandSerial();
mBuffers[mCurrentBuffer].acquireState = D3D12_RESOURCE_STATE_COMMON;
mApiTexture->APIDestroy();
mApiTexture = nullptr;
return {};
}
ResultOrError<SwapChainTextureInfo> SwapChain::GetCurrentTextureImpl() {
Queue* queue = ToBackend(GetDevice()->GetQueue());
// Synchronously wait until previous operations on the next swapchain buffer are finished.
// This is the logic that performs frame pacing.
// TODO(crbug.com/dawn/269): Consider whether this should be lifted for Mailbox so that
// there is not frame pacing.
mCurrentBuffer = GetDXGISwapChain()->GetCurrentBackBufferIndex();
const Buffer& buffer = mBuffers[mCurrentBuffer];
DAWN_TRY(queue->WaitForSerial(buffer.lastUsed));
// Create the API side objects for this use of the swapchain's buffer.
TextureDescriptor descriptor = GetSwapChainBaseTextureDescriptor(this);
DAWN_TRY_ASSIGN(mApiTexture,
Texture::CreateForSwapChain(ToBackend(GetDevice()), Unpack(&descriptor),
buffer.resource, buffer.acquireState));
SwapChainTextureInfo info;
info.texture = mApiTexture;
// TODO(dawn:2320): Check for optimality
info.status = wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal;
return info;
}
MaybeError SwapChain::DetachAndWaitForDeallocation() {
DetachFromSurface();
// DetachFromSurface calls Texture->Destroy that enqueues the D3D12 resource in a
// SerialQueue with the current "pending serial" so that we don't destroy the texture
// before it is finished being used. Flush the commands and wait for that serial to be
// passed, then Tick the device to make sure the reference to the D3D12 texture is removed.
Queue* queue = ToBackend(GetDevice()->GetQueue());
DAWN_TRY(queue->EnsureCommandsFlushed(queue->GetPendingCommandSerial()));
DAWN_TRY(queue->WaitForSerial(queue->GetLastSubmittedCommandSerial()));
return ToBackend(GetDevice())->TickImpl();
}
void SwapChain::DetachFromSurfaceImpl() {
if (mApiTexture != nullptr) {
mApiTexture->APIDestroy();
mApiTexture = nullptr;
}
mBuffers.clear();
ReleaseDXGISwapChain();
}
} // namespace dawn::native::d3d12