mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 04:38:30 +09:00
[Feat] (DirectGLES, DirectVulkan): instrument the six memo gates, the staging byte paths and both Present hooks with the MGPipe counters
- The sites of plan B section 2.3.1, verified against dev@81b17c0b (the plan's own line numbers for DirectGLES drift by 4-9 lines; the DirectVulkan ones are exact): SyncRenderState is DirectGLES.cpp:1994 with the version read at :1998 and the early-out at :2007-2010 (plan says 2003 / 2007 / 2016-2018); SyncNeccessaryTextures at :1511 (plan :1520); CurrentUnitBindingsEpoch at :1412-1435 (plan :1418-1436); PrepareForDraw at :2907-2968 (plan :2916-2976); the global-UBO upload at :3355-3397 (plan :3369-3392); TrySetupDrawFastPath :5994, GetOrCreatePipeline :4948-4993, ApplyDynamicDrawStateTail :5871-5893 and UniformManager ResolveUniformBufferPayload :2022/:2052 all as cited. - Accessor counting is STATIC TALLIES at ten hot entry points, not a wrapper around the 293 pGLContext-> sites: each instrumented function adds the number of accessor calls its own body made on the path taken. Reads inside callees, and every conditional read (the sRGB capability in SyncRenderState, the XFB probe and the version-gated parameter fetch in TrySetupDrawFastPath, the cull-mode/logic-op/tessellation reads in the pipeline payload builder) are excluded, so the number is a consistent LOWER bound. The full inventory of what is and is not counted is the header comment of PipeStats.cpp. - The Magma fast-path gate is counted from SetupDraw, not from inside TrySetupDrawFastPath: that function has 27 decline returns and one success return, and counting at the caller is the only shape that cannot miss one. - The texture upload counts the SHAPE (union box vs N-rect list) separately from the bytes, because SSIM is blind to the shape and the +6 ms/frame Mali regression of section 7.3 was a shape regression, not a byte one. - Frame boundary: DirectGLES::Present after the ring upkeep, and DirectVulkan's backend Present rather than VulkanRenderer::Present - the latter has an early return for the no-usable-swapchain case, and a suspended frame is still a frame the counters close. - Measured on lavapipe/llvmpipe with MOBILEGL_PIPE_STATS=1, GuiBatchScenario, 14 frames and 26 draws: Espryt 20.65 accessor calls per draw (gates ers 22/18, etl 71/9, eub 71/9), Magma 15.54 (mfp 12/14, mpm 0/14, mdt 12/14). Both land inside the 10-25 band section 2.3.1 predicted and far below the 124/169 static counts, which is the correction that section was written to force.
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
#include <MG_Util/Converters/MGToGL/RenderStateEnumConverter.h>
|
||||
#include <MG_Util/Math/HalfFloat.h>
|
||||
#include <MG_Util/Metrics/BufferMetrics.h>
|
||||
#include <MG_Util/Metrics/PipeStats.h>
|
||||
#include <MG_Util/Texture/PixelStoreProcessor.h>
|
||||
#include <Config.h>
|
||||
#include <atomic>
|
||||
@@ -1412,10 +1413,21 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
static Uint64 CurrentUnitBindingsEpoch(Int maxTouchedUnit) {
|
||||
const Uint64 contextId = MG_State::pGLContext->GetTextureContextId();
|
||||
const Uint64 bindGeneration = MG_State::pGLContext->GetTextureBindGeneration();
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
// Two accessor calls whichever way the shutter goes; only the unit WALK is
|
||||
// gated, and that walk reads no GLContext accessor of its own.
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::AccessorCalls, 2);
|
||||
}
|
||||
if (g_observedUnitBindingsContextId == contextId && g_observedUnitBindingsMaxUnit == maxTouchedUnit &&
|
||||
g_observedUnitBindingsGeneration == bindGeneration) {
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::CountGate(MG_Util::PipeStats::Gate::EsprytUnitBindingsEpoch, /*hit=*/true);
|
||||
}
|
||||
return g_unitBindingsEpoch;
|
||||
}
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::CountGate(MG_Util::PipeStats::Gate::EsprytUnitBindingsEpoch, /*hit=*/false);
|
||||
}
|
||||
if (g_observedUnitBindingsContextId != contextId || g_observedUnitBindingsMaxUnit != maxTouchedUnit ||
|
||||
!UnitBindingsUnchanged(maxTouchedUnit, g_observedUnitBindings)) {
|
||||
CaptureUnitBindings(maxTouchedUnit, g_observedUnitBindings);
|
||||
@@ -1505,6 +1517,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
keys.maxTouchedUnit = MG_State::pGLContext->GetMaxTouchedTextureUnit();
|
||||
keys.samplingGeneration = MG_State::pGLContext->GetSamplingResolutionGeneration();
|
||||
keys.unitBindingsEpoch = CurrentUnitBindingsEpoch(keys.maxTouchedUnit);
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
// The three reads above; CurrentUnitBindingsEpoch counts its own two.
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::AccessorCalls, 3);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
@@ -1534,6 +1550,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_unitTextureSyncListEpoch == unitBindingsEpoch &&
|
||||
g_unitTextureSyncListSamplingGeneration == samplingGeneration &&
|
||||
PairingsIntact(g_unitTextureSyncList)) {
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
// Gate 2 of section 2.3.1. The served path walks the memoised entries
|
||||
// and reads no GLContext accessor at all.
|
||||
MG_Util::PipeStats::CountGate(MG_Util::PipeStats::Gate::EsprytTextureSyncList, /*hit=*/true);
|
||||
}
|
||||
for (const auto& entry : g_unitTextureSyncList) {
|
||||
// Aggregate gate == the conjunction of the three callees' own
|
||||
// early-outs (see IsDrawSyncClean); skipping on true is
|
||||
@@ -1546,6 +1567,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
entry.backend->SyncMipmapsToBackend(*entry.slot);
|
||||
}
|
||||
} else {
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::CountGate(MG_Util::PipeStats::Gate::EsprytTextureSyncList, /*hit=*/false);
|
||||
// One GetTextureUnitObject per touched unit in the rebuild walk below.
|
||||
MG_Util::PipeStats::AddCalls(
|
||||
MG_Util::PipeStats::CallClass::AccessorCalls,
|
||||
maxTouchedUnit >= 0 ? static_cast<Uint64>(maxTouchedUnit) + 1u : 0u);
|
||||
}
|
||||
g_unitTextureSyncListValid = false;
|
||||
g_unitTextureSyncList.clear();
|
||||
for (Int index = 0; index <= maxTouchedUnit; ++index) {
|
||||
@@ -2006,8 +2034,22 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
const Bool colorMaskWidenDirty = appliedWidenMask != g_syncedColorMaskAlphaWidenMask;
|
||||
if (!forceFullPush && !colorMaskWidenDirty && g_hasSyncedRenderState &&
|
||||
currentRenderStateVersion == g_syncedRenderStateVersion) {
|
||||
// Gate 1 of section 2.3.1: the steady-state cost of this whole function is
|
||||
// the one Uint16 read above plus this compare.
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::CountGate(MG_Util::PipeStats::Gate::EsprytRenderState, /*hit=*/true);
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::AccessorCalls, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::CountGate(MG_Util::PipeStats::Gate::EsprytRenderState, /*hit=*/false);
|
||||
// The version read above, the parameter-block fetch and the viewport fetch
|
||||
// below - the three accessor calls this function makes unconditionally on a
|
||||
// miss. The conditional sRGB capability read further down is deliberately
|
||||
// NOT counted (see the inventory in PipeStats.cpp).
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::AccessorCalls, 3);
|
||||
}
|
||||
|
||||
const auto& parameters = MG_State::pGLContext->GetRenderStateParameters();
|
||||
|
||||
@@ -2924,6 +2966,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// cross-TU call with a guarded static inside - repeating it per stage showed
|
||||
// up in draw-loop profiles.
|
||||
const auto& currentProgram = MG_State::pGLContext->GetProgramForDraw();
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
// THE per-draw denominator for Espryt, plus this function's own two accessor
|
||||
// calls (the VAO and the draw program). Everything the callees below read is
|
||||
// counted by the callees that are instrumented; the rest is not counted (see
|
||||
// the inventory in PipeStats.cpp).
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::Draws, 1);
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::AccessorCalls, 2);
|
||||
}
|
||||
const TextureImpl::DrawTextureSyncKeys textureKeys = TextureImpl::CaptureDrawTextureSyncKeys();
|
||||
|
||||
BufferImpl::SyncNeccessaryBuffers(currentVAO, vaoTwin, vaoConfigVersion,
|
||||
@@ -3370,6 +3420,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (BufferImpl::UboRingAllocate(bindSize, offset)) {
|
||||
std::memcpy(static_cast<Uint8*>(BufferImpl::UboRingMappedPtr()) + offset,
|
||||
currentProgram->MapUBO(), uboSize);
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::AddBytes(MG_Util::PipeStats::ByteClass::StageUboGlobal,
|
||||
static_cast<Uint64>(uboSize));
|
||||
}
|
||||
ringSlot = {uboContentVersion, BufferImpl::UboRingGeneration(), frameSerial,
|
||||
offset};
|
||||
slotValid = true;
|
||||
@@ -3389,6 +3443,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_GLESFuncs.glBindBuffer(GL_UNIFORM_BUFFER, backendProgram.GetBackendGlobalUBOId());
|
||||
g_GLESFuncs.glBufferSubData(GL_UNIFORM_BUFFER, 0, currentProgram->GetUBOSize(),
|
||||
currentProgram->MapUBO());
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::AddBytes(
|
||||
MG_Util::PipeStats::ByteClass::StageUboGlobal,
|
||||
static_cast<Uint64>(currentProgram->GetUBOSize()));
|
||||
}
|
||||
g_GLESFuncs.glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
backendProgram.SetLastUploadedGlobalUboVersion(uboContentVersion);
|
||||
}
|
||||
@@ -4306,6 +4365,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_restartIndices.capacity = capacity;
|
||||
if (data != nullptr && bytes != 0) {
|
||||
g_GLESFuncs.glBufferSubData(BufferImpl::TempBufferTarget, 0, static_cast<GLsizeiptr>(bytes), data);
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
// Rewritten index list staged on the draw path: in a split build these
|
||||
// bytes are the index-mirror-versus-ship decision of section 8.
|
||||
MG_Util::PipeStats::AddBytes(MG_Util::PipeStats::ByteClass::StageIndexClient,
|
||||
static_cast<Uint64>(bytes));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -10638,6 +10703,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
BufferImpl::UnpackRingOnPresent();
|
||||
BufferImpl::UploadRingOnPresent();
|
||||
BufferImpl::TrimBufferPool();
|
||||
|
||||
// THE frame boundary for the MGPipe counters: publish this frame's plots, fold the
|
||||
// frame into the run totals and, every 120th frame, emit the summary line.
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::OnPresent();
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyEGLContext() {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <MG_Util/ShaderTranspiler/TranslationCache.h>
|
||||
|
||||
#include <MG_Util/BackendLoaders/OpenGL/Loader.h>
|
||||
#include <MG_Util/Metrics/PipeStats.h>
|
||||
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
|
||||
#include <MG_Util/Converters/MGToGL/DataTypeConverter.h>
|
||||
#include <MG_Util/Converters/MGToGL/BufferEnumConverter.h>
|
||||
@@ -779,6 +780,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
const void* initialData =
|
||||
(size > 0 && bufferObject.HasDefinedContent()) ? bufferObject.MappedData() : nullptr;
|
||||
g_GLESFuncs.glBufferData(TempBufferTarget, (GLsizeiptr)size, initialData, usage);
|
||||
if (MG_Util::PipeStats::Enabled() && initialData != nullptr) {
|
||||
// An ORPHANING respecify passes NULL and moves nothing, which is exactly
|
||||
// why the test is on initialData rather than on size.
|
||||
MG_Util::PipeStats::AddBytes(MG_Util::PipeStats::ByteClass::StageBuffer,
|
||||
static_cast<Uint64>(size));
|
||||
}
|
||||
resource.storageSize = size;
|
||||
resource.storageInitialized = true;
|
||||
resource.pendingRespecify = false;
|
||||
@@ -886,6 +893,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
const SizeT start = std::min(range.start, end);
|
||||
const SizeT size = end - start;
|
||||
if (size == 0) continue;
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
// Counted once per queued range, before the three delivery shapes
|
||||
// below diverge: all three move exactly these bytes, and it is the
|
||||
// byte count - not the shape - that sizes SEG_STAGE.
|
||||
MG_Util::PipeStats::AddBytes(MG_Util::PipeStats::ByteClass::StageBuffer,
|
||||
static_cast<Uint64>(size));
|
||||
}
|
||||
// The invalidating map's fast path is SHAPE-dependent on this Mali
|
||||
// driver: a whole-buffer invalidation renames the store outright,
|
||||
// and a large range gets fresh pages - but a small unaligned range
|
||||
@@ -953,6 +967,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (write.offset >= limit) continue;
|
||||
const SizeT size = std::min(write.bytes.size(), limit - write.offset);
|
||||
if (size == 0) continue;
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::AddBytes(MG_Util::PipeStats::ByteClass::StageBuffer,
|
||||
static_cast<Uint64>(size));
|
||||
}
|
||||
SizeT ringOffset = 0;
|
||||
if (ringUsable && size <= kUploadRingMaxBytes &&
|
||||
RingAllocate(g_uploadRing, size, ringOffset)) {
|
||||
@@ -2545,6 +2563,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_GLESFuncs.glBufferData(GL_ARRAY_BUFFER,
|
||||
static_cast<GLsizeiptr>(converted.size() * sizeof(Float)),
|
||||
converted.data(), GL_STREAM_DRAW);
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::AddBytes(MG_Util::PipeStats::ByteClass::StageVertexClient,
|
||||
static_cast<Uint64>(converted.size() * sizeof(Float)));
|
||||
}
|
||||
// GL ignores `normalized` for floating-point array types, so it is not
|
||||
// forwarded here either.
|
||||
g_GLESFuncs.glVertexAttribPointer(attribIndex, attrib.Size, GL_FLOAT, GL_FALSE,
|
||||
@@ -2575,6 +2597,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
BufferImpl::BindBufferId(GL_ARRAY_BUFFER, bufferId);
|
||||
g_GLESFuncs.glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(uploadSize), clientData,
|
||||
GL_STREAM_DRAW);
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::AddBytes(MG_Util::PipeStats::ByteClass::StageVertexClient,
|
||||
static_cast<Uint64>(uploadSize));
|
||||
}
|
||||
|
||||
if (!attrib.IsInteger) {
|
||||
const GLint glSize = attrib.IsBgra ? static_cast<GLint>(GL_BGRA) : attrib.Size;
|
||||
@@ -4391,6 +4417,42 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (ringStaged) {
|
||||
BufferImpl::BindPixelUnpackBufferId(BufferImpl::UnpackRingBufferId());
|
||||
}
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
// One emission per (upload target, level) that ships texels;
|
||||
// the switch below turns it into either one union-box job or
|
||||
// dirtyRectCount rect jobs. The box/rect split is counted
|
||||
// separately from the bytes on purpose: SSIM is blind to it
|
||||
// and the +6 ms/frame Mali cliff was a shape regression, not
|
||||
// a byte regression (plan section 7.3).
|
||||
const Bool rectShape = subRectEligible && dirtyRectCount >= 2;
|
||||
Uint64 shippedBytes = 0;
|
||||
if (rectShape) {
|
||||
for (SizeT r = 0; r < dirtyRectCount; ++r) {
|
||||
const auto& rect = dirtyRects[r];
|
||||
shippedBytes += static_cast<Uint64>(rect.hi.x() - rect.lo.x()) *
|
||||
static_cast<Uint64>(rect.hi.y() - rect.lo.y()) *
|
||||
static_cast<Uint64>(std::max(rect.hi.z() - rect.lo.z(), 1)) *
|
||||
static_cast<Uint64>(bpp);
|
||||
}
|
||||
} else if (subRectEligible) {
|
||||
shippedBytes = static_cast<Uint64>(regionSize.x()) *
|
||||
static_cast<Uint64>(regionSize.y()) *
|
||||
static_cast<Uint64>(std::max(regionSize.z(), 1)) *
|
||||
static_cast<Uint64>(bpp);
|
||||
} else {
|
||||
shippedBytes = static_cast<Uint64>(byteSize);
|
||||
}
|
||||
MG_Util::PipeStats::AddBytes(MG_Util::PipeStats::ByteClass::StageTexture,
|
||||
shippedBytes);
|
||||
MG_Util::PipeStats::AddCalls(
|
||||
MG_Util::PipeStats::CallClass::TextureUploadEmissions, 1);
|
||||
MG_Util::PipeStats::AddCalls(
|
||||
rectShape ? MG_Util::PipeStats::CallClass::TextureUploadRectEmissions
|
||||
: MG_Util::PipeStats::CallClass::TextureUploadBoxEmissions,
|
||||
1);
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::TextureUploadJobs,
|
||||
rectShape ? static_cast<Uint64>(dirtyRectCount) : 1u);
|
||||
}
|
||||
switch (MapToBackendTextureTarget(stateTextureObject->GetTarget())) {
|
||||
case TextureTarget::Texture2D:
|
||||
case TextureTarget::TextureCubeMap:
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "MG_State/GLState/ErrorState/ErrorInfo.h"
|
||||
#include "MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h"
|
||||
#include "MG_Util/Converters/GLToMG/TextureEnumConverter.h"
|
||||
#include "MG_Util/Metrics/PipeStats.h"
|
||||
#include "MG_Util/Metrics/TextureMetrics.h"
|
||||
#include "MG_Util/Miscellany/IndexGenerator.h"
|
||||
#include <atomic>
|
||||
@@ -1430,5 +1431,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void Present() {
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::Present called with null VulkanRenderer");
|
||||
pVulkanRenderer->Present();
|
||||
// THE frame boundary for the MGPipe counters, at the backend entry point rather
|
||||
// than inside VulkanRenderer::Present: that function has an early return for the
|
||||
// no-usable-swapchain case, and a suspended frame is still a frame the counters
|
||||
// must close.
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::OnPresent();
|
||||
}
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "MG_Util/Converters/GLToMG/TextureEnumConverter.h"
|
||||
#include "MG_Util/Converters/MGToStr/FramebufferEnumConverter.h"
|
||||
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
|
||||
#include "MG_Util/Metrics/PipeStats.h"
|
||||
#include "MG_Util/Metrics/TextureMetrics.h"
|
||||
#include "MG_Util/ShaderTranspiler/Types.h"
|
||||
#include <Config.h>
|
||||
@@ -2058,6 +2059,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
out.payload = outData;
|
||||
out.payloadSize = outSize;
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
// D-B8: these are the bytes Magma repacks into its own UBO ring, i.e. exactly
|
||||
// the host payload a split build would have to ship with set_shader_buffers.
|
||||
// Espryt binds the frontend buffer to the driver and contributes nothing here,
|
||||
// which is why the class is named for the payload and not for the call.
|
||||
MG_Util::PipeStats::AddBytes(MG_Util::PipeStats::ByteClass::StageUboNamed,
|
||||
static_cast<Uint64>(outSize));
|
||||
}
|
||||
|
||||
// Zero-copy direct bind: for a persistent-mapped coherent app buffer whose full reflected
|
||||
// block fits within the aligned bound range, point the descriptor straight at the app's
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "MG_Util/Converters/MGToVk/RenderStateEnumConverter.h"
|
||||
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
|
||||
#include "MG_Util/Math/HalfFloat.h"
|
||||
#include "MG_Util/Metrics/PipeStats.h"
|
||||
#include "MG_Util/Metrics/TextureMetrics.h"
|
||||
#include "MG_Util/SelfTest/PrimitivesGeneratedNoXfbProbe.h"
|
||||
#include "MG_Util/Texture/PixelStoreProcessor.h"
|
||||
@@ -4999,9 +5000,19 @@ void main() {
|
||||
entry.pipelineStateHash == pipelineStateHash &&
|
||||
entry.primitiveRestartEnable == primitiveRestartEnable &&
|
||||
entry.transformFlags == transformFlags) {
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
// Gate 5 of section 2.3.1. On a hit this whole function cost the one
|
||||
// GetPipelineStateVersion read above plus this value compare.
|
||||
MG_Util::PipeStats::CountGate(MG_Util::PipeStats::Gate::MagmaPipelineMemo, /*hit=*/true);
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::AccessorCalls, 1);
|
||||
}
|
||||
return entry.pipeline;
|
||||
}
|
||||
}
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::CountGate(MG_Util::PipeStats::Gate::MagmaPipelineMemo, /*hit=*/false);
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::AccessorCalls, 1);
|
||||
}
|
||||
|
||||
// Shape gate. Behind the memo probe deliberately: only a pipeline that was created
|
||||
// successfully is ever memoized, so a program refused here can never be sitting in the
|
||||
@@ -5157,6 +5168,17 @@ void main() {
|
||||
syntheticVertexInputState.pNext = vis.state.pNext;
|
||||
pipelineVertexInputState = &syntheticVertexInputState;
|
||||
}
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
// THE payload-builder walk section 2.3.1 says only runs on a pipeline memo
|
||||
// miss. Counted as a constant: the unconditional accessor reads between here
|
||||
// and the end of the payload build (the six capability reads, the draw-FBO
|
||||
// slot, the two stencil faces, the polygon mode, sample shading + min sample
|
||||
// shading, patch vertices, the depth mask and the depth func, and the second
|
||||
// draw-FBO slot read). Reads that are themselves conditional - the cull-mode
|
||||
// ternary, the logic-op fetch, the two tessellation default-level reads - are
|
||||
// deliberately excluded, so this stays a LOWER bound like every other tally.
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::AccessorCalls, 15);
|
||||
}
|
||||
auto cullFaceEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::CullFace);
|
||||
auto depthTestEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::DepthTest);
|
||||
auto polygonOffsetFillEnabled =
|
||||
@@ -5890,8 +5912,18 @@ void main() {
|
||||
if (shadow.dynamicTailValid && shadow.dynamicTailParamsVersion == paramsVersion &&
|
||||
shadow.dynamicTailExtentX == extent.x() && shadow.dynamicTailExtentY == extent.y() &&
|
||||
shadow.dynamicTailIsDefaultFbo == isDefaultFbo) {
|
||||
// Gate 6 of section 2.3.1: one version read plus a four-integer compare.
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::CountGate(MG_Util::PipeStats::Gate::MagmaDynamicTail, /*hit=*/true);
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::AccessorCalls, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
// The version read above and the bulk parameter fetch that builds the value key.
|
||||
MG_Util::PipeStats::CountGate(MG_Util::PipeStats::Gate::MagmaDynamicTail, /*hit=*/false);
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::AccessorCalls, 2);
|
||||
}
|
||||
const VkSurfaceTransformFlagBitsKHR preTransform = m_swapchainObject.GetPreTransform();
|
||||
// Second-level VALUE gate: the version moved, but RenderState's version counts
|
||||
// every parameter, most of which this tail never reads. Build the key over
|
||||
@@ -6365,6 +6397,15 @@ void main() {
|
||||
MOBILEGL_ASSERT(idxUploadOk, "SetupDraw fast path: failed to upload index buffer");
|
||||
}
|
||||
ApplyDynamicDrawStateTail(frame, snap.renderPassExtent, snap.drawFboIsDefault, snap.viewportCount);
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
// The six accessor reads this function makes unconditionally on the path that
|
||||
// reaches here: the draw program, the VAO, the draw-FBO slot, the pipeline
|
||||
// state version, the texture bind generation and the sampling-resolution
|
||||
// generation. The XFB-active probe is elided on a device without the feature
|
||||
// and the parameter-block fetch only runs when the pipeline state version
|
||||
// moved, so neither is counted (lower bound, as everywhere else).
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::AccessorCalls, 6);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -6390,9 +6431,27 @@ void main() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
// THE per-draw denominator for Magma, plus the draw-program read above. Placed
|
||||
// here rather than inside TrySetupDrawFastPath because the fast path has 27
|
||||
// decline returns and one success return: counting the gate from the caller is
|
||||
// the only shape that cannot miss one.
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::Draws, 1);
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::AccessorCalls, 1);
|
||||
}
|
||||
if (TrySetupDrawFastPath(frame, mode, aspects, drawParams, pIndexBufferView)) {
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::CountGate(MG_Util::PipeStats::Gate::MagmaDrawFastPath, /*hit=*/true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (MG_Util::PipeStats::Enabled()) {
|
||||
MG_Util::PipeStats::CountGate(MG_Util::PipeStats::Gate::MagmaDrawFastPath, /*hit=*/false);
|
||||
// The three reads the full path makes immediately below (draw FBO, VAO,
|
||||
// program). The accessor reads the declined fast path had already made before
|
||||
// it turned back are NOT counted.
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::AccessorCalls, 3);
|
||||
}
|
||||
const auto& drawFbo =
|
||||
MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
|
||||
if (drawFbo != nullptr && IsUnsupportedFramebufferForDirectVulkan(*drawFbo)) {
|
||||
|
||||
Reference in New Issue
Block a user