[Refactor] (P13 W6f): Magma's pipeline build reads the draw framebuffer record only, and the frontend texture sync behind it goes; link ratchet 58 -> 49.

This commit is contained in:
2026-10-06 19:31:02 -04:00
parent 9f88379141
commit 922507f8f8
4 changed files with 14 additions and 1780 deletions
File diff suppressed because it is too large Load Diff
@@ -565,18 +565,6 @@ public:
Bool isTextureView = false;
};
// Records what a GL texture view needs of the image it views, so the next sync of the
// STORAGE texture creates (or recreates and copies forward) an image the view can be built
// over. See m_viewRequestedImageFlags for why this is lazy rather than unconditional.
void NoteTextureViewImageRequirements(MG_State::GLState::ITextureObject& viewTexture,
MG_State::GLState::ITextureObject& storageTexture);
VkImageCreateFlags GetViewRequestedImageFlags(const MG_State::GLState::ITextureObject& storageTexture) const;
// Appends every format a GL texture view reinterprets this storage as, for the narrowed
// VkImageFormatListCreateInfo the image is created with.
void AppendViewRequestedFormats(const MG_State::GLState::ITextureObject& storageTexture,
Vector<VkFormat>& outFormats) const;
TextureResource* SyncTextureAndGetDescriptor(
MG_State::GLState::ITextureObject& texture);
// P5f (fm): THE HANDLE-KEYED ARM of SyncTextureAndGetDescriptor. Under an active transport
// the apply thread may not name the client's ITextureObject (rule E), so the clear / blit /
// readback / mipmap verbs resolve their attachment textures from the wire handle the
@@ -710,33 +698,8 @@ private:
// stamp of 0 so a fresh resource counts as untouched.
Uint64 m_recordingGeneration = 1;
Bool SyncTexture(MG_State::GLState::ITextureObject &texture,
TextureResource &outResource);
Bool SyncTextureResource(const MG_State::GLState::ITextureObject &texture,
TextureUploadTarget uploadTarget,
const IntVec3 &texelSize, SizeT byteSize, Uint32 mipLevels,
TextureResource &resource);
Bool SyncTextureViews(const MG_State::GLState::ITextureObject& texture, TextureResource& resource);
VkImageView CreateImageView(VkImage image, VkFormat format, VkImageAspectFlags aspect,
VkImageViewType viewType, Uint32 baseMipLevel, Uint32 levelCount,
Uint32 baseArrayLayer,
Uint32 layerCount,
const VkComponentMapping* components = nullptr,
VkImageUsageFlags viewUsage = 0) const;
Bool UploadDirtyMipLevels(MG_State::GLState::TextureObjectMipmap &mipmapTexture,
TextureUploadTarget uploadTarget,
TextureResource &outResource);
static Bool CheckMipmapCompleteness(const MG_State::GLState::ITextureObject& texture,
TextureUploadTarget& outTarget,
IntVec3& outTexelSize,
SizeT& outByteSize,
Uint32& outMipLevelCount);
static Uint32 GetUploadMipLevelCount(const MG_State::GLState::TextureObjectMipmap& texture, TextureUploadTarget target);
static void ResolveViewMipRange(const MG_State::GLState::ITextureObject& texture, Uint32 mipLevels,
Uint32& outBaseMipLevel, Uint32& outLevelCount);
static VkImageAspectFlags GetAspectMaskForFormat(VkFormat format);
void DeferResourceRelease(TextureResource&& resource);
void DeferViewRelease(VkImageView view);
void CollectDeferredReleases(Uint32 frameIndex);
void DestroyDeferredReleases();
// Frees the fence/command buffer/staging buffer of every in-flight texture
@@ -193,25 +193,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
}
static VkColorComponentFlags GetSupportedColorWriteMaskForComponentCount(SizeT componentCount) {
switch (componentCount) {
case 1:
return VK_COLOR_COMPONENT_R_BIT;
case 2:
return VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT;
case 3:
return VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT;
case 4:
return VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
default:
MOBILEGL_ASSERT(false,
"GetSupportedColorWriteMaskForComponentCount: unsupported componentCount=%zu",
componentCount);
return 0;
}
}
static Bool IsQuarterTurnPreTransform(VkSurfaceTransformFlagBitsKHR preTransform) {
return preTransform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
preTransform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR;
@@ -1128,11 +1109,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
static constexpr Uint32 kDescriptorSetsPerFrame = 64;
static Bool IsCubeMapFaceUploadTarget(TextureUploadTarget target) {
return target >= TextureUploadTarget::CubeMapPositiveX &&
target <= TextureUploadTarget::CubeMapNegativeZ;
}
enum class BlitSurfaceTransform : Uint32 {
Identity = 0,
Rotate90 = 1,
@@ -3239,34 +3215,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MOBILEGL_ASSERT(payload.colorAttachmentCount <= PipelineFactory::PipelineCreatePayload::kMaxColorAttachments,
"GetOrCreatePipeline: colorAttachmentCount=%u exceeds payload capacity",
payload.colorAttachmentCount);
// The draw framebuffer is the applier's record on every arm (P13 W6): a draw with none
// has nothing to build a pipeline for, and is declined by name rather than guessed at.
const auto* wireFbo = MG_Pipe::MGPipeApplier().DrawFramebuffer();
const auto drawFboBinding = wireFbo ? SharedPtr<MG_State::GLState::FramebufferObject>{} :
MG_Pipe::gPipeInputs.GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
MOBILEGL_ASSERT(wireFbo != nullptr || drawFboBinding != nullptr, "GetOrCreatePipeline: draw framebuffer is null");
const Bool isDefaultDrawFbo = wireFbo ? wireFbo->IsDefault : drawFboBinding->IsDefaultFramebuffer();
Array<FramebufferAttachmentType, MG_Pipe::kMGPipeMaxColorAttachments> wireDrawBuffers{};
if (wireFbo) for (SizeT i = 0; i < wireDrawBuffers.size(); ++i)
wireDrawBuffers[i] = wireFbo->DrawBuffers[i] < 0 ? FramebufferAttachmentType::None :
if (wireFbo == nullptr) {
MGLOG_E_ONCE("GetOrCreatePipeline skipped: no draw framebuffer record is bound");
return VK_NULL_HANDLE;
}
const Bool isDefaultDrawFbo = wireFbo->IsDefault;
Array<FramebufferAttachmentType, MG_Pipe::kMGPipeMaxColorAttachments> drawBuffers{};
for (SizeT i = 0; i < drawBuffers.size(); ++i)
drawBuffers[i] = wireFbo->DrawBuffers[i] < 0 ? FramebufferAttachmentType::None :
static_cast<FramebufferAttachmentType>(static_cast<Int>(FramebufferAttachmentType::Color0) + wireFbo->DrawBuffers[i]);
const auto& drawBuffers = wireFbo ? wireDrawBuffers : drawFboBinding->GetDrawBuffers();
auto resolveCompleteColorAttachmentTexture = [&](Uint32 drawBufferIndex) -> MG_State::GLState::ITextureObject* {
if (wireFbo) return nullptr;
if (isDefaultDrawFbo || drawBufferIndex >= drawBuffers.size()) {
return nullptr;
}
const auto drawBuffer = drawBuffers[drawBufferIndex];
if (drawBuffer == FramebufferAttachmentType::None) {
return nullptr;
}
const auto& attachment = drawFboBinding->GetAttachment(drawBuffer);
if (!attachment.IsTexture() || !attachment.IsComplete()) {
return nullptr;
}
return attachment.GetTexture().get();
};
for (Uint32 i = 0; i < payload.colorAttachmentCount; ++i) {
BlendFactor srcRGB = BlendFactor::One;
BlendFactor dstRGB = BlendFactor::Zero;
@@ -3288,8 +3248,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
(bufferMask.b() ? VK_COLOR_COMPONENT_B_BIT : 0u) |
(bufferMask.a() ? VK_COLOR_COMPONENT_A_BIT : 0u));
Bool effectiveBlendEnabled = blendEnabled;
MG_State::GLState::ITextureObject* colorAttachmentTexture = nullptr;
MG_State::GLState::RenderbufferObject* colorAttachmentRenderbuffer = nullptr;
if (isDefaultDrawFbo && i < drawBuffers.size() &&
drawBuffers[i] == FramebufferAttachmentType::None) {
// The default framebuffer spans the same MAX_DRAW_BUFFERS slots as an FBO
@@ -3301,100 +3259,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
attachmentColorWriteMask = 0;
effectiveBlendEnabled = false;
}
if (wireFbo && !isDefaultDrawFbo) {
if (!isDefaultDrawFbo) {
const Int32 slot = wireFbo->DrawBuffers[i];
if (slot < 0 || wireFbo->Color[slot].Kind == MG_Pipe::kMGPipeSurfaceKindNone) {
attachmentColorWriteMask = 0;
effectiveBlendEnabled = false;
}
} else
if (!isDefaultDrawFbo && i < drawBuffers.size()) {
const auto drawBuffer = drawBuffers[i];
colorAttachmentTexture = resolveCompleteColorAttachmentTexture(i);
if (colorAttachmentTexture == nullptr && drawBuffer != FramebufferAttachmentType::None) {
const auto& attachment = drawFboBinding->GetAttachment(drawBuffer);
if (attachment.IsRenderbuffer() && attachment.IsComplete()) {
colorAttachmentRenderbuffer = attachment.GetRenderbuffer().get();
}
}
if (drawBuffer == FramebufferAttachmentType::None ||
(colorAttachmentTexture == nullptr && colorAttachmentRenderbuffer == nullptr)) {
// GL ignores writes and per-target blend state for GL_NONE draw buffer slots.
// Depth-only or otherwise unattached draw buffers should also discard color writes.
attachmentColorWriteMask = 0;
effectiveBlendEnabled = false;
}
if (colorAttachmentRenderbuffer != nullptr) {
const SizeT componentCount = MG_Util::GetBaseInternalFormatComponentCount(
colorAttachmentRenderbuffer->GetInternalFormat());
attachmentColorWriteMask &= GetSupportedColorWriteMaskForComponentCount(componentCount);
}
if (colorAttachmentTexture != nullptr) {
auto* texture = colorAttachmentTexture;
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
const auto* textureResource = m_textureManager->SyncTextureAndGetDescriptor(*texture);
MOBILEGL_ASSERT(textureResource != nullptr,
"GetOrCreatePipeline: failed to sync color attachment textureId=%d",
texture->GetExternalIndex());
VkFormatProperties attachmentFormatProperties{};
vkGetPhysicalDeviceFormatProperties(
m_physicalDevice.handle,
textureResource->format,
&attachmentFormatProperties);
MOBILEGL_ASSERT(
(attachmentFormatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) != 0,
"GetOrCreatePipeline: color attachment %u format=%d textureId=%d lacks VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT (program=%u)",
i,
static_cast<Int>(textureResource->format),
texture->GetExternalIndex(),
program.GetExternalIndex());
#endif
const SizeT componentCount = MG_Util::GetBaseInternalFormatComponentCount(texture->GetFormat());
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
const NumericDomain attachmentNumericDomain =
GetNumericDomainForTextureInternalFormat(texture->GetFormat());
for (Uint32 outputLocation = 0;
outputLocation < ProgramFactory::VkProgramObject::kMaxVertexInputLocations;
++outputLocation) {
if ((programObj.activeFragmentOutputLocationMask & (1u << outputLocation)) == 0 ||
outputLocation != i) {
continue;
}
const GLenum fragmentOutputType = programObj.fragmentOutputTypes[outputLocation];
const NumericDomain fragmentOutputDomain =
GetNumericDomainForShaderValueType(fragmentOutputType);
// GL allows fragment outputs with more components than the bound color attachment;
// excess components are discarded during conversion to the attachment format.
MOBILEGL_ASSERT(
attachmentNumericDomain == NumericDomain::Unknown ||
fragmentOutputDomain == NumericDomain::Unknown ||
attachmentNumericDomain == fragmentOutputDomain,
"GetOrCreatePipeline: fragment output location=%d type=%u mismatches color attachment %u internalFormat=%d textureId=%d program=%u",
static_cast<Int>(outputLocation),
static_cast<Uint32>(fragmentOutputType),
i,
static_cast<Int>(texture->GetFormat()),
texture->GetExternalIndex(),
program.GetExternalIndex());
}
#endif
const VkColorComponentFlags supportedColorWriteMask =
GetSupportedColorWriteMaskForComponentCount(componentCount);
if ((attachmentColorWriteMask & ~supportedColorWriteMask) != 0) {
MGLOG_W_ONCE(
"GetOrCreatePipeline: clamping colorWriteMask=0x%x to 0x%x on color attachment %u (componentCount=%zu textureId=%d internalFormat=%d program=%u blendEnabled=%d)",
static_cast<Uint32>(attachmentColorWriteMask),
static_cast<Uint32>(attachmentColorWriteMask & supportedColorWriteMask),
i,
componentCount,
texture->GetExternalIndex(),
static_cast<Int>(texture->GetFormat()),
program.GetExternalIndex(),
effectiveBlendEnabled ? 1 : 0);
attachmentColorWriteMask &= supportedColorWriteMask;
}
}
}
if (effectiveBlendEnabled) {
MOBILEGL_ASSERT(i < drawBuffers.size(),
@@ -3403,40 +3273,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkFormat colorAttachmentFormat = VK_FORMAT_UNDEFINED;
Int textureExternalIndex = -1;
if (wireFbo && !isDefaultDrawFbo) {
if (!isDefaultDrawFbo) {
const Int32 slot = wireFbo->DrawBuffers[i];
colorAttachmentFormat = ResolveWireImage(*wireFbo, wireFbo->Color[slot], VK_IMAGE_ASPECT_COLOR_BIT).format;
} else
if (isDefaultDrawFbo) {
colorAttachmentFormat = m_swapchainObject.GetSurfaceFormat().format;
} else if (colorAttachmentRenderbuffer != nullptr) {
textureExternalIndex = static_cast<Int>(colorAttachmentRenderbuffer->GetExternalIndex());
// The SAME resolver GetOrCreateRenderbufferResource backs the image with, so the
// probe cannot ask about a format the attachment does not have. The strict 1:1
// converter is the wrong question here and answered VK_FORMAT_UNDEFINED for
// RGBA2/RGBA12/RGB10/RGB12/RGB16 and the packed 16-bit formats for RGBA4/RGB5_A1
// - and VkFormatProperties for UNDEFINED are all zero, so blending was
// force-disabled forever on attachments that blend perfectly well. Every
// three-channel colour renderbuffer was in that set too (R8G8B8_UNORM is rarely
// supported), which is the more ordinary shape.
//
// Resolved rather than looked up: GetOrCreateRenderbufferResource creates images
// and bumps epochs, which a pipeline-state query must not do as a side effect.
// A renderbuffer has no device-fallback step after the resolver (unlike the
// texture path's D24 -> D32 substitution), so the resolver IS its live format.
colorAttachmentFormat =
ResolveTextureFormatInfo(colorAttachmentRenderbuffer->GetInternalFormat()).format;
} else {
auto* texture = colorAttachmentTexture;
MOBILEGL_ASSERT(texture != nullptr,
"GetOrCreatePipeline: blend is enabled on draw buffer %u but no complete color attachment is bound",
i);
textureExternalIndex = texture->GetExternalIndex();
auto* textureResource = m_textureManager->SyncTextureAndGetDescriptor(*texture);
MOBILEGL_ASSERT(textureResource != nullptr,
"GetOrCreatePipeline: failed to sync blend color attachment textureId=%d",
texture->GetExternalIndex());
colorAttachmentFormat = textureResource->format;
colorAttachmentFormat = m_swapchainObject.GetSurfaceFormat().format;
}
// Blending on an attachment whose format lacks
+1 -10
View File
@@ -5,7 +5,7 @@
# base commit : <unknown>
# configure : -DMOBILEGL_BUILD_DISAGGREGATED=ON -DMOBILEGL_BUILD_DISAGGREGATED_INPROC=ON -DCMAKE_BUILD_TYPE=Release
# build dir : /home/swung/mgl-p13-split
# count : 58
# count : 49
#
# One demangled symbol per line. A trailing ` # note` is an annotation the parser
# ignores; `# P13` marks a symbol that cannot fall before P13 establishes module
@@ -21,7 +21,6 @@ MobileGL::MG_Pipe::MGPipeSlotAllocator::FindByLifetimeId(MobileGL::MG_Pipe::MGPi
MobileGL::MG_Pipe::MGPipeSlotAllocator::Free(MobileGL::MG_Pipe::MGPipeKind, MobileGL::MG_Pipe::MGPipeHandle) # P13
MobileGL::MG_Pipe::MGPipeSlots() # P13
MobileGL::MG_Pipe::PipeInputs::GetBufferBindingPointCount(MobileGL::BufferTarget) const
MobileGL::MG_Pipe::PipeInputs::GetTextureObject(unsigned int) # P13 monolith arm: MGB_CTX accessor / GL readback packer (CONTRACT-P7 §12)
MobileGL::MG_Pipe::PipeInputs::HasOpenTransformFeedbackSpan(unsigned long) const # P13 monolith arm: MGB_CTX accessor / GL readback packer (CONTRACT-P7 §12)
MobileGL::MG_Pipe::PipeInputs::InvalidateCompileEnv() # P13 monolith arm: MGB_CTX accessor / GL readback packer (CONTRACT-P7 §12)
MobileGL::MG_Pipe::PipeInputs::IsLive() const
@@ -48,18 +47,10 @@ MobileGL::MG_State::GLState::BufferObject::SyncPersistentMappedRange()
MobileGL::MG_State::GLState::BufferObject::WritebackFromBackend(MobileGL::DataPtr, unsigned long)
MobileGL::MG_State::GLState::ClassifyVertexAttribType(unsigned int)
MobileGL::MG_State::GLState::DecodeProgramArchive(unsigned char const*, unsigned long, MobileGL::MG_State::GLState::ProgramArchive&)
MobileGL::MG_State::GLState::FramebufferAttachmentObject::GetRenderbuffer() const
MobileGL::MG_State::GLState::FramebufferAttachmentObject::GetTexture() const
MobileGL::MG_State::GLState::FramebufferAttachmentObject::IsComplete() const # P13 monolith arm: reads an app FBO/RBO/VAO/buffer object (CONTRACT-P7 §12)
MobileGL::MG_State::GLState::FramebufferAttachmentObject::IsRenderbuffer() const
MobileGL::MG_State::GLState::FramebufferAttachmentObject::IsTexture() const
MobileGL::MG_State::GLState::FramebufferObject::GetAttachment(MobileGL::FramebufferAttachmentType) const
MobileGL::MG_State::GLState::FramebufferObject::GetDrawBuffers() const
MobileGL::MG_State::GLState::GetBufferBackendOps()
MobileGL::MG_State::GLState::ProgramArtifactsSchemaFingerprint()
MobileGL::MG_State::GLState::ProgramObject::JoinPendingLink() const
MobileGL::MG_State::GLState::RenderbufferObject::GetExternalIndex() const
MobileGL::MG_State::GLState::RenderbufferObject::GetInternalFormat() const
MobileGL::MG_State::GLState::SamplerObject::GetAllSamplerParameters() const
MobileGL::MG_State::GLState::SamplerObject::GetExternalIndex() const
MobileGL::MG_State::GLState::SamplerObject::GetLifetimeId() const