Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

D3D9: Port flush heuristic from D3D11 #3529

Merged
merged 2 commits into from
Jun 26, 2023
Merged
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
70 changes: 33 additions & 37 deletions src/d3d9/d3d9_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ namespace dxvk {
, m_isSWVP ( (BehaviorFlags & D3DCREATE_SOFTWARE_VERTEXPROCESSING) ? true : false )
, m_csThread ( dxvkDevice, dxvkDevice->createContext(DxvkContextType::Primary) )
, m_csChunk ( AllocCsChunk() )
, m_submissionFence (new sync::Fence())
, m_d3d9Interop ( this )
, m_d3d9On12 ( this )
, m_d3d8Bridge ( this ) {
Expand Down Expand Up @@ -1003,7 +1004,7 @@ namespace dxvk {
if (dstTexInfo->IsAutomaticMip() && mipLevels != dstTexInfo->Desc()->MipLevels)
MarkTextureMipsDirty(dstTexInfo);

FlushImplicit(false);
ConsiderFlush(GpuFlushType::ImplicitWeakHint);

return D3D_OK;
}
Expand Down Expand Up @@ -1450,7 +1451,9 @@ namespace dxvk {
return D3D_OK;

// Do a strong flush if the first render target is changed.
FlushImplicit(RenderTargetIndex == 0 ? TRUE : FALSE);
ConsiderFlush(RenderTargetIndex == 0
? GpuFlushType::ImplicitStrongHint
: GpuFlushType::ImplicitWeakHint);
m_flags.set(D3D9DeviceFlag::DirtyFramebuffer);

m_state.renderTargets[RenderTargetIndex] = rt;
Expand Down Expand Up @@ -1529,7 +1532,7 @@ namespace dxvk {
if (m_state.depthStencil == ds)
return D3D_OK;

FlushImplicit(FALSE);
ConsiderFlush(GpuFlushType::ImplicitWeakHint);
m_flags.set(D3D9DeviceFlag::DirtyFramebuffer);

if (ds != nullptr && m_depthBiasRepresentation.depthBiasRepresentation != VK_DEPTH_BIAS_REPRESENTATION_FLOAT_EXT) {
Expand Down Expand Up @@ -1588,7 +1591,7 @@ namespace dxvk {
if (unlikely(!m_flags.test(D3D9DeviceFlag::InScene)))
return D3DERR_INVALIDCALL;

FlushImplicit(true);
ConsiderFlush(GpuFlushType::ImplicitStrongHint);

m_flags.clr(D3D9DeviceFlag::InScene);

Expand Down Expand Up @@ -4377,7 +4380,7 @@ namespace dxvk {
// We don't have to wait, but misbehaving games may
// still try to spin on `Map` until the resource is
// idle, so we should flush pending commands
FlushImplicit(FALSE);
ConsiderFlush(GpuFlushType::ImplicitWeakHint);
return false;
}
else {
Expand Down Expand Up @@ -4890,7 +4893,7 @@ namespace dxvk {
slice.slice);
}
UnmapTextures();
FlushImplicit(false);
ConsiderFlush(GpuFlushType::ImplicitWeakHint);
}

void D3D9DeviceEx::EmitGenerateMips(
Expand Down Expand Up @@ -5049,7 +5052,7 @@ namespace dxvk {
TrackBufferMappingBufferSequenceNumber(pResource);

UnmapTextures();
FlushImplicit(false);
ConsiderFlush(GpuFlushType::ImplicitWeakHint);
return D3D_OK;
}

Expand Down Expand Up @@ -5080,25 +5083,15 @@ namespace dxvk {

void D3D9DeviceEx::EmitCsChunk(DxvkCsChunkRef&& chunk) {
m_csSeqNum = m_csThread.dispatchChunk(std::move(chunk));
m_csIsBusy = true;
}


void D3D9DeviceEx::FlushImplicit(BOOL StrongHint) {
// Flush only if the GPU is about to go idle, in
// order to keep the number of submissions low.
uint32_t pending = m_dxvkDevice->pendingSubmissions();
void D3D9DeviceEx::ConsiderFlush(GpuFlushType FlushType) {
uint64_t chunkId = GetCurrentSequenceNumber();
uint64_t submissionId = m_submissionFence->value();

if (StrongHint || pending <= MaxPendingSubmits) {
auto now = dxvk::high_resolution_clock::now();

uint32_t delay = MinFlushIntervalUs
+ IncFlushIntervalUs * pending;

// Prevent flushing too often in short intervals.
if (now - m_lastFlush >= std::chrono::microseconds(delay))
Flush();
}
if (m_flushTracker.considerFlush(FlushType, chunkId, submissionId))
Flush();
}


Expand Down Expand Up @@ -5458,28 +5451,31 @@ namespace dxvk {
m_initializer->Flush();
m_converter->Flush();

if (m_csIsBusy || !m_csChunk->empty()) {
EmitStagingBufferMarker();
EmitStagingBufferMarker();

// Add commands to flush the threaded
// context, then flush the command list
EmitCs([](DxvkContext* ctx) {
ctx->flushCommandList(nullptr);
});
// Add commands to flush the threaded
// context, then flush the command list
uint64_t submissionId = ++m_submissionId;

FlushCsChunk();
EmitCs<false>([
cSubmissionFence = m_submissionFence,
cSubmissionId = submissionId
] (DxvkContext* ctx) {
ctx->signal(cSubmissionFence, cSubmissionId);
ctx->flushCommandList(nullptr);
});

// Reset flush timer used for implicit flushes
m_lastFlush = dxvk::high_resolution_clock::now();
m_csIsBusy = false;
}
FlushCsChunk();

m_flushSeqNum = m_csSeqNum;
m_flushTracker.notifyFlush(m_flushSeqNum, submissionId);
}


void D3D9DeviceEx::EndFrame() {
D3D9DeviceLock lock = LockDevice();

EmitCs([] (DxvkContext* ctx) {
EmitCs<false>([] (DxvkContext* ctx) {
ctx->endFrame();
});
}
Expand Down Expand Up @@ -6789,9 +6785,9 @@ namespace dxvk {
if (unlikely(pQuery->IsEvent())) {
pQuery->IsStalling()
? Flush()
: FlushImplicit(TRUE);
: ConsiderFlush(GpuFlushType::ImplicitStrongHint);
} else if (pQuery->IsStalling()) {
FlushImplicit(FALSE);
ConsiderFlush(GpuFlushType::ImplicitWeakHint);
}
}

Expand Down
18 changes: 12 additions & 6 deletions src/d3d9/d3d9_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <type_traits>
#include <unordered_map>

#include "../util/util_flush.h"
#include "../util/util_lru.h"

namespace dxvk {
Expand Down Expand Up @@ -927,7 +928,7 @@ namespace dxvk {
void SetVertexBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits);
void SetPixelBoolBitfield (uint32_t idx, uint32_t mask, uint32_t bits);

void FlushImplicit(BOOL StrongHint);
void ConsiderFlush(GpuFlushType FlushType);

bool ChangeReportedMemory(int64_t delta) {
if (IsExtended())
Expand Down Expand Up @@ -995,12 +996,15 @@ namespace dxvk {
return DxvkCsChunkRef(chunk, &m_csChunkPool);
}

template<typename Cmd>
template<bool AllowFlush = true, typename Cmd>
void EmitCs(Cmd&& command) {
if (unlikely(!m_csChunk->push(command))) {
EmitCsChunk(std::move(m_csChunk));

m_csChunk = AllocCsChunk();

if constexpr (AllowFlush)
ConsiderFlush(GpuFlushType::ImplicitWeakHint);

m_csChunk->push(command);
}
}
Expand Down Expand Up @@ -1343,12 +1347,14 @@ namespace dxvk {
D3D9ViewportInfo m_viewportInfo;

DxvkCsChunkPool m_csChunkPool;
dxvk::high_resolution_clock::time_point m_lastFlush
= dxvk::high_resolution_clock::now();
DxvkCsThread m_csThread;
DxvkCsChunkRef m_csChunk;
uint64_t m_csSeqNum = 0ull;
bool m_csIsBusy = false;

Rc<sync::Fence> m_submissionFence;
uint64_t m_submissionId = 0ull;
uint64_t m_flushSeqNum = 0ull;
GpuFlushTracker m_flushTracker;

std::atomic<int64_t> m_availableMemory = { 0 };
std::atomic<int32_t> m_samplerCount = { 0 };
Expand Down
2 changes: 1 addition & 1 deletion src/d3d9/d3d9_query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ namespace dxvk {
// they didn't call end, do some flushy stuff...
if (flush && hr == S_FALSE && m_state != D3D9_VK_QUERY_BEGUN) {
this->NotifyStall();
m_parent->FlushImplicit(FALSE);
m_parent->ConsiderFlush(GpuFlushType::ImplicitSynchronization);
}

return hr;
Expand Down
11 changes: 0 additions & 11 deletions src/dxvk/dxvk_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -498,17 +498,6 @@ namespace dxvk {
m_submissionQueue.unlockDeviceQueue();
}

/**
* \brief Number of pending submissions
*
* A return value of 0 indicates
* that the GPU is currently idle.
* \returns Pending submission count
*/
uint32_t pendingSubmissions() const {
return m_submissionQueue.pendingSubmissions();
}

/**
* \brief Increments a given stat counter
*
Expand Down
5 changes: 0 additions & 5 deletions src/dxvk/dxvk_queue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ namespace dxvk {
entry.status = status;
entry.submit = std::move(submitInfo);

m_pending += 1;
m_submitQueue.push(std::move(entry));
m_appendCond.notify_all();
}
Expand Down Expand Up @@ -215,10 +214,6 @@ namespace dxvk {
entry.submit.cmdList->notifyObjects();

lock.lock();

if (entry.submit.cmdList != nullptr)
m_pending -= 1;

m_finishQueue.pop();
m_finishCond.notify_all();
lock.unlock();
Expand Down
12 changes: 0 additions & 12 deletions src/dxvk/dxvk_queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,6 @@ namespace dxvk {

~DxvkSubmissionQueue();

/**
* \brief Number of pending submissions
*
* A return value of 0 indicates
* that the GPU is currently idle.
* \returns Pending submission count
*/
uint32_t pendingSubmissions() const {
return m_pending.load();
}

/**
* \brief Retrieves estimated GPU idle time
*
Expand Down Expand Up @@ -193,7 +182,6 @@ namespace dxvk {
std::atomic<VkResult> m_lastError = { VK_SUCCESS };

std::atomic<bool> m_stopped = { false };
std::atomic<uint32_t> m_pending = { 0u };
std::atomic<uint64_t> m_gpuIdle = { 0ull };

dxvk::mutex m_mutex;
Expand Down