| // 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 |