diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp index 4de76548b7..bb8320e04e 100644 --- a/src/dawn_native/d3d12/DeviceD3D12.cpp +++ b/src/dawn_native/d3d12/DeviceD3D12.cpp @@ -339,13 +339,13 @@ namespace dawn_native { namespace d3d12 { } ResultOrError> Device::CreateSwapChainImpl( const SwapChainDescriptor* descriptor) { - return SwapChain::Create(this, descriptor); + return OldSwapChain::Create(this, descriptor); } ResultOrError> Device::CreateSwapChainImpl( Surface* surface, NewSwapChainBase* previousSwapChain, const SwapChainDescriptor* descriptor) { - return DAWN_VALIDATION_ERROR("New swapchains not implemented."); + return SwapChain::Create(this, surface, previousSwapChain, descriptor); } ResultOrError> Device::CreateTextureImpl(const TextureDescriptor* descriptor) { return Texture::Create(this, descriptor); diff --git a/src/dawn_native/d3d12/SwapChainD3D12.cpp b/src/dawn_native/d3d12/SwapChainD3D12.cpp index 8fd4554e7f..de9dc30d8d 100644 --- a/src/dawn_native/d3d12/SwapChainD3D12.cpp +++ b/src/dawn_native/d3d12/SwapChainD3D12.cpp @@ -14,19 +14,70 @@ #include "dawn_native/d3d12/SwapChainD3D12.h" +#include "dawn_native/Surface.h" +#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/TextureD3D12.h" #include namespace dawn_native { namespace d3d12 { + namespace { + + uint32_t PresentModeToBufferCount(wgpu::PresentMode mode) { + switch (mode) { + case wgpu::PresentMode::Immediate: + case wgpu::PresentMode::Fifo: + return 2; + case wgpu::PresentMode::Mailbox: + return 3; + } + } + + uint32_t PresentModeToSwapInterval(wgpu::PresentMode mode) { + switch (mode) { + case wgpu::PresentMode::Immediate: + case wgpu::PresentMode::Mailbox: + return 0; + case wgpu::PresentMode::Fifo: + return 1; + } + } + + UINT PresentModeToSwapChainFlags(wgpu::PresentMode mode) { + UINT flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + + if (mode == wgpu::PresentMode::Immediate) { + flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + } + + return flags; + } + + DXGI_USAGE ToDXGIUsage(wgpu::TextureUsage usage) { + DXGI_USAGE dxgiUsage = DXGI_CPU_ACCESS_NONE; + if (usage & wgpu::TextureUsage::Sampled) { + dxgiUsage |= DXGI_USAGE_SHADER_INPUT; + } + if (usage & wgpu::TextureUsage::Storage) { + dxgiUsage |= DXGI_USAGE_UNORDERED_ACCESS; + } + if (usage & wgpu::TextureUsage::RenderAttachment) { + dxgiUsage |= DXGI_USAGE_RENDER_TARGET_OUTPUT; + } + return dxgiUsage; + } + + } // namespace + + // OldSwapChain // static - Ref SwapChain::Create(Device* device, const SwapChainDescriptor* descriptor) { - return AcquireRef(new SwapChain(device, descriptor)); + Ref OldSwapChain::Create(Device* device, const SwapChainDescriptor* descriptor) { + return AcquireRef(new OldSwapChain(device, descriptor)); } - SwapChain::SwapChain(Device* device, const SwapChainDescriptor* descriptor) + OldSwapChain::OldSwapChain(Device* device, const SwapChainDescriptor* descriptor) : OldSwapChainBase(device, descriptor) { const auto& im = GetImplementation(); DawnWSIContextD3D12 wsiContext = {}; @@ -37,10 +88,9 @@ namespace dawn_native { namespace d3d12 { mTextureUsage = static_cast(im.textureUsage); } - SwapChain::~SwapChain() { - } + OldSwapChain::~OldSwapChain() = default; - TextureBase* SwapChain::GetNextTextureImpl(const TextureDescriptor* descriptor) { + TextureBase* OldSwapChain::GetNextTextureImpl(const TextureDescriptor* descriptor) { DeviceBase* device = GetDevice(); const auto& im = GetImplementation(); DawnSwapChainNextTexture next = {}; @@ -61,7 +111,7 @@ namespace dawn_native { namespace d3d12 { return dawnTexture.Detach(); } - MaybeError SwapChain::OnBeforePresent(TextureViewBase* view) { + MaybeError OldSwapChain::OnBeforePresent(TextureViewBase* view) { Device* device = ToBackend(GetDevice()); CommandRecordingContext* commandContext; @@ -77,4 +127,218 @@ namespace dawn_native { namespace d3d12 { return {}; } + // SwapChain + + // static + ResultOrError> SwapChain::Create(Device* device, + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor) { + Ref swapchain = AcquireRef(new SwapChain(device, surface, descriptor)); + DAWN_TRY(swapchain->Initialize(previousSwapChain)); + return swapchain; + } + + SwapChain::~SwapChain() { + DetachFromSurface(); + } + + // Initializes the swapchain on the surface. Note that `previousSwapChain` may or may not be + // nullptr. If it is not nullptr it means that it is the swapchain previously in use on the + // surface and that we have a chance to reuse it's underlying IDXGISwapChain and "buffers". + MaybeError SwapChain::Initialize(NewSwapChainBase* previousSwapChain) { + ASSERT(GetSurface()->GetType() == Surface::Type::WindowsHWND); + + // Precompute the configuration parameters we want for the DXGI swapchain. + mConfig.bufferCount = PresentModeToBufferCount(GetPresentMode()); + mConfig.format = D3D12TextureFormat(GetFormat()); + mConfig.swapChainFlags = PresentModeToSwapChainFlags(GetPresentMode()); + mConfig.usage = ToDXGIUsage(GetUsage()); + + // There is no previous swapchain so we can create one directly and don't have anything else + // to do. + if (previousSwapChain == nullptr) { + return InitializeSwapChainFromScratch(); + } + + // TODO(cwallez@chromium.org): figure out what should happen when surfaces are used by + // multiple backends one after the other. It probably needs to block until the backend + // and GPU are completely finished with the previous swapchain. + if (previousSwapChain->GetBackendType() != wgpu::BackendType::D3D12) { + return DAWN_VALIDATION_ERROR("d3d12::SwapChain cannot switch between APIs"); + } + + // TODO(cwallez@chromium.org): use ToBackend once OldSwapChainBase is removed. + SwapChain* previousD3D12SwapChain = static_cast(previousSwapChain); + + // TODO(cwallez@chromium.org): Figure out switching an HWND between devices, it might + // require just losing the reference to the swapchain, but might also need to wait for + // all previous operations to complete. + if (GetDevice() != previousSwapChain->GetDevice()) { + return DAWN_VALIDATION_ERROR("d3d12::SwapChain cannot switch between devices"); + } + + // The previous swapchain is on the same device so we want to reuse it but it is still not + // always possible. Because DXGI requires that a new swapchain be created if the + // DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING flag is changed. + bool canReuseSwapChain = + ((mConfig.swapChainFlags ^ previousD3D12SwapChain->mConfig.swapChainFlags) & + DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) == 0; + + // We can't reuse the previous swapchain, so we destroy it and wait for all of its reference + // to be forgotten (otherwise DXGI complains that there are outstanding references). + if (!canReuseSwapChain) { + DAWN_TRY(previousD3D12SwapChain->DetachAndWaitForDeallocation()); + return InitializeSwapChainFromScratch(); + } + + // After all this we know we can reuse the swapchain, see if it is possible to also reuse + // the buffers. + mDXGISwapChain = std::move(previousD3D12SwapChain->mDXGISwapChain); + + bool canReuseBuffers = GetWidth() == previousSwapChain->GetWidth() && + GetHeight() == previousSwapChain->GetHeight() && + GetFormat() == previousSwapChain->GetFormat() && + GetPresentMode() == previousSwapChain->GetPresentMode(); + if (canReuseBuffers) { + mBuffers = std::move(previousD3D12SwapChain->mBuffers); + mBufferLastUsedSerials = std::move(previousD3D12SwapChain->mBufferLastUsedSerials); + mCurrentBuffer = previousD3D12SwapChain->mCurrentBuffer; + return {}; + } + + // We can't reuse the buffers so we need to resize, IDXGSwapChain->ResizeBuffers requires + // that all references to buffers are lost before it is called. Contrary to D3D11, the + // application is responsible for keeping references to the buffers until the GPU is done + // using them so we have no choice but to synchrounously wait for all operations to complete + // on the previous swapchain and then lose references to its buffers. + DAWN_TRY(previousD3D12SwapChain->DetachAndWaitForDeallocation()); + DAWN_TRY( + CheckHRESULT(mDXGISwapChain->ResizeBuffers(mConfig.bufferCount, GetWidth(), GetHeight(), + mConfig.format, mConfig.swapChainFlags), + "IDXGISwapChain::ResizeBuffer")); + return CollectSwapChainBuffers(); + } + + MaybeError SwapChain::InitializeSwapChainFromScratch() { + ASSERT(mDXGISwapChain == nullptr); + + Device* device = ToBackend(GetDevice()); + + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; + swapChainDesc.Width = GetWidth(); + swapChainDesc.Height = GetHeight(); + swapChainDesc.Format = mConfig.format; + swapChainDesc.Stereo = false; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.BufferUsage = mConfig.usage; + swapChainDesc.BufferCount = mConfig.bufferCount; + swapChainDesc.Scaling = DXGI_SCALING_STRETCH; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; + swapChainDesc.Flags = mConfig.swapChainFlags; + + ComPtr factory2 = nullptr; + DAWN_TRY(CheckHRESULT(device->GetFactory()->QueryInterface(IID_PPV_ARGS(&factory2)), + "Getting IDXGIFactory2")); + + ComPtr swapChain1; + DAWN_TRY(CheckHRESULT( + factory2->CreateSwapChainForHwnd(device->GetCommandQueue().Get(), + static_cast(GetSurface()->GetHWND()), + &swapChainDesc, nullptr, nullptr, &swapChain1), + "Creating the IDXGISwapChain1")); + DAWN_TRY(CheckHRESULT(swapChain1.As(&mDXGISwapChain), "Gettting IDXGISwapChain1")); + + return CollectSwapChainBuffers(); + } + + MaybeError SwapChain::CollectSwapChainBuffers() { + ASSERT(mDXGISwapChain != nullptr); + ASSERT(mBuffers.empty()); + + mBuffers.resize(mConfig.bufferCount); + for (uint32_t i = 0; i < mConfig.bufferCount; i++) { + DAWN_TRY(CheckHRESULT(mDXGISwapChain->GetBuffer(i, IID_PPV_ARGS(&mBuffers[i])), + "Getting IDXGISwapChain buffer")); + } + + // Pretend all the buffers were last used at the beginning of time. + mBufferLastUsedSerials.resize(mConfig.bufferCount, ExecutionSerial(0)); + return {}; + } + + MaybeError SwapChain::PresentImpl() { + Device* device = ToBackend(GetDevice()); + + // Transition the texture to the present state as required by IDXGISwapChain1::Present() + // TODO(cwallez@chromium.org): Remove the need for this by eagerly transitioning the + // presentable texture to present at the end of submits that use them. + CommandRecordingContext* commandContext; + DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext()); + mApiTexture->TrackUsageAndTransitionNow(commandContext, kPresentTextureUsage, + mApiTexture->GetAllSubresources()); + DAWN_TRY(device->ExecutePendingCommandContext()); + + // Do the actual present. DXGI_STATUS_OCCLUDED is a valid return value that's just a + // message to the application that it could stop rendering. + HRESULT presentResult = + mDXGISwapChain->Present(PresentModeToSwapInterval(GetPresentMode()), 0); + if (presentResult != DXGI_STATUS_OCCLUDED) { + DAWN_TRY(CheckHRESULT(presentResult, "IDXGISwapChain::Present")); + } + + // Record that "new" is the last time the buffer has been used. + DAWN_TRY(device->NextSerial()); + mBufferLastUsedSerials[mCurrentBuffer] = device->GetPendingCommandSerial(); + + mApiTexture->APIDestroy(); + mApiTexture = nullptr; + + return {}; + } + + ResultOrError SwapChain::GetCurrentTextureViewImpl() { + Device* device = ToBackend(GetDevice()); + + // Synchronously wait until previous operations on the next swapchain buffer are finished. + // This is the logic that performs frame pacing. + // TODO(cwallez@chromium.org): Consider whether this should be lifted for Mailbox so that + // there is not frame pacing. + mCurrentBuffer = mDXGISwapChain->GetCurrentBackBufferIndex(); + DAWN_TRY(device->WaitForSerial(mBufferLastUsedSerials[mCurrentBuffer])); + + // Create the API side objects for this use of the swapchain's buffer. + TextureDescriptor descriptor = GetSwapChainBaseTextureDescriptor(this); + DAWN_TRY_ASSIGN(mApiTexture, Texture::Create(ToBackend(GetDevice()), &descriptor, + mBuffers[mCurrentBuffer])); + + // TODO(dawn:723): change to not use AcquireRef for reentrant object creation. + return mApiTexture->APICreateView(); + } + + 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. + Device* device = ToBackend(GetDevice()); + DAWN_TRY(device->NextSerial()); + DAWN_TRY(device->WaitForSerial(device->GetLastSubmittedCommandSerial())); + return device->TickImpl(); + } + + void SwapChain::DetachFromSurfaceImpl() { + if (mApiTexture != nullptr) { + mApiTexture->APIDestroy(); + mApiTexture = nullptr; + } + + mDXGISwapChain = nullptr; + mBuffers.clear(); + } + }} // namespace dawn_native::d3d12 diff --git a/src/dawn_native/d3d12/SwapChainD3D12.h b/src/dawn_native/d3d12/SwapChainD3D12.h index 4083b04a14..bc476d1193 100644 --- a/src/dawn_native/d3d12/SwapChainD3D12.h +++ b/src/dawn_native/d3d12/SwapChainD3D12.h @@ -17,23 +17,71 @@ #include "dawn_native/SwapChain.h" +#include "dawn_native/IntegerTypes.h" +#include "dawn_native/d3d12/d3d12_platform.h" + namespace dawn_native { namespace d3d12 { class Device; + class Texture; - class SwapChain final : public OldSwapChainBase { + class OldSwapChain final : public OldSwapChainBase { public: - static Ref Create(Device* device, const SwapChainDescriptor* descriptor); + static Ref Create(Device* device, const SwapChainDescriptor* descriptor); protected: - SwapChain(Device* device, const SwapChainDescriptor* descriptor); - ~SwapChain() override; + OldSwapChain(Device* device, const SwapChainDescriptor* descriptor); + ~OldSwapChain() override; TextureBase* GetNextTextureImpl(const TextureDescriptor* descriptor) override; MaybeError OnBeforePresent(TextureViewBase* view) override; wgpu::TextureUsage mTextureUsage; }; + class SwapChain final : public NewSwapChainBase { + public: + static ResultOrError> Create(Device* device, + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor); + + private: + ~SwapChain() override; + + using NewSwapChainBase::NewSwapChainBase; + MaybeError Initialize(NewSwapChainBase* previousSwapChain); + + struct Config { + // Information that's passed to the D3D12 swapchain creation call. + UINT bufferCount; + UINT swapChainFlags; + DXGI_FORMAT format; + DXGI_USAGE usage; + }; + + // NewSwapChainBase implementation + MaybeError PresentImpl() override; + ResultOrError GetCurrentTextureViewImpl() override; + void DetachFromSurfaceImpl() override; + + // Does the swapchain initialization steps assuming there is nothing we can reuse. + MaybeError InitializeSwapChainFromScratch(); + // Does the swapchain initialization step of gathering the buffers. + MaybeError CollectSwapChainBuffers(); + // Calls DetachFromSurface but also synchronously waits until all references to the + // swapchain and buffers are removed, as that's a constraint for some DXGI operations. + MaybeError DetachAndWaitForDeallocation(); + + Config mConfig; + + ComPtr mDXGISwapChain; + std::vector> mBuffers; + std::vector mBufferLastUsedSerials; + uint32_t mCurrentBuffer = 0; + + Ref mApiTexture; + }; + }} // namespace dawn_native::d3d12 #endif // DAWNNATIVE_D3D12_SWAPCHAIN_D3D12_H_ diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp index fdcd43e484..6274801c20 100644 --- a/src/dawn_native/d3d12/TextureD3D12.cpp +++ b/src/dawn_native/d3d12/TextureD3D12.cpp @@ -530,9 +530,6 @@ namespace dawn_native { namespace d3d12 { // texture is owned externally. The texture's owning entity must remain responsible for // memory management. mResourceAllocation = { info, 0, std::move(d3d12Texture), nullptr }; - - SetIsSubresourceContentInitialized(true, GetAllSubresources()); - return {}; }