d3d11: implement SwapChain
This CL also abstracts common code from d3d12::SwapChain to d3d::SwapChain base class for sharing. Bug: dawn:1705 Change-Id: I7b6ac8770b3c517fe552061de3c5589174ba5814 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/126665 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Peng Huang <penghuang@chromium.org>
This commit is contained in:
parent
4dade91986
commit
b08d87c06e
|
@ -414,6 +414,8 @@ source_set("sources") {
|
|||
"d3d/PlatformFunctions.h",
|
||||
"d3d/ShaderUtils.cpp",
|
||||
"d3d/ShaderUtils.h",
|
||||
"d3d/SwapChainD3D.cpp",
|
||||
"d3d/SwapChainD3D.h",
|
||||
"d3d/UtilsD3D.cpp",
|
||||
"d3d/UtilsD3D.h",
|
||||
"d3d/d3d_platform.h",
|
||||
|
@ -453,6 +455,8 @@ source_set("sources") {
|
|||
"d3d11/SamplerD3D11.h",
|
||||
"d3d11/ShaderModuleD3D11.cpp",
|
||||
"d3d11/ShaderModuleD3D11.h",
|
||||
"d3d11/SwapChainD3D11.cpp",
|
||||
"d3d11/SwapChainD3D11.h",
|
||||
"d3d11/TextureD3D11.cpp",
|
||||
"d3d11/TextureD3D11.h",
|
||||
"d3d11/UtilsD3D11.cpp",
|
||||
|
|
|
@ -271,6 +271,8 @@ if (DAWN_ENABLE_D3D11 OR DAWN_ENABLE_D3D12)
|
|||
"d3d/PlatformFunctions.h"
|
||||
"d3d/ShaderUtils.cpp"
|
||||
"d3d/ShaderUtils.h"
|
||||
"d3d/SwapChainD3D.cpp"
|
||||
"d3d/SwapChainD3D.h"
|
||||
"d3d/UtilsD3D.cpp"
|
||||
"d3d/UtilsD3D.h"
|
||||
"d3d/d3d_platform.h"
|
||||
|
@ -310,6 +312,8 @@ if (DAWN_ENABLE_D3D11)
|
|||
"d3d11/SamplerD3D11.h"
|
||||
"d3d11/ShaderModuleD3D11.cpp"
|
||||
"d3d11/ShaderModuleD3D11.h"
|
||||
"d3d11/SwapChainD3D11.cpp"
|
||||
"d3d11/SwapChainD3D11.h"
|
||||
"d3d11/TextureD3D11.cpp"
|
||||
"d3d11/TextureD3D11.h"
|
||||
"d3d11/UtilsD3D11.cpp"
|
||||
|
|
|
@ -21,10 +21,12 @@ namespace dawn::native::d3d {
|
|||
|
||||
class Adapter;
|
||||
class Device;
|
||||
class SwapChain;
|
||||
|
||||
struct D3DBackendTraits {
|
||||
using AdapterType = Adapter;
|
||||
using DeviceType = Device;
|
||||
using SwapChainType = SwapChain;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
|
|
@ -0,0 +1,246 @@
|
|||
// Copyright 2023 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/d3d/SwapChainD3D.h"
|
||||
|
||||
#include <windows.ui.xaml.media.dxinterop.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "dawn/native/Surface.h"
|
||||
#include "dawn/native/d3d/D3DError.h"
|
||||
#include "dawn/native/d3d/DeviceD3D.h"
|
||||
#include "dawn/native/d3d/Forward.h"
|
||||
#include "dawn/native/d3d/UtilsD3D.h"
|
||||
|
||||
namespace dawn::native::d3d {
|
||||
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::TextureBinding) {
|
||||
dxgiUsage |= DXGI_USAGE_SHADER_INPUT;
|
||||
}
|
||||
if (usage & wgpu::TextureUsage::StorageBinding) {
|
||||
dxgiUsage |= DXGI_USAGE_UNORDERED_ACCESS;
|
||||
}
|
||||
if (usage & wgpu::TextureUsage::RenderAttachment) {
|
||||
dxgiUsage |= DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
}
|
||||
return dxgiUsage;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SwapChain::~SwapChain() = default;
|
||||
|
||||
void SwapChain::DestroyImpl() {
|
||||
SwapChainBase::DestroyImpl();
|
||||
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(SwapChainBase* previousSwapChain) {
|
||||
ASSERT(GetSurface()->GetType() == Surface::Type::WindowsHWND);
|
||||
|
||||
// Precompute the configuration parameters we want for the DXGI swapchain.
|
||||
mConfig.bufferCount = PresentModeToBufferCount(GetPresentMode());
|
||||
mConfig.format = d3d::DXGITextureFormat(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(crbug.com/dawn/269): 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.
|
||||
DAWN_INVALID_IF(previousSwapChain->GetBackendType() != GetBackendType(),
|
||||
"D3D SwapChain cannot switch backend types from %s to %s.",
|
||||
previousSwapChain->GetBackendType(), GetBackendType());
|
||||
|
||||
SwapChain* previousD3DSwapChain = ToBackend(previousSwapChain);
|
||||
|
||||
// TODO(crbug.com/dawn/269): 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.
|
||||
DAWN_INVALID_IF(GetDevice() != previousSwapChain->GetDevice(),
|
||||
"D3D SwapChain cannot switch between D3D 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 ^ previousD3DSwapChain->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(previousD3DSwapChain->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(previousD3DSwapChain->mDXGISwapChain);
|
||||
|
||||
bool canReuseBuffers = GetWidth() == previousSwapChain->GetWidth() &&
|
||||
GetHeight() == previousSwapChain->GetHeight() &&
|
||||
GetFormat() == previousSwapChain->GetFormat() &&
|
||||
GetPresentMode() == previousSwapChain->GetPresentMode();
|
||||
if (canReuseBuffers) {
|
||||
this->ReuseBuffers(previousSwapChain);
|
||||
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.
|
||||
if (GetBackendType() == wgpu::BackendType::D3D12) {
|
||||
DAWN_TRY(previousD3DSwapChain->DetachAndWaitForDeallocation());
|
||||
} else {
|
||||
previousD3DSwapChain->DetachFromSurface();
|
||||
}
|
||||
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<IDXGIFactory2> factory2 = nullptr;
|
||||
DAWN_TRY(CheckHRESULT(device->GetFactory()->QueryInterface(IID_PPV_ARGS(&factory2)),
|
||||
"Getting IDXGIFactory2"));
|
||||
|
||||
ComPtr<IDXGISwapChain1> swapChain1;
|
||||
switch (GetSurface()->GetType()) {
|
||||
case Surface::Type::WindowsHWND: {
|
||||
DAWN_TRY(CheckHRESULT(
|
||||
factory2->CreateSwapChainForHwnd(GetD3DDeviceForCreatingSwapChain(),
|
||||
static_cast<HWND>(GetSurface()->GetHWND()),
|
||||
&swapChainDesc, nullptr, nullptr, &swapChain1),
|
||||
"Creating the IDXGISwapChain1"));
|
||||
break;
|
||||
}
|
||||
case Surface::Type::WindowsCoreWindow: {
|
||||
DAWN_TRY(CheckHRESULT(
|
||||
factory2->CreateSwapChainForCoreWindow(GetD3DDeviceForCreatingSwapChain(),
|
||||
GetSurface()->GetCoreWindow(),
|
||||
&swapChainDesc, nullptr, &swapChain1),
|
||||
"Creating the IDXGISwapChain1"));
|
||||
break;
|
||||
}
|
||||
case Surface::Type::WindowsSwapChainPanel: {
|
||||
DAWN_TRY(CheckHRESULT(
|
||||
factory2->CreateSwapChainForComposition(GetD3DDeviceForCreatingSwapChain(),
|
||||
&swapChainDesc, nullptr, &swapChain1),
|
||||
"Creating the IDXGISwapChain1"));
|
||||
ComPtr<ISwapChainPanelNative> swapChainPanelNative;
|
||||
DAWN_TRY(CheckHRESULT(GetSurface()->GetSwapChainPanel()->QueryInterface(
|
||||
IID_PPV_ARGS(&swapChainPanelNative)),
|
||||
"Getting ISwapChainPanelNative"));
|
||||
DAWN_TRY(CheckHRESULT(swapChainPanelNative->SetSwapChain(swapChain1.Get()),
|
||||
"Setting SwapChain"));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
DAWN_TRY(CheckHRESULT(swapChain1.As(&mDXGISwapChain), "Gettting IDXGISwapChain1"));
|
||||
|
||||
return CollectSwapChainBuffers();
|
||||
}
|
||||
|
||||
MaybeError SwapChain::PresentDXGISwapChain() {
|
||||
// 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"));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void SwapChain::ReleaseDXGISwapChain() {
|
||||
mDXGISwapChain = nullptr;
|
||||
}
|
||||
|
||||
IDXGISwapChain3* SwapChain::GetDXGISwapChain() const {
|
||||
return mDXGISwapChain.Get();
|
||||
}
|
||||
|
||||
const SwapChain::Config& SwapChain::GetConfig() const {
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
} // namespace dawn::native::d3d
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2023 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.
|
||||
|
||||
#ifndef SRC_DAWN_NATIVE_D3D_SWAPCHAIND3D_H_
|
||||
#define SRC_DAWN_NATIVE_D3D_SWAPCHAIND3D_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "dawn/native/SwapChain.h"
|
||||
|
||||
#include "dawn/native/IntegerTypes.h"
|
||||
#include "dawn/native/d3d/d3d_platform.h"
|
||||
|
||||
namespace dawn::native::d3d {
|
||||
|
||||
class Device;
|
||||
class Texture;
|
||||
|
||||
// SwapChain abstracts the swapchain creation for D3D12 and D3D11.
|
||||
// D3D11 and D3D12 have different ways to present and get the backbuffer.
|
||||
// D3D11 doesn't need to wait for the GPU to finish before presenting, but D3D12 does.
|
||||
// D3D11 manages buffers and we can only read and write the the buffer with index 0, but for D3D12
|
||||
// we need to manage all buffers by ourselves.
|
||||
class SwapChain : public SwapChainBase {
|
||||
protected:
|
||||
using SwapChainBase::SwapChainBase;
|
||||
~SwapChain() override;
|
||||
|
||||
void DestroyImpl() override;
|
||||
|
||||
MaybeError Initialize(SwapChainBase* previousSwapChain);
|
||||
|
||||
virtual IUnknown* GetD3DDeviceForCreatingSwapChain() = 0;
|
||||
virtual void ReuseBuffers(SwapChainBase* previousSwapChain) = 0;
|
||||
// Does the swapchain initialization step of gathering the buffers.
|
||||
virtual MaybeError CollectSwapChainBuffers() = 0;
|
||||
// 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.
|
||||
virtual MaybeError DetachAndWaitForDeallocation() = 0;
|
||||
|
||||
MaybeError PresentDXGISwapChain();
|
||||
void ReleaseDXGISwapChain();
|
||||
|
||||
IDXGISwapChain3* GetDXGISwapChain() const;
|
||||
|
||||
struct Config {
|
||||
// Information that's passed to the D3D12 swapchain creation call.
|
||||
UINT bufferCount;
|
||||
UINT swapChainFlags;
|
||||
DXGI_FORMAT format;
|
||||
DXGI_USAGE usage;
|
||||
};
|
||||
const Config& GetConfig() const;
|
||||
|
||||
private:
|
||||
// Does the swapchain initialization steps assuming there is nothing we can reuse.
|
||||
MaybeError InitializeSwapChainFromScratch();
|
||||
|
||||
Config mConfig;
|
||||
ComPtr<IDXGISwapChain3> mDXGISwapChain;
|
||||
};
|
||||
|
||||
} // namespace dawn::native::d3d
|
||||
|
||||
#endif // SRC_DAWN_NATIVE_D3D12_SWAPCHAIND3D12_H_
|
|
@ -39,6 +39,7 @@
|
|||
#include "dawn/native/d3d11/RenderPipelineD3D11.h"
|
||||
#include "dawn/native/d3d11/SamplerD3D11.h"
|
||||
#include "dawn/native/d3d11/ShaderModuleD3D11.h"
|
||||
#include "dawn/native/d3d11/SwapChainD3D11.h"
|
||||
#include "dawn/native/d3d11/TextureD3D11.h"
|
||||
#include "dawn/platform/DawnPlatform.h"
|
||||
#include "dawn/platform/tracing/TraceEvent.h"
|
||||
|
@ -289,7 +290,7 @@ ResultOrError<Ref<SwapChainBase>> Device::CreateSwapChainImpl(
|
|||
Surface* surface,
|
||||
SwapChainBase* previousSwapChain,
|
||||
const SwapChainDescriptor* descriptor) {
|
||||
return DAWN_UNIMPLEMENTED_ERROR("CreateSwapChainImpl");
|
||||
return SwapChain::Create(this, surface, previousSwapChain, descriptor);
|
||||
}
|
||||
|
||||
ResultOrError<Ref<TextureBase>> Device::CreateTextureImpl(const TextureDescriptor* descriptor) {
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright 2023 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/d3d11/SwapChainD3D11.h"
|
||||
|
||||
#include <windows.ui.xaml.media.dxinterop.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "dawn/native/Surface.h"
|
||||
#include "dawn/native/d3d/D3DError.h"
|
||||
#include "dawn/native/d3d/UtilsD3D.h"
|
||||
#include "dawn/native/d3d11/DeviceD3D11.h"
|
||||
#include "dawn/native/d3d11/TextureD3D11.h"
|
||||
|
||||
namespace dawn::native::d3d11 {
|
||||
// static
|
||||
ResultOrError<Ref<SwapChain>> SwapChain::Create(Device* device,
|
||||
Surface* surface,
|
||||
SwapChainBase* previousSwapChain,
|
||||
const SwapChainDescriptor* descriptor) {
|
||||
Ref<SwapChain> swapchain = AcquireRef(new SwapChain(device, surface, descriptor));
|
||||
DAWN_TRY(swapchain->Initialize(previousSwapChain));
|
||||
return swapchain;
|
||||
}
|
||||
|
||||
SwapChain::~SwapChain() = default;
|
||||
|
||||
IUnknown* SwapChain::GetD3DDeviceForCreatingSwapChain() {
|
||||
return ToBackend(GetDevice())->GetD3D11Device();
|
||||
}
|
||||
|
||||
void SwapChain::ReuseBuffers(SwapChainBase* previousSwapChain) {
|
||||
SwapChain* previousD3DSwapChain = ToBackend(previousSwapChain);
|
||||
mBuffer = std::move(previousD3DSwapChain->mBuffer);
|
||||
}
|
||||
|
||||
MaybeError SwapChain::CollectSwapChainBuffers() {
|
||||
ASSERT(GetDXGISwapChain() != nullptr);
|
||||
ASSERT(!mBuffer);
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/dxgi/ne-dxgi-dxgi_swap_effect
|
||||
// DXGISwapChain is created with DXGI_SWAP_EFFECT_FLIP_DISCARD, we can read and write to the
|
||||
// buffer 0 only for D3D11.
|
||||
DAWN_TRY(CheckHRESULT(GetDXGISwapChain()->GetBuffer(0, IID_PPV_ARGS(&mBuffer)),
|
||||
"Getting IDXGISwapChain buffer"));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
MaybeError SwapChain::PresentImpl() {
|
||||
DAWN_TRY(PresentDXGISwapChain());
|
||||
|
||||
mApiTexture->APIDestroy();
|
||||
mApiTexture = nullptr;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ResultOrError<Ref<TextureViewBase>> SwapChain::GetCurrentTextureViewImpl() {
|
||||
// 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, mBuffer));
|
||||
return mApiTexture->CreateView();
|
||||
}
|
||||
|
||||
MaybeError SwapChain::DetachAndWaitForDeallocation() {
|
||||
DetachFromSurface();
|
||||
return {};
|
||||
}
|
||||
|
||||
void SwapChain::DetachFromSurfaceImpl() {
|
||||
if (mApiTexture != nullptr) {
|
||||
mApiTexture->APIDestroy();
|
||||
mApiTexture = nullptr;
|
||||
}
|
||||
|
||||
mBuffer = nullptr;
|
||||
ReleaseDXGISwapChain();
|
||||
}
|
||||
|
||||
} // namespace dawn::native::d3d11
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2023 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.
|
||||
|
||||
#ifndef SRC_DAWN_NATIVE_D3D11_SWAPCHAIND3D11_H_
|
||||
#define SRC_DAWN_NATIVE_D3D11_SWAPCHAIND3D11_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "dawn/native/d3d/SwapChainD3D.h"
|
||||
|
||||
#include "dawn/native/IntegerTypes.h"
|
||||
|
||||
namespace dawn::native::d3d11 {
|
||||
|
||||
class Device;
|
||||
class Texture;
|
||||
|
||||
class SwapChain final : public d3d::SwapChain {
|
||||
public:
|
||||
static ResultOrError<Ref<SwapChain>> Create(Device* device,
|
||||
Surface* surface,
|
||||
SwapChainBase* previousSwapChain,
|
||||
const SwapChainDescriptor* descriptor);
|
||||
|
||||
private:
|
||||
using Base = d3d::SwapChain;
|
||||
using Base::Base;
|
||||
~SwapChain() override;
|
||||
|
||||
// SwapChainBase implementation
|
||||
MaybeError PresentImpl() override;
|
||||
ResultOrError<Ref<TextureViewBase>> GetCurrentTextureViewImpl() override;
|
||||
void DetachFromSurfaceImpl() override;
|
||||
|
||||
// d3d::SwapChain implementation
|
||||
IUnknown* GetD3DDeviceForCreatingSwapChain() override;
|
||||
void ReuseBuffers(SwapChainBase* previousSwapChain) override;
|
||||
// Does the swapchain initialization step of gathering the buffers.
|
||||
MaybeError CollectSwapChainBuffers() override;
|
||||
// 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() override;
|
||||
|
||||
ComPtr<ID3D11Texture2D> mBuffer;
|
||||
Ref<Texture> mApiTexture;
|
||||
};
|
||||
|
||||
} // namespace dawn::native::d3d11
|
||||
|
||||
#endif // SRC_DAWN_NATIVE_D3D12_SWAPCHAIND3D11_H_
|
|
@ -25,54 +25,6 @@
|
|||
#include "dawn/native/d3d12/TextureD3D12.h"
|
||||
|
||||
namespace dawn::native::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::TextureBinding) {
|
||||
dxgiUsage |= DXGI_USAGE_SHADER_INPUT;
|
||||
}
|
||||
if (usage & wgpu::TextureUsage::StorageBinding) {
|
||||
dxgiUsage |= DXGI_USAGE_UNORDERED_ACCESS;
|
||||
}
|
||||
if (usage & wgpu::TextureUsage::RenderAttachment) {
|
||||
dxgiUsage |= DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
}
|
||||
return dxgiUsage;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
ResultOrError<Ref<SwapChain>> SwapChain::Create(Device* device,
|
||||
Surface* surface,
|
||||
|
@ -85,161 +37,31 @@ ResultOrError<Ref<SwapChain>> SwapChain::Create(Device* device,
|
|||
|
||||
SwapChain::~SwapChain() = default;
|
||||
|
||||
void SwapChain::DestroyImpl() {
|
||||
SwapChainBase::DestroyImpl();
|
||||
DetachFromSurface();
|
||||
IUnknown* SwapChain::GetD3DDeviceForCreatingSwapChain() {
|
||||
return ToBackend(GetDevice())->GetCommandQueue().Get();
|
||||
}
|
||||
|
||||
// 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(SwapChainBase* previousSwapChain) {
|
||||
ASSERT(GetSurface()->GetType() == Surface::Type::WindowsHWND);
|
||||
|
||||
// Precompute the configuration parameters we want for the DXGI swapchain.
|
||||
mConfig.bufferCount = PresentModeToBufferCount(GetPresentMode());
|
||||
mConfig.format = d3d::DXGITextureFormat(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(crbug.com/dawn/269): 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.
|
||||
DAWN_INVALID_IF(previousSwapChain->GetBackendType() != wgpu::BackendType::D3D12,
|
||||
"D3D12 SwapChain cannot switch backend types from %s to %s.",
|
||||
previousSwapChain->GetBackendType(), wgpu::BackendType::D3D12);
|
||||
|
||||
SwapChain* previousD3D12SwapChain = ToBackend(previousSwapChain);
|
||||
|
||||
// TODO(crbug.com/dawn/269): 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.
|
||||
DAWN_INVALID_IF(GetDevice() != previousSwapChain->GetDevice(),
|
||||
"D3D12 SwapChain cannot switch between D3D 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<IDXGIFactory2> factory2 = nullptr;
|
||||
DAWN_TRY(CheckHRESULT(device->GetFactory()->QueryInterface(IID_PPV_ARGS(&factory2)),
|
||||
"Getting IDXGIFactory2"));
|
||||
|
||||
ComPtr<IDXGISwapChain1> swapChain1;
|
||||
switch (GetSurface()->GetType()) {
|
||||
case Surface::Type::WindowsHWND: {
|
||||
DAWN_TRY(CheckHRESULT(
|
||||
factory2->CreateSwapChainForHwnd(device->GetCommandQueue().Get(),
|
||||
static_cast<HWND>(GetSurface()->GetHWND()),
|
||||
&swapChainDesc, nullptr, nullptr, &swapChain1),
|
||||
"Creating the IDXGISwapChain1"));
|
||||
break;
|
||||
}
|
||||
case Surface::Type::WindowsCoreWindow: {
|
||||
DAWN_TRY(CheckHRESULT(
|
||||
factory2->CreateSwapChainForCoreWindow(device->GetCommandQueue().Get(),
|
||||
GetSurface()->GetCoreWindow(),
|
||||
&swapChainDesc, nullptr, &swapChain1),
|
||||
"Creating the IDXGISwapChain1"));
|
||||
break;
|
||||
}
|
||||
case Surface::Type::WindowsSwapChainPanel: {
|
||||
DAWN_TRY(CheckHRESULT(
|
||||
factory2->CreateSwapChainForComposition(device->GetCommandQueue().Get(),
|
||||
&swapChainDesc, nullptr, &swapChain1),
|
||||
"Creating the IDXGISwapChain1"));
|
||||
ComPtr<ISwapChainPanelNative> swapChainPanelNative;
|
||||
DAWN_TRY(CheckHRESULT(GetSurface()->GetSwapChainPanel()->QueryInterface(
|
||||
IID_PPV_ARGS(&swapChainPanelNative)),
|
||||
"Getting ISwapChainPanelNative"));
|
||||
DAWN_TRY(CheckHRESULT(swapChainPanelNative->SetSwapChain(swapChain1.Get()),
|
||||
"Setting SwapChain"));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
DAWN_TRY(CheckHRESULT(swapChain1.As(&mDXGISwapChain), "Gettting IDXGISwapChain1"));
|
||||
|
||||
return CollectSwapChainBuffers();
|
||||
void SwapChain::ReuseBuffers(SwapChainBase* previousSwapChain) {
|
||||
SwapChain* previousD3DSwapChain = ToBackend(previousSwapChain);
|
||||
mBuffers = std::move(previousD3DSwapChain->mBuffers);
|
||||
mBufferLastUsedSerials = std::move(previousD3DSwapChain->mBufferLastUsedSerials);
|
||||
}
|
||||
|
||||
MaybeError SwapChain::CollectSwapChainBuffers() {
|
||||
ASSERT(mDXGISwapChain != nullptr);
|
||||
ASSERT(GetDXGISwapChain() != 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])),
|
||||
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])),
|
||||
"Getting IDXGISwapChain buffer"));
|
||||
}
|
||||
|
||||
// Pretend all the buffers were last used at the beginning of time.
|
||||
mBufferLastUsedSerials.resize(mConfig.bufferCount, ExecutionSerial(0));
|
||||
mBufferLastUsedSerials.resize(config.bufferCount, ExecutionSerial(0));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -255,12 +77,7 @@ MaybeError SwapChain::PresentImpl() {
|
|||
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"));
|
||||
}
|
||||
DAWN_TRY(PresentDXGISwapChain());
|
||||
|
||||
// Record that "new" is the last time the buffer has been used.
|
||||
DAWN_TRY(device->NextSerial());
|
||||
|
@ -279,7 +96,7 @@ ResultOrError<Ref<TextureViewBase>> SwapChain::GetCurrentTextureViewImpl() {
|
|||
// 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 = mDXGISwapChain->GetCurrentBackBufferIndex();
|
||||
mCurrentBuffer = GetDXGISwapChain()->GetCurrentBackBufferIndex();
|
||||
DAWN_TRY(device->WaitForSerial(mBufferLastUsedSerials[mCurrentBuffer]));
|
||||
|
||||
// Create the API side objects for this use of the swapchain's buffer.
|
||||
|
@ -307,9 +124,9 @@ void SwapChain::DetachFromSurfaceImpl() {
|
|||
mApiTexture->APIDestroy();
|
||||
mApiTexture = nullptr;
|
||||
}
|
||||
|
||||
mDXGISwapChain = nullptr;
|
||||
mBuffers.clear();
|
||||
|
||||
ReleaseDXGISwapChain();
|
||||
}
|
||||
|
||||
} // namespace dawn::native::d3d12
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include "dawn/native/SwapChain.h"
|
||||
#include "dawn/native/d3d/SwapChainD3D.h"
|
||||
|
||||
#include "dawn/native/IntegerTypes.h"
|
||||
#include "dawn/native/d3d12/d3d12_platform.h"
|
||||
|
@ -27,7 +27,7 @@ namespace dawn::native::d3d12 {
|
|||
class Device;
|
||||
class Texture;
|
||||
|
||||
class SwapChain final : public SwapChainBase {
|
||||
class SwapChain final : public d3d::SwapChain {
|
||||
public:
|
||||
static ResultOrError<Ref<SwapChain>> Create(Device* device,
|
||||
Surface* surface,
|
||||
|
@ -35,37 +35,24 @@ class SwapChain final : public SwapChainBase {
|
|||
const SwapChainDescriptor* descriptor);
|
||||
|
||||
private:
|
||||
using Base = d3d::SwapChain;
|
||||
using Base::Base;
|
||||
~SwapChain() override;
|
||||
|
||||
void DestroyImpl() override;
|
||||
|
||||
using SwapChainBase::SwapChainBase;
|
||||
MaybeError Initialize(SwapChainBase* previousSwapChain);
|
||||
|
||||
struct Config {
|
||||
// Information that's passed to the D3D12 swapchain creation call.
|
||||
UINT bufferCount;
|
||||
UINT swapChainFlags;
|
||||
DXGI_FORMAT format;
|
||||
DXGI_USAGE usage;
|
||||
};
|
||||
|
||||
// SwapChainBase implementation
|
||||
MaybeError PresentImpl() override;
|
||||
ResultOrError<Ref<TextureViewBase>> GetCurrentTextureViewImpl() override;
|
||||
void DetachFromSurfaceImpl() override;
|
||||
|
||||
// Does the swapchain initialization steps assuming there is nothing we can reuse.
|
||||
MaybeError InitializeSwapChainFromScratch();
|
||||
// d3d::SwapChain implementation
|
||||
IUnknown* GetD3DDeviceForCreatingSwapChain() override;
|
||||
void ReuseBuffers(SwapChainBase* previousSwapChain) override;
|
||||
// Does the swapchain initialization step of gathering the buffers.
|
||||
MaybeError CollectSwapChainBuffers();
|
||||
MaybeError CollectSwapChainBuffers() override;
|
||||
// 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();
|
||||
MaybeError DetachAndWaitForDeallocation() override;
|
||||
|
||||
Config mConfig;
|
||||
|
||||
ComPtr<IDXGISwapChain3> mDXGISwapChain;
|
||||
std::vector<ComPtr<ID3D12Resource>> mBuffers;
|
||||
std::vector<ExecutionSerial> mBufferLastUsedSerials;
|
||||
uint32_t mCurrentBuffer = 0;
|
||||
|
|
Loading…
Reference in New Issue