Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Graphics/GraphicsEngineD3D12/include/FenceD3D12Impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ class FenceD3D12Impl final : public FenceBase<EngineD3D12ImplTraits>
private:
/// Access to the fence internal data is thread safe.
CComPtr<ID3D12Fence> m_pd3d12Fence; ///< D3D12 Fence object

const HANDLE m_FenceCompleteEvent;
};

} // namespace Diligent
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ CommandQueueD3D12Impl::CommandQueueD3D12Impl(IReferenceCounters* pRefCounters,
m_WaitForGPUEventHandle{CreateEvent(nullptr, false, false, nullptr)}
// clang-format on
{
VERIFY_EXPR(m_WaitForGPUEventHandle != INVALID_HANDLE_VALUE);
VERIFY_EXPR(m_WaitForGPUEventHandle != nullptr);
m_d3d12Fence->Signal(0);
}

Expand Down
27 changes: 7 additions & 20 deletions Graphics/GraphicsEngineD3D12/src/FenceD3D12Impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,8 @@ namespace Diligent
FenceD3D12Impl::FenceD3D12Impl(IReferenceCounters* pRefCounters,
RenderDeviceD3D12Impl* pDevice,
const FenceDesc& Desc) :
TFenceBase{pRefCounters, pDevice, Desc},
m_FenceCompleteEvent //
{
CreateEvent(NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // initial state is nonsignaled
NULL) // object name
}
TFenceBase{pRefCounters, pDevice, Desc}
{
VERIFY(m_FenceCompleteEvent != NULL, "Failed to create fence complete event");

const D3D12_FENCE_FLAGS Flags = (m_Desc.Type == FENCE_TYPE_GENERAL && pDevice->GetNumImmediateContexts() > 1) ? D3D12_FENCE_FLAG_SHARED : D3D12_FENCE_FLAG_NONE;
ID3D12Device* const pd3d12Device = pDevice->GetD3D12Device();
HRESULT hr = pd3d12Device->CreateFence(0, Flags, __uuidof(m_pd3d12Fence), reinterpret_cast<void**>(static_cast<ID3D12Fence**>(&m_pd3d12Fence)));
Expand All @@ -67,10 +58,6 @@ FenceD3D12Impl::~FenceD3D12Impl()
{
// D3D12 object can only be destroyed when it is no longer used by the GPU
GetDevice()->SafeReleaseDeviceObject(std::move(m_pd3d12Fence), ~0ull);
if (m_FenceCompleteEvent != NULL)
{
CloseHandle(m_FenceCompleteEvent);
}
}

Uint64 FenceD3D12Impl::GetCompletedValue()
Expand All @@ -94,13 +81,13 @@ void FenceD3D12Impl::Wait(Uint64 Value)
if (GetCompletedValue() >= Value)
return;

if (m_FenceCompleteEvent != NULL)
{
m_pd3d12Fence->SetEventOnCompletion(Value, m_FenceCompleteEvent);
WaitForSingleObject(m_FenceCompleteEvent, INFINITE);
}
else
// A null event blocks this caller until Value completes and supports concurrent waits.
// Reusing a manual-reset event would allow later waits to return prematurely.
const HRESULT hr = m_pd3d12Fence->SetEventOnCompletion(Value, nullptr);
if (FAILED(hr))
{
// Wait() must not return before the fence reaches the value, so fall back to polling.
LOG_D3D_ERROR(hr, FormatString("Failed to wait for fence '", m_Desc.Name, "' to reach value ", Value, ". Falling back to polling."));
while (GetCompletedValue() < Value)
std::this_thread::sleep_for(std::chrono::microseconds{1});
}
Expand Down
14 changes: 13 additions & 1 deletion Graphics/GraphicsEngineD3DBase/include/SwapChainD3DBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class SwapChainD3DBase : public SwapChainBase<BaseInterface>

~SwapChainD3DBase()
{
ReleaseFrameLatencyWaitableObject();
if (m_pSwapChain)
{
// Swap chain must be in windowed mode when it is destroyed
Expand All @@ -85,6 +86,15 @@ class SwapChainD3DBase : public SwapChainBase<BaseInterface>
}

protected:
void ReleaseFrameLatencyWaitableObject()
{
if (m_FrameLatencyWaitableObject != NULL)
{
CloseHandle(m_FrameLatencyWaitableObject);
m_FrameLatencyWaitableObject = NULL;
}
}

virtual void UpdateSwapChain(bool CreateNew) = 0;

bool Resize(Uint32 NewWidth, Uint32 NewHeight, SURFACE_TRANSFORM NewPreTransform, Int32 Dummy = 0 /*To be different from virtual function*/)
Expand All @@ -103,6 +113,9 @@ class SwapChainD3DBase : public SwapChainBase<BaseInterface>

void CreateDXGISwapChain(IUnknown* pD3D11DeviceOrD3D12CmdQueue)
{
// The previous swap chain, if any, has already been released. Close its waitable object.
ReleaseFrameLatencyWaitableObject();

#if PLATFORM_WIN32
HWND hWnd = reinterpret_cast<HWND>(m_Window.hWnd);
if (m_SwapChainDesc.Width == 0 || m_SwapChainDesc.Height == 0)
Expand Down Expand Up @@ -282,7 +295,6 @@ class SwapChainD3DBase : public SwapChainBase<BaseInterface>
}
else
{
m_FrameLatencyWaitableObject = NULL;
SetDXGIDeviceMaximumFrameLatency();
}
}
Expand Down
41 changes: 41 additions & 0 deletions Tests/DiligentCoreAPITest/src/FenceTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
* of the possibility of such damages.
*/

#include <chrono>
#include <thread>

#include "GPUTestingEnvironment.hpp"
#include "TestingSwapChainBase.hpp"
#include "BasicMath.hpp"
Expand Down Expand Up @@ -327,4 +330,42 @@ TEST_F(FenceTest, ContextWaitForAnotherContext)
pSwapChain->Present();
}


TEST_F(FenceTest, RepeatedCPUWait)
{
auto* pEnv = GPUTestingEnvironment::GetInstance();
auto* pDevice = pEnv->GetDevice();
if (!pDevice->GetDeviceInfo().Features.NativeFence)
{
GTEST_SKIP() << "NativeFence feature is not supported";
}

FenceDesc FenceCI;
FenceCI.Name = "Repeated CPU wait";
FenceCI.Type = FENCE_TYPE_GENERAL;

RefCntAutoPtr<IFence> pFence;
pDevice->CreateFence(FenceCI, &pFence);
ASSERT_NE(pFence, nullptr);

// Gives Wait() time to start blocking before the fence is signaled
static constexpr std::chrono::milliseconds SignalDelay{20};
// Every wait after the first blocking one must also block until its own value is reached
static constexpr Uint64 NumWaits = 4;

for (Uint64 Value = 1; Value <= NumWaits; ++Value)
{
std::thread SignalThread{
[&pFence, Value]() {
std::this_thread::sleep_for(SignalDelay);
pFence->Signal(Value);
}};

pFence->Wait(Value);
EXPECT_GE(pFence->GetCompletedValue(), Value) << "Wait() returned before the fence reached the value";

SignalThread.join();
}
}

} // namespace
Loading