#include "d3d11_device.h" #include "d3d11_query.h" namespace dxvk { D3D11Query::D3D11Query( D3D11Device* device, const D3D11_QUERY_DESC& desc) : m_device(device), m_desc(desc), m_state(D3D11_VK_QUERY_INITIAL), m_d3d10(this, device->GetD3D10Interface()) { Rc dxvkDevice = m_device->GetDXVKDevice(); switch (m_desc.Query) { case D3D11_QUERY_EVENT: m_event[0] = dxvkDevice->createGpuEvent(); break; case D3D11_QUERY_OCCLUSION: m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_OCCLUSION, VK_QUERY_CONTROL_PRECISE_BIT, 0); break; case D3D11_QUERY_OCCLUSION_PREDICATE: m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_OCCLUSION, 0, 0); break; case D3D11_QUERY_TIMESTAMP: m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_TIMESTAMP, 0, 0); break; case D3D11_QUERY_TIMESTAMP_DISJOINT: break; case D3D11_QUERY_PIPELINE_STATISTICS: m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_PIPELINE_STATISTICS, 0, 0); break; case D3D11_QUERY_SO_STATISTICS: case D3D11_QUERY_SO_STATISTICS_STREAM0: case D3D11_QUERY_SO_OVERFLOW_PREDICATE: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM0: // FIXME it is technically incorrect to map // SO_OVERFLOW_PREDICATE to the first stream, // but this is good enough for D3D10 behaviour m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT, 0, 0); break; case D3D11_QUERY_SO_STATISTICS_STREAM1: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM1: m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT, 0, 1); break; case D3D11_QUERY_SO_STATISTICS_STREAM2: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM2: m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT, 0, 2); break; case D3D11_QUERY_SO_STATISTICS_STREAM3: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM3: m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT, 0, 3); break; default: throw DxvkError(str::format("D3D11: Unhandled query type: ", desc.Query)); } } D3D11Query::~D3D11Query() { if (m_predicate.defined()) m_device->FreePredicateSlice(m_predicate); } HRESULT STDMETHODCALLTYPE D3D11Query::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11Asynchronous) || riid == __uuidof(ID3D11Query)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D10DeviceChild) || riid == __uuidof(ID3D10Asynchronous) || riid == __uuidof(ID3D10Query)) { *ppvObject = ref(&m_d3d10); return S_OK; } if (m_desc.Query == D3D11_QUERY_OCCLUSION_PREDICATE) { if (riid == __uuidof(ID3D11Predicate)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(ID3D10Predicate)) { *ppvObject = ref(&m_d3d10); return S_OK; } } Logger::warn("D3D11Query: Unknown interface query"); Logger::warn(str::format(riid)); return E_NOINTERFACE; } void STDMETHODCALLTYPE D3D11Query::GetDevice(ID3D11Device **ppDevice) { *ppDevice = ref(m_device); } UINT STDMETHODCALLTYPE D3D11Query::GetDataSize() { switch (m_desc.Query) { case D3D11_QUERY_EVENT: return sizeof(BOOL); case D3D11_QUERY_OCCLUSION: return sizeof(UINT64); case D3D11_QUERY_TIMESTAMP: return sizeof(UINT64); case D3D11_QUERY_TIMESTAMP_DISJOINT: return sizeof(D3D11_QUERY_DATA_TIMESTAMP_DISJOINT); case D3D11_QUERY_PIPELINE_STATISTICS: return sizeof(D3D11_QUERY_DATA_PIPELINE_STATISTICS); case D3D11_QUERY_OCCLUSION_PREDICATE: return sizeof(BOOL); case D3D11_QUERY_SO_STATISTICS: case D3D11_QUERY_SO_STATISTICS_STREAM0: case D3D11_QUERY_SO_STATISTICS_STREAM1: case D3D11_QUERY_SO_STATISTICS_STREAM2: case D3D11_QUERY_SO_STATISTICS_STREAM3: return sizeof(D3D11_QUERY_DATA_SO_STATISTICS); case D3D11_QUERY_SO_OVERFLOW_PREDICATE: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM0: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM1: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM2: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM3: return sizeof(BOOL); } Logger::err("D3D11Query: Failed to query data size"); return 0; } void STDMETHODCALLTYPE D3D11Query::GetDesc(D3D11_QUERY_DESC *pDesc) { *pDesc = m_desc; } void D3D11Query::Begin(DxvkContext* ctx) { if (unlikely(m_state == D3D11_VK_QUERY_BEGUN)) return; switch (m_desc.Query) { case D3D11_QUERY_EVENT: case D3D11_QUERY_TIMESTAMP: case D3D11_QUERY_TIMESTAMP_DISJOINT: break; default: ctx->beginQuery(m_query[0]); } m_state = D3D11_VK_QUERY_BEGUN; } void D3D11Query::End(DxvkContext* ctx) { switch (m_desc.Query) { case D3D11_QUERY_EVENT: ctx->signalGpuEvent(m_event[0]); break; case D3D11_QUERY_TIMESTAMP: ctx->writeTimestamp(m_query[0]); break; case D3D11_QUERY_TIMESTAMP_DISJOINT: break; default: if (unlikely(m_state != D3D11_VK_QUERY_BEGUN)) return; ctx->endQuery(m_query[0]); } if (m_predicate.defined()) ctx->writePredicate(m_predicate, m_query[0]); m_state = D3D11_VK_QUERY_ENDED; } HRESULT STDMETHODCALLTYPE D3D11Query::GetData( void* pData, UINT GetDataFlags) { if (m_desc.Query == D3D11_QUERY_EVENT) { DxvkGpuEventStatus status = m_event[0]->test(); if (status == DxvkGpuEventStatus::Invalid) return DXGI_ERROR_INVALID_CALL; bool signaled = status == DxvkGpuEventStatus::Signaled; if (pData != nullptr) *static_cast(pData) = signaled; return signaled ? S_OK : S_FALSE; } else { std::array queryData = { }; for (uint32_t i = 0; i < MaxGpuQueries && m_query[i] != nullptr; i++) { DxvkGpuQueryStatus status = m_query[i]->getData(queryData[i]); if (status == DxvkGpuQueryStatus::Invalid || status == DxvkGpuQueryStatus::Failed) return DXGI_ERROR_INVALID_CALL; if (status == DxvkGpuQueryStatus::Pending) return S_FALSE; } if (pData == nullptr) return S_OK; switch (m_desc.Query) { case D3D11_QUERY_OCCLUSION: *static_cast(pData) = queryData[0].occlusion.samplesPassed; return S_OK; case D3D11_QUERY_OCCLUSION_PREDICATE: *static_cast(pData) = queryData[0].occlusion.samplesPassed != 0; return S_OK; case D3D11_QUERY_TIMESTAMP: *static_cast(pData) = queryData[0].timestamp.time; return S_OK; case D3D11_QUERY_TIMESTAMP_DISJOINT: { auto data = static_cast(pData); data->Frequency = GetTimestampQueryFrequency(); data->Disjoint = FALSE; } return S_OK; case D3D11_QUERY_PIPELINE_STATISTICS: { auto data = static_cast(pData); data->IAVertices = queryData[0].statistic.iaVertices; data->IAPrimitives = queryData[0].statistic.iaPrimitives; data->VSInvocations = queryData[0].statistic.vsInvocations; data->GSInvocations = queryData[0].statistic.gsInvocations; data->GSPrimitives = queryData[0].statistic.gsPrimitives; data->CInvocations = queryData[0].statistic.clipInvocations; data->CPrimitives = queryData[0].statistic.clipPrimitives; data->PSInvocations = queryData[0].statistic.fsInvocations; data->HSInvocations = queryData[0].statistic.tcsPatches; data->DSInvocations = queryData[0].statistic.tesInvocations; data->CSInvocations = queryData[0].statistic.csInvocations; } return S_OK; case D3D11_QUERY_SO_STATISTICS: case D3D11_QUERY_SO_STATISTICS_STREAM0: case D3D11_QUERY_SO_STATISTICS_STREAM1: case D3D11_QUERY_SO_STATISTICS_STREAM2: case D3D11_QUERY_SO_STATISTICS_STREAM3: { auto data = static_cast(pData); data->NumPrimitivesWritten = queryData[0].xfbStream.primitivesWritten; data->PrimitivesStorageNeeded = queryData[0].xfbStream.primitivesNeeded; } return S_OK; case D3D11_QUERY_SO_OVERFLOW_PREDICATE: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM0: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM1: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM2: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM3: { auto data = static_cast(pData); *data = queryData[0].xfbStream.primitivesNeeded > queryData[0].xfbStream.primitivesWritten; } return S_OK; default: Logger::err(str::format("D3D11: Unhandled query type in GetData: ", m_desc.Query)); return E_INVALIDARG; } } } DxvkBufferSlice D3D11Query::GetPredicate(DxvkContext* ctx) { std::lock_guard lock(m_predicateLock); if (unlikely(m_desc.Query != D3D11_QUERY_OCCLUSION_PREDICATE)) return DxvkBufferSlice(); if (unlikely(m_state != D3D11_VK_QUERY_ENDED)) return DxvkBufferSlice(); if (unlikely(!m_predicate.defined())) { m_predicate = m_device->AllocPredicateSlice(); ctx->writePredicate(m_predicate, m_query[0]); } return m_predicate; } UINT64 D3D11Query::GetTimestampQueryFrequency() const { Rc device = m_device->GetDXVKDevice(); Rc adapter = device->adapter(); VkPhysicalDeviceLimits limits = adapter->deviceProperties().limits; return uint64_t(1'000'000'000.0f / limits.timestampPeriod); } }