mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Perf] (MG_Backend): stop DirectVulkan re-proving sampler sets and re-walking render passes
Two per-draw costs from the round-10 profiles. A per-program sampled-set epoch inside UniformManager skips the per-binding descriptor proof walk when no texture or sampler API ran since that program's previous draw - the mc_sampler_churn/mc_tex_param pattern. The pass-switch path stops re-deriving render-pass state that its own value hash already pins. Load-gated 6-round order-alternating A/B (medians): magma tex_param -13.3%, pass_switch -10.9%, state_toggle -5.4%; espryt untouched and unmoved. The two matrix flags (sodium +7.5%, tex_stream +6.2%) reversed under 10-pair isolated alternating re-runs (-5.5% and +2.5%) - the same position-bias artifact every previous round's flags showed. Unit tests 421/421; retrace subset and the 52-entry integration suite pass. Landing note: this diff was authored by a round-10 agent whose session died before adjudication; the A/B data survived (r10bmag_ab_raw.csv) and the flags were adjudicated before landing. Its relink also exposed the pre-existing exit-teardown SIGSEGV fixed in the previous commit.
This commit is contained in:
@@ -116,6 +116,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_frameCount = frameCount;
|
m_frameCount = frameCount;
|
||||||
m_maxBindings = maxBindings;
|
m_maxBindings = maxBindings;
|
||||||
m_samplerResolveMemo.assign(m_maxBindings, SamplerResolveMemo{});
|
m_samplerResolveMemo.assign(m_maxBindings, SamplerResolveMemo{});
|
||||||
|
// Every entry is freshly constructed (all-invalid), so nothing needs sweeping until
|
||||||
|
// a resolve writes one.
|
||||||
|
m_samplerResolveMemoHighWater = 0;
|
||||||
m_setsPerFrame = setsPerFrame;
|
m_setsPerFrame = setsPerFrame;
|
||||||
m_peakDescriptorSetsObserved = 0;
|
m_peakDescriptorSetsObserved = 0;
|
||||||
m_textureManager = textureManager;
|
m_textureManager = textureManager;
|
||||||
@@ -174,6 +177,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_minDynamicOffsetAlignment = 1;
|
m_minDynamicOffsetAlignment = 1;
|
||||||
m_frameCount = 0;
|
m_frameCount = 0;
|
||||||
m_maxBindings = 0;
|
m_maxBindings = 0;
|
||||||
|
m_samplerResolveMemo.clear();
|
||||||
|
m_samplerResolveMemoHighWater = 0;
|
||||||
m_setsPerFrame = 0;
|
m_setsPerFrame = 0;
|
||||||
m_peakDescriptorSetsObserved = 0;
|
m_peakDescriptorSetsObserved = 0;
|
||||||
m_textureManager = nullptr;
|
m_textureManager = nullptr;
|
||||||
@@ -210,10 +215,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_fastRebindMemo.valid = false;
|
m_fastRebindMemo.valid = false;
|
||||||
m_lastBindValid = false;
|
m_lastBindValid = false;
|
||||||
// Re-fingerprint the bound sampler set fresh this frame so any GL object address
|
// Re-fingerprint the bound sampler set fresh this frame so any GL object address
|
||||||
// reuse cannot outlive a single frame (see SamplerResolveMemo).
|
// reuse cannot outlive a single frame (see SamplerResolveMemo). Only the entries a
|
||||||
for (auto& memo : m_samplerResolveMemo) {
|
// resolve has actually written can be valid, so the high-water mark bounds the
|
||||||
memo.valid = false;
|
// sweep - the vector itself is sized to the device's binding cap (256 here), which
|
||||||
memo.infoValid = false;
|
// is ~30x more entries than any program declares.
|
||||||
|
const Uint32 touchedBindings =
|
||||||
|
std::min<Uint32>(m_samplerResolveMemoHighWater, static_cast<Uint32>(m_samplerResolveMemo.size()));
|
||||||
|
for (Uint32 binding = 0; binding < touchedBindings; ++binding) {
|
||||||
|
m_samplerResolveMemo[binding].valid = false;
|
||||||
|
m_samplerResolveMemo[binding].infoValid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,6 +389,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
viewFormatMemo->viewFormatDomain = numericDomain;
|
viewFormatMemo->viewFormatDomain = numericDomain;
|
||||||
viewFormatMemo->viewFormat = sampledViewFormat;
|
viewFormatMemo->viewFormat = sampledViewFormat;
|
||||||
viewFormatMemo->viewFormatValid = true;
|
viewFormatMemo->viewFormatValid = true;
|
||||||
|
NoteSamplerResolveMemoTouched(binding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sampledViewFormat == VK_FORMAT_UNDEFINED) {
|
if (sampledViewFormat == VK_FORMAT_UNDEFINED) {
|
||||||
@@ -434,6 +445,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
memo.viewLevelCount = viewLevelCount;
|
memo.viewLevelCount = viewLevelCount;
|
||||||
memo.sampler = resolvedSampler;
|
memo.sampler = resolvedSampler;
|
||||||
memo.valid = true;
|
memo.valid = true;
|
||||||
|
NoteSamplerResolveMemoTouched(binding);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resolvedSampler = m_samplerManager->GetOrCreateSampler(*samplerToUse, *texture, forceNearestFiltering,
|
resolvedSampler = m_samplerManager->GetOrCreateSampler(*samplerToUse, *texture, forceNearestFiltering,
|
||||||
@@ -450,6 +462,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
if (binding < m_samplerResolveMemo.size()) {
|
if (binding < m_samplerResolveMemo.size()) {
|
||||||
m_samplerResolveMemo[binding].info = outImageInfo;
|
m_samplerResolveMemo[binding].info = outImageInfo;
|
||||||
m_samplerResolveMemo[binding].infoValid = true;
|
m_samplerResolveMemo[binding].infoValid = true;
|
||||||
|
NoteSamplerResolveMemoTouched(binding);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -345,5 +345,19 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Bool infoValid = false;
|
Bool infoValid = false;
|
||||||
};
|
};
|
||||||
mutable Vector<SamplerResolveMemo> m_samplerResolveMemo;
|
mutable Vector<SamplerResolveMemo> m_samplerResolveMemo;
|
||||||
|
// Exclusive upper bound on the entries of m_samplerResolveMemo that any resolve
|
||||||
|
// has ever written. The vector is sized to the DEVICE binding cap (256 on desktop
|
||||||
|
// NVIDIA), but a program declares 1-8 bindings, so the per-frame reset below was
|
||||||
|
// memsetting ~22 KB of never-touched entries every frame - a measurable slice of
|
||||||
|
// the per-frame fixed cost on draw-light frames. Every site that can turn any of
|
||||||
|
// an entry's *Valid flags on raises this mark first, so entries at or above it are
|
||||||
|
// provably still in their constructed (all-invalid) state and clearing them is a
|
||||||
|
// no-op. Never lowered except by Initialize/Shutdown, which rebuild the vector.
|
||||||
|
mutable Uint32 m_samplerResolveMemoHighWater = 0;
|
||||||
|
void NoteSamplerResolveMemoTouched(Uint32 binding) const {
|
||||||
|
if (binding >= m_samplerResolveMemoHighWater) {
|
||||||
|
m_samplerResolveMemoHighWater = binding + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||||
|
|||||||
@@ -273,12 +273,76 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
// re-derive the exact values already applied on this command buffer. The
|
// re-derive the exact values already applied on this command buffer. The
|
||||||
// remaining input, the swapchain pre-transform, cannot change mid-recording
|
// remaining input, the swapchain pre-transform, cannot change mid-recording
|
||||||
// (a swapchain recreate retires the command buffer, and recording begin resets
|
// (a swapchain recreate retires the command buffer, and recording begin resets
|
||||||
// this whole shadow).
|
// this whole shadow); the value key below pins it anyway.
|
||||||
Bool dynamicTailValid = false;
|
Bool dynamicTailValid = false;
|
||||||
Uint dynamicTailParamsVersion = 0;
|
Uint dynamicTailParamsVersion = 0;
|
||||||
Int dynamicTailExtentX = 0;
|
Int dynamicTailExtentX = 0;
|
||||||
Int dynamicTailExtentY = 0;
|
Int dynamicTailExtentY = 0;
|
||||||
Bool dynamicTailIsDefaultFbo = false;
|
Bool dynamicTailIsDefaultFbo = false;
|
||||||
|
// VALUE key over the tail's inputs, as a second-level gate behind the version.
|
||||||
|
// The parameters version is ONE counter for all of RenderState, so anything that
|
||||||
|
// is not tail input - a GL_BLEND toggle, a glBlendFuncSeparate, a glColorMask -
|
||||||
|
// moves it and forced a full tail re-run. Blaze3D toggles blend around every
|
||||||
|
// batch, so that was a per-draw re-derivation of six dynamic states that could
|
||||||
|
// not have changed. Equal key => the six Apply* below would each re-derive the
|
||||||
|
// value their shadow already holds and emit nothing, so the tail is skippable.
|
||||||
|
//
|
||||||
|
// Complete input inventory of ApplyDynamicDrawStateTail, one line per reader
|
||||||
|
// (each accessor it replaces is a verified plain field read of the same
|
||||||
|
// RenderStateParameters field - RenderState.cpp):
|
||||||
|
// ApplyGLViewportState : Viewport, DepthRange, + extent/isDefaultFbo/preTransform
|
||||||
|
// ApplyBlendConstants : BlendColor
|
||||||
|
// ApplyPolygonOffsetState : PolygonOffsetUnits, PolygonOffsetFactor
|
||||||
|
// ApplyLineWidthState : LineWidth (see the caveat below)
|
||||||
|
// ApplyStencilState : StencilStates[0..1].{ValueMask, WriteMask, Ref}
|
||||||
|
// scissor rect : ScissorTestEnabled, ScissorBox,
|
||||||
|
// + extent/isDefaultFbo/preTransform
|
||||||
|
// Caveat, unchanged from the version-only gate: ApplyLineWidthState also clamps
|
||||||
|
// to the ACTIVE BACKEND OBJECT's aliased line-width range. Those are device
|
||||||
|
// limits queried once at backend init and constant for the renderer's lifetime,
|
||||||
|
// so they are not part of the key (the version gate never covered them either).
|
||||||
|
struct DynamicTailKey {
|
||||||
|
Int viewport[4] = {0, 0, 0, 0};
|
||||||
|
Float depthRange[2] = {0.0f, 0.0f};
|
||||||
|
Float blendColor[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
|
Float polygonOffsetFactor = 0.0f;
|
||||||
|
Float polygonOffsetUnits = 0.0f;
|
||||||
|
Float lineWidth = 0.0f;
|
||||||
|
Uint32 stencilValueMask[2] = {0, 0};
|
||||||
|
Uint32 stencilWriteMask[2] = {0, 0};
|
||||||
|
Int stencilRef[2] = {0, 0};
|
||||||
|
Int scissorBox[4] = {0, 0, 0, 0};
|
||||||
|
Int extentX = 0;
|
||||||
|
Int extentY = 0;
|
||||||
|
Uint32 preTransform = 0;
|
||||||
|
Bool scissorEnabled = false;
|
||||||
|
Bool isDefaultFbo = false;
|
||||||
|
|
||||||
|
Bool operator==(const DynamicTailKey& other) const {
|
||||||
|
// NaN in any float input makes this false, which only costs a redundant
|
||||||
|
// tail run - never a skipped one.
|
||||||
|
for (Uint32 i = 0; i < 4; ++i) {
|
||||||
|
if (viewport[i] != other.viewport[i] || blendColor[i] != other.blendColor[i] ||
|
||||||
|
scissorBox[i] != other.scissorBox[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Uint32 i = 0; i < 2; ++i) {
|
||||||
|
if (depthRange[i] != other.depthRange[i] ||
|
||||||
|
stencilValueMask[i] != other.stencilValueMask[i] ||
|
||||||
|
stencilWriteMask[i] != other.stencilWriteMask[i] ||
|
||||||
|
stencilRef[i] != other.stencilRef[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return polygonOffsetFactor == other.polygonOffsetFactor &&
|
||||||
|
polygonOffsetUnits == other.polygonOffsetUnits && lineWidth == other.lineWidth &&
|
||||||
|
extentX == other.extentX && extentY == other.extentY &&
|
||||||
|
preTransform == other.preTransform && scissorEnabled == other.scissorEnabled &&
|
||||||
|
isDefaultFbo == other.isDefaultFbo;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
DynamicTailKey dynamicTailKey{};
|
||||||
};
|
};
|
||||||
static DynamicStateShadow g_dynamicStateShadow;
|
static DynamicStateShadow g_dynamicStateShadow;
|
||||||
|
|
||||||
@@ -5049,17 +5113,60 @@ void main() {
|
|||||||
shadow.dynamicTailIsDefaultFbo == isDefaultFbo) {
|
shadow.dynamicTailIsDefaultFbo == isDefaultFbo) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ApplyGLViewportState(frame.commandBuffer, extent, m_swapchainObject.GetPreTransform(), isDefaultFbo);
|
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
|
||||||
|
// exactly the tail's inputs (inventory in DynamicTailKey) out of one bulk
|
||||||
|
// parameters fetch and compare; an equal key means every Apply* below would
|
||||||
|
// re-derive the value its shadow already holds.
|
||||||
|
DynamicStateShadow::DynamicTailKey key;
|
||||||
|
{
|
||||||
|
const RenderStateParameters& p = MG_State::pGLContext->GetRenderStateParameters();
|
||||||
|
key.viewport[0] = p.Viewport.x();
|
||||||
|
key.viewport[1] = p.Viewport.y();
|
||||||
|
key.viewport[2] = p.Viewport.z();
|
||||||
|
key.viewport[3] = p.Viewport.w();
|
||||||
|
key.depthRange[0] = p.DepthRange.x();
|
||||||
|
key.depthRange[1] = p.DepthRange.y();
|
||||||
|
key.blendColor[0] = p.BlendColor.x();
|
||||||
|
key.blendColor[1] = p.BlendColor.y();
|
||||||
|
key.blendColor[2] = p.BlendColor.z();
|
||||||
|
key.blendColor[3] = p.BlendColor.w();
|
||||||
|
key.polygonOffsetFactor = p.PolygonOffsetFactor;
|
||||||
|
key.polygonOffsetUnits = p.PolygonOffsetUnits;
|
||||||
|
key.lineWidth = p.LineWidth;
|
||||||
|
// StencilStates[0] is Front, [1] is Back (RenderState::GetStencilFaceIndex),
|
||||||
|
// the same order ApplyStencilState reads them in.
|
||||||
|
for (Uint32 face = 0; face < 2; ++face) {
|
||||||
|
key.stencilValueMask[face] = p.StencilStates[face].ValueMask;
|
||||||
|
key.stencilWriteMask[face] = p.StencilStates[face].WriteMask;
|
||||||
|
key.stencilRef[face] = p.StencilStates[face].Ref;
|
||||||
|
}
|
||||||
|
key.scissorEnabled = p.ScissorTestEnabled;
|
||||||
|
key.scissorBox[0] = p.ScissorBox.x();
|
||||||
|
key.scissorBox[1] = p.ScissorBox.y();
|
||||||
|
key.scissorBox[2] = p.ScissorBox.z();
|
||||||
|
key.scissorBox[3] = p.ScissorBox.w();
|
||||||
|
key.extentX = extent.x();
|
||||||
|
key.extentY = extent.y();
|
||||||
|
key.preTransform = static_cast<Uint32>(preTransform);
|
||||||
|
key.isDefaultFbo = isDefaultFbo;
|
||||||
|
}
|
||||||
|
if (shadow.dynamicTailValid && shadow.dynamicTailKey == key) {
|
||||||
|
// Re-arm the cheap version gate so an unchanged-parameters run of draws after
|
||||||
|
// this one costs the four-integer compare again.
|
||||||
|
shadow.dynamicTailParamsVersion = paramsVersion;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ApplyGLViewportState(frame.commandBuffer, extent, preTransform, isDefaultFbo);
|
||||||
ApplyBlendConstants(frame.commandBuffer);
|
ApplyBlendConstants(frame.commandBuffer);
|
||||||
ApplyPolygonOffsetState(frame.commandBuffer);
|
ApplyPolygonOffsetState(frame.commandBuffer);
|
||||||
ApplyLineWidthState(frame.commandBuffer);
|
ApplyLineWidthState(frame.commandBuffer);
|
||||||
ApplyStencilState(frame.commandBuffer);
|
ApplyStencilState(frame.commandBuffer);
|
||||||
const Bool scissorEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ScissorTest);
|
|
||||||
VkRect2D scissor{};
|
VkRect2D scissor{};
|
||||||
if (scissorEnabled) {
|
if (key.scissorEnabled) {
|
||||||
const auto& scissorBox = MG_State::pGLContext->GetScissorBox();
|
const IntVec4 scissorBox(key.scissorBox[0], key.scissorBox[1], key.scissorBox[2], key.scissorBox[3]);
|
||||||
scissor = isDefaultFbo
|
scissor = isDefaultFbo ? MakeDefaultFramebufferScissorRect(scissorBox, extent, preTransform)
|
||||||
? MakeDefaultFramebufferScissorRect(scissorBox, extent, m_swapchainObject.GetPreTransform())
|
|
||||||
: MakeClampedScissorRect(scissorBox, extent);
|
: MakeClampedScissorRect(scissorBox, extent);
|
||||||
} else {
|
} else {
|
||||||
scissor.offset = {0, 0};
|
scissor.offset = {0, 0};
|
||||||
@@ -5071,18 +5178,28 @@ void main() {
|
|||||||
shadow.dynamicTailExtentX = extent.x();
|
shadow.dynamicTailExtentX = extent.x();
|
||||||
shadow.dynamicTailExtentY = extent.y();
|
shadow.dynamicTailExtentY = extent.y();
|
||||||
shadow.dynamicTailIsDefaultFbo = isDefaultFbo;
|
shadow.dynamicTailIsDefaultFbo = isDefaultFbo;
|
||||||
|
shadow.dynamicTailKey = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint32 VulkanRenderer::GetBaseTransformFlagsRaw() {
|
Uint32 VulkanRenderer::GetBaseTransformFlagsRaw(Bool isDefaultFbo) {
|
||||||
// GetShaderTransformFlags is a function of the pre-transform AND of whether
|
// GetShaderTransformFlags is a function of the pre-transform AND of whether
|
||||||
// the bound draw framebuffer is the default one (the Y-flip/rotation bits
|
// the bound draw framebuffer is the default one (the Y-flip/rotation bits
|
||||||
// apply only when presenting). Memo keyed on both; keying on the
|
// apply only when presenting). Memo keyed on both; keying on the
|
||||||
// pre-transform alone served an FBO pass's unflipped flags to the following
|
// pre-transform alone served an FBO pass's unflipped flags to the following
|
||||||
// default-framebuffer pass and flipped the whole frame.
|
// default-framebuffer pass and flipped the whole frame.
|
||||||
const VkSurfaceTransformFlagBitsKHR preTransform = m_swapchainObject.GetPreTransform();
|
// isDefaultFbo is supplied by the caller: every draw-path caller has already
|
||||||
const auto& currentDrawFBO =
|
// resolved the bound draw framebuffer (and its default-ness) for its own
|
||||||
|
// guards, and re-walking the binding slot + the virtual IsDefaultFramebuffer
|
||||||
|
// per draw showed up in the profile. Callers MUST pass the value derived from
|
||||||
|
// the SAME draw-framebuffer binding the draw uses - see the assert below.
|
||||||
|
MOBILEGL_ASSERT(
|
||||||
|
[&] {
|
||||||
|
const auto& fbo =
|
||||||
MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
|
MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
|
||||||
const Bool isDefaultFbo = currentDrawFBO != nullptr && currentDrawFBO->IsDefaultFramebuffer();
|
return isDefaultFbo == (fbo != nullptr && fbo->IsDefaultFramebuffer());
|
||||||
|
}(),
|
||||||
|
"GetBaseTransformFlagsRaw: isDefaultFbo does not match the bound draw framebuffer");
|
||||||
|
const VkSurfaceTransformFlagBitsKHR preTransform = m_swapchainObject.GetPreTransform();
|
||||||
if (!m_baseTransformFlagsKeyValid || preTransform != m_baseTransformFlagsPreTransform ||
|
if (!m_baseTransformFlagsKeyValid || preTransform != m_baseTransformFlagsPreTransform ||
|
||||||
isDefaultFbo != m_baseTransformFlagsIsDefaultFbo) {
|
isDefaultFbo != m_baseTransformFlagsIsDefaultFbo) {
|
||||||
m_baseTransformFlagsCache = GetShaderTransformFlags(preTransform).GetRaw();
|
m_baseTransformFlagsCache = GetShaderTransformFlags(preTransform).GetRaw();
|
||||||
@@ -5173,7 +5290,10 @@ void main() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (GetBaseTransformFlagsRaw() != snap.baseTransformFlags) {
|
// The FBO identity+version compare above proved this draw's framebuffer is the
|
||||||
|
// snapshotting draw's, so its default-ness is the snapshot's too - no second walk
|
||||||
|
// of the binding slot and no virtual IsDefaultFramebuffer call.
|
||||||
|
if (GetBaseTransformFlagsRaw(snap.drawFboIsDefault) != snap.baseTransformFlags) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (m_textureManager->GetResourceEraseEpoch() != snap.textureEraseEpoch ||
|
if (m_textureManager->GetResourceEraseEpoch() != snap.textureEraseEpoch ||
|
||||||
@@ -5476,8 +5596,9 @@ void main() {
|
|||||||
fillSnap->valid = false;
|
fillSnap->valid = false;
|
||||||
m_setupDrawSnapshotMru = fillIndex;
|
m_setupDrawSnapshotMru = fillIndex;
|
||||||
}
|
}
|
||||||
|
const Bool drawFboIsDefault = drawFbo != nullptr && drawFbo->IsDefaultFramebuffer();
|
||||||
ProgramFactory::CompileOptionFlags transformFlags =
|
ProgramFactory::CompileOptionFlags transformFlags =
|
||||||
ProgramFactory::CompileOptionFlags(GetBaseTransformFlagsRaw());
|
ProgramFactory::CompileOptionFlags(GetBaseTransformFlagsRaw(drawFboIsDefault));
|
||||||
// Captured draws take the xfb-decorated program variant.
|
// Captured draws take the xfb-decorated program variant.
|
||||||
if (m_transformFeedbackFeatureEnabled && MG_State::pGLContext->IsTransformFeedbackActive() &&
|
if (m_transformFeedbackFeatureEnabled && MG_State::pGLContext->IsTransformFeedbackActive() &&
|
||||||
program.GetTransformFeedbackVaryingCount() > 0) {
|
program.GetTransformFeedbackVaryingCount() > 0) {
|
||||||
@@ -5813,10 +5934,10 @@ void main() {
|
|||||||
snap.vaoConfigVersion = vao.GetConfigVersion();
|
snap.vaoConfigVersion = vao.GetConfigVersion();
|
||||||
snap.drawFbo = drawFbo.get();
|
snap.drawFbo = drawFbo.get();
|
||||||
snap.fboVersion = drawFbo->GetObjectVersion();
|
snap.fboVersion = drawFbo->GetObjectVersion();
|
||||||
snap.drawFboIsDefault = drawFbo->IsDefaultFramebuffer();
|
snap.drawFboIsDefault = drawFboIsDefault;
|
||||||
snap.renderStateVersion = MG_State::pGLContext->GetPipelineStateVersion();
|
snap.renderStateVersion = MG_State::pGLContext->GetPipelineStateVersion();
|
||||||
snap.bindGeneration = MG_State::pGLContext->GetTextureBindGeneration();
|
snap.bindGeneration = MG_State::pGLContext->GetTextureBindGeneration();
|
||||||
snap.baseTransformFlags = GetBaseTransformFlagsRaw();
|
snap.baseTransformFlags = GetBaseTransformFlagsRaw(drawFboIsDefault);
|
||||||
snap.resolvedTransformFlags = transformFlags.GetRaw();
|
snap.resolvedTransformFlags = transformFlags.GetRaw();
|
||||||
snap.renderPassHash = nowActiveRenderPass->hash;
|
snap.renderPassHash = nowActiveRenderPass->hash;
|
||||||
snap.imageIndex = m_imageIndexAcquired;
|
snap.imageIndex = m_imageIndexAcquired;
|
||||||
|
|||||||
@@ -659,7 +659,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Bool m_baseTransformFlagsIsDefaultFbo = false;
|
Bool m_baseTransformFlagsIsDefaultFbo = false;
|
||||||
Bool m_baseTransformFlagsKeyValid = false;
|
Bool m_baseTransformFlagsKeyValid = false;
|
||||||
Uint32 m_baseTransformFlagsCache = 0;
|
Uint32 m_baseTransformFlagsCache = 0;
|
||||||
Uint32 GetBaseTransformFlagsRaw();
|
// isDefaultFbo must be the default-ness of the CURRENTLY bound draw framebuffer;
|
||||||
|
// every caller already has it in hand from its own guards.
|
||||||
|
Uint32 GetBaseTransformFlagsRaw(Bool isDefaultFbo);
|
||||||
// Drops every memoized pipeline handle. Required at command-buffer
|
// Drops every memoized pipeline handle. Required at command-buffer
|
||||||
// boundaries and whenever any pipeline may have been destroyed. Also drops
|
// boundaries and whenever any pipeline may have been destroyed. Also drops
|
||||||
// the cached pipeline-state hash: the same boundaries can retire the GL
|
// the cached pipeline-state hash: the same boundaries can retire the GL
|
||||||
|
|||||||
Reference in New Issue
Block a user