[MG_Backend, MG_IntegrationTest] (Disaggregated): P5f fm - apply bufferless draws and compute from server records

This commit is contained in:
2026-09-19 22:34:28 -04:00
parent a6223c6509
commit fec49a54d8
6 changed files with 294 additions and 13 deletions
+14 -1
View File
@@ -37,6 +37,15 @@
#include <regex>
namespace MobileGL::MG_Backend::DirectGLES {
#if MOBILEGL_BUILD_DISAGGREGATED
// read_pixels writes server reply scratch. The client refuses PACK_BUFFER before
// emission, so no frontend pack binding can affect this destination.
static const SharedPtr<MG_State::GLState::BufferObject>& SplitReadbackPackBuffer() {
static const SharedPtr<MG_State::GLState::BufferObject> none;
return none;
}
#endif
#if MOBILEGL_BUILD_DISAGGREGATED
const FormatCapabilityCache* ActiveBackendFormatCaps() {
// Under a live split session the SERVER's private backend is what owns the context on the
@@ -2350,7 +2359,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
static Bool StoreClientRows(SizeT dstPixelBytes, SizeT swapGroupSize, GLsizei width, GLsizei sliceHeight,
GLsizei sliceCount, void* pixels, Bool applyPackImageParams, FillRow&& fillRow) {
const auto& pixelPackBufferObject =
MGB_CTX->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
#if MOBILEGL_BUILD_DISAGGREGATED
MG_Config::Transport != MG_Config::TransportMode::Monolith
? SplitReadbackPackBuffer() :
#endif
MGB_CTX->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
// Destination layout is computed from the client-side PACK parameters; only the actual pixel
// rows are written so skip regions of the destination stay untouched.
@@ -3154,9 +3154,15 @@ void main() {
inline ProgramFactory::CompileOptionFlags GetShaderTransformFlags(VkSurfaceTransformFlagBitsKHR preTransform) {
ProgramFactory::CompileOptionFlags flags = ProgramFactory::CompileOptionBit::PositionZRemap;
const auto& currentDrawFBO =
MGB_CTX->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
if (currentDrawFBO != nullptr && currentDrawFBO->IsDefaultFramebuffer()) {
#if MOBILEGL_BUILD_DISAGGREGATED
const auto* wireFbo = MG_Config::Transport != MG_Config::TransportMode::Monolith ? MG_Pipe::MGPipeApplier().DrawFramebuffer() : nullptr;
const Bool isDefault = wireFbo ? wireFbo->IsDefault :
MGB_CTX->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject()->IsDefaultFramebuffer();
#else
const auto& currentDrawFBO = MGB_CTX->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
const Bool isDefault = currentDrawFBO != nullptr && currentDrawFBO->IsDefaultFramebuffer();
#endif
if (isDefault) {
flags |= ProgramFactory::CompileOptionBit::PositionYFlip;
// gl_FragCoord follows the same rule the default-framebuffer RECTANGLES follow
// (GetDefaultFramebufferRectMapping): flipped for identity/180, left alone under a
@@ -3420,6 +3426,9 @@ void main() {
if (m_device != VK_NULL_HANDLE) {
VK_VERIFY(vkDeviceWaitIdle(m_device));
}
#if MOBILEGL_BUILD_DISAGGREGATED
DestroyWireDrawPass();
#endif
OnSubmitsCompletedUpTo(m_submitCounter);
DestroySubmitFencePool();
@@ -5210,7 +5219,7 @@ void main() {
// A program that runs a geometry shader AND captures transform feedback. Both halves are
// link-time properties, so this is safe to fold into a pipeline keyed on the program hash.
static Bool ProgramCapturesXfbFromGeometryStage(const MG_State::GLState::ProgramObject& program) {
static Bool ProgramCapturesXfbFromGeometryStage(const MagmaProgramSource& program) {
if (program.GetTransformFeedbackVaryingCount() == 0) return false;
// Both halves are link-time properties, so both are asked of the LAST LINK. Reading the
// live attach list would let a glAttachShader that has not been linked in yet - which GL
@@ -5249,12 +5258,22 @@ void main() {
VkPipeline VulkanRenderer::GetOrCreatePipeline(
GLenum mode,
const MG_State::GLState::ProgramObject& program,
const MagmaProgramSource& program,
const ProgramFactory::VkProgramObject& programObj,
ProgramFactory::CompileOptionFlags transformFlags,
const MG_State::GLState::VertexArrayObject& vao,
const RenderPassEntry& renderPassEntry,
Bool primitiveRestartEnable) {
#if MOBILEGL_BUILD_DISAGGREGATED
return GetOrCreatePipelineWithInput(mode, program, programObj, transformFlags,
m_vertexInputStateFactory->GetOrCreateVertexInputState(vao), renderPassEntry, primitiveRestartEnable);
}
VkPipeline VulkanRenderer::GetOrCreatePipelineWithInput(GLenum mode, const MagmaProgramSource& program,
const ProgramFactory::VkProgramObject& programObj, ProgramFactory::CompileOptionFlags transformFlags,
const VertexInputStateFactory::BackendVertexInputState& vis, const RenderPassEntry& renderPassEntry,
Bool primitiveRestartEnable) {
#endif
Bool invertClockwise = transformFlags & ProgramFactory::CompileOptionBit::PositionYFlip;
if (programObj.stages.empty()) {
MGLOG_D("GetOrCreatePipeline skipped: program has no shader stages");
@@ -5272,7 +5291,9 @@ void main() {
// payload key on the resolved LAYOUT hash instead, so draws over identical
// layouts share one pipeline.
// The one-arg fetch rides the VAO's state-pointer memo (no hash, no map).
#if !MOBILEGL_BUILD_DISAGGREGATED
auto& vis = m_vertexInputStateFactory->GetOrCreateVertexInputState(vao);
#endif
const Uint64 vertexLayoutHash = vis.layoutHash;
const Uint64 renderPassHash = renderPassEntry.hash;
// The pipeline-relevant subset only: glViewport / glScissor / glBlendColor / glStencilMask
@@ -5506,6 +5527,16 @@ void main() {
// (stencil) test always passes and nothing is written - even when the bound
// image is a packed depth-stencil texture attached through only one half.
{
#if MOBILEGL_BUILD_DISAGGREGATED
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
const auto* fbo = MG_Pipe::MGPipeApplier().DrawFramebuffer();
if (fbo && !fbo->IsDefault) {
if (fbo->Depth.Kind == MG_Pipe::kMGPipeSurfaceKindNone) depthTestEnabled = false;
if (fbo->Stencil.Kind == MG_Pipe::kMGPipeSurfaceKindNone) stencilTestEnabled = false;
}
} else
#endif
{
const auto& gatingFbo =
MGB_CTX->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
if (gatingFbo != nullptr && !gatingFbo->IsDefaultFramebuffer()) {
@@ -5519,6 +5550,7 @@ void main() {
}
}
}
}
const StencilFaceState& frontStencil = MGB_CTX->GetStencilState(StencilFace::Front);
const StencilFaceState& backStencil = MGB_CTX->GetStencilState(StencilFace::Back);
const VkPolygonMode requestedPolygonMode =
@@ -5726,12 +5758,31 @@ void main() {
MOBILEGL_ASSERT(payload.colorAttachmentCount <= PipelineFactory::PipelineCreatePayload::kMaxColorAttachments,
"GetOrCreatePipeline: colorAttachmentCount=%u exceeds payload capacity",
payload.colorAttachmentCount);
#if MOBILEGL_BUILD_DISAGGREGATED
const auto* wireFbo = MG_Config::Transport != MG_Config::TransportMode::Monolith ?
MG_Pipe::MGPipeApplier().DrawFramebuffer() : nullptr;
const auto drawFboBinding = wireFbo ? SharedPtr<MG_State::GLState::FramebufferObject>{} :
MGB_CTX->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
#else
const auto& drawFboBinding =
MGB_CTX->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
#endif
MOBILEGL_ASSERT(drawFboBinding != nullptr, "GetOrCreatePipeline: draw framebuffer is null");
#if MOBILEGL_BUILD_DISAGGREGATED
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 :
static_cast<FramebufferAttachmentType>(static_cast<Int>(FramebufferAttachmentType::Color0) + wireFbo->DrawBuffers[i]);
const auto& drawBuffers = wireFbo ? wireDrawBuffers : drawFboBinding->GetDrawBuffers();
#else
const Bool isDefaultDrawFbo = drawFboBinding->IsDefaultFramebuffer();
const auto& drawBuffers = drawFboBinding->GetDrawBuffers();
#endif
auto resolveCompleteColorAttachmentTexture = [&](Uint32 drawBufferIndex) -> MG_State::GLState::ITextureObject* {
#if MOBILEGL_BUILD_DISAGGREGATED
if (wireFbo) return nullptr;
#endif
if (isDefaultDrawFbo || drawBufferIndex >= drawBuffers.size()) {
return nullptr;
}
@@ -5782,6 +5833,15 @@ void main() {
attachmentColorWriteMask = 0;
effectiveBlendEnabled = false;
}
#if MOBILEGL_BUILD_DISAGGREGATED
if (wireFbo && !isDefaultDrawFbo) {
const Int32 slot = wireFbo->DrawBuffers[i];
if (slot < 0 || wireFbo->Color[slot].Kind == MG_Pipe::kMGPipeSurfaceKindNone) {
attachmentColorWriteMask = 0;
effectiveBlendEnabled = false;
}
} else
#endif
if (!isDefaultDrawFbo && i < drawBuffers.size()) {
const auto drawBuffer = drawBuffers[i];
colorAttachmentTexture = resolveCompleteColorAttachmentTexture(i);
@@ -5877,6 +5937,12 @@ void main() {
VkFormat colorAttachmentFormat = VK_FORMAT_UNDEFINED;
Int textureExternalIndex = -1;
#if MOBILEGL_BUILD_DISAGGREGATED
if (wireFbo && !isDefaultDrawFbo) {
const Int32 slot = wireFbo->DrawBuffers[i];
colorAttachmentFormat = ResolveWireImage(*wireFbo, wireFbo->Color[slot], VK_IMAGE_ASPECT_COLOR_BIT).format;
} else
#endif
if (isDefaultDrawFbo) {
colorAttachmentFormat = m_swapchainObject.GetSurfaceFormat().format;
} else if (colorAttachmentRenderbuffer != nullptr) {
@@ -6004,7 +6070,7 @@ void main() {
Bool VulkanRenderer::PrepareStorageImageTextures(
FrameContext::FrameData& frame,
const MG_State::GLState::ProgramObject& program,
const MagmaProgramSource& program,
const ProgramFactory::VkProgramObject& programObj) {
if (!programObj.hasStorageImages) {
return true;
@@ -6100,7 +6166,7 @@ void main() {
}
Bool VulkanRenderer::PrepareSamplerImageFeedbackSnapshots(
FrameContext::FrameData& frame,
const MG_State::GLState::ProgramObject& program,
const MagmaProgramSource& program,
const ProgramFactory::VkProgramObject& programObj,
VkPipelineStageFlags consumerShaderStageMask) {
auto& feedbackBindings = m_samplerImageFeedbackScratch;
@@ -6801,6 +6867,10 @@ void main() {
Bool VulkanRenderer::SetupDraw(FrameContext::FrameData& frame, GLenum mode, Flags<DrawSetupAspect> aspects,
const DrawCmdParam& drawParams,
const IndexBufferView* pIndexBufferView) {
#if MOBILEGL_BUILD_DISAGGREGATED
if (MG_Config::Transport != MG_Config::TransportMode::Monolith)
return SetupWireDraw(frame, mode, aspects, drawParams);
#endif
// Sync each sampled texture at most once across this whole draw: the layout
// probe loop, the post-transition loop, and ResolveSamplerDescriptor would
// otherwise each re-run the full SyncTexture path on the same textures.
@@ -7417,6 +7487,12 @@ void main() {
}
void VulkanRenderer::DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ) {
#if MOBILEGL_BUILD_DISAGGREGATED
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
DispatchWireCompute(numGroupsX, numGroupsY, numGroupsZ);
return;
}
#endif
m_textureManager->CollectGarbage();
auto& frame = m_frameContext.GetCurrent();
// The DISPATCH accessor: with a pipeline bound this is its compute stage program
@@ -7637,6 +7713,7 @@ void main() {
}
#include "WireFramebuffer.inc"
#include "WireDraw.inc"
void VulkanRenderer::Clear(GLbitfield mask) {
#if MOBILEGL_BUILD_DISAGGREGATED
@@ -457,6 +457,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void ReadWirePixels(GLint x, GLint y, GLsizei width, GLsizei height,
GLenum format, GLenum type, void* pixels);
void GenerateWireMipmap();
Bool SetupWireDraw(FrameContext::FrameData& frame, GLenum mode, Flags<DrawSetupAspect> aspects,
const DrawCmdParam& drawParams);
void DestroyWireDrawPass();
void DispatchWireCompute(GLuint x, GLuint y, GLuint z);
UniquePtr<RenderPassEntry> m_wireDrawPass;
Vector<VkImageView> m_wireDrawViews;
VkPipeline GetOrCreatePipelineWithInput(GLenum mode, const MagmaProgramSource& program,
const ProgramFactory::VkProgramObject& programObj, ProgramFactory::CompileOptionFlags transformFlags,
const VertexInputStateFactory::BackendVertexInputState& vis, const RenderPassEntry& renderPassEntry,
Bool primitiveRestartEnable);
void CopyWireFramebufferToTexture(GLenum target, GLint level, GLint xoffset, GLint yoffset,
GLint x, GLint y, GLsizei width, GLsizei height);
#endif
@@ -1535,7 +1546,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkPipeline GetOrCreatePipeline(
GLenum mode,
const MG_State::GLState::ProgramObject& program,
const MagmaProgramSource& program,
const ProgramFactory::VkProgramObject& programObj,
ProgramFactory::CompileOptionFlags transformFlags,
const MG_State::GLState::VertexArrayObject& vao,
@@ -1547,14 +1558,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// flush the pending recording (see the body), which retires the current command buffer.
Bool PrepareStorageImageTextures(
FrameContext::FrameData& frame,
const MG_State::GLState::ProgramObject& program,
const MagmaProgramSource& program,
const ProgramFactory::VkProgramObject& programObj);
// Vulkan forbids a sampled descriptor and writable storage descriptor from naming the
// same image subresource in one shader operation. Snapshot only the sampler side; the
// storage descriptor continues to name the application texture.
Bool PrepareSamplerImageFeedbackSnapshots(
FrameContext::FrameData& frame,
const MG_State::GLState::ProgramObject& program,
const MagmaProgramSource& program,
const ProgramFactory::VkProgramObject& programObj,
VkPipelineStageFlags consumerShaderStageMask);
@@ -0,0 +1,179 @@
// Server-owned draw/dispatch setup. The legacy setup remains the monolith implementation.
#if MOBILEGL_BUILD_DISAGGREGATED
static MagmaProgramSource WireProgramSource(MG_Pipe::MGPipeHandle handle) {
auto& state = MG_Pipe::MGPipeApplier();
const Bool composite = handle.Slot >= MG_Pipe::kMGPipeShaderCsoCompositeSlotBase;
const auto& records = composite ? state.CompositeShaderCsos : state.ShaderCsos;
const SizeT index = composite ? handle.Slot - MG_Pipe::kMGPipeShaderCsoCompositeSlotBase : handle.Slot;
if (MG_Pipe::MGPipeHandleIsNull(handle) || index >= records.size() ||
!records[index].Live || records[index].Gen != handle.Gen || !records[index].Archive)
MagmaWireFatal("program-record");
return MagmaProgramSource(handle, records[index]);
}
[[noreturn]] static void WireBufferLegacyFatal() {
MGLOG_F("MGPipe: Fatal{RoleViolation, \"buffer-legacy-arm\"} (Magma P7 buffer consumer)");
std::abort();
}
void VulkanRenderer::DestroyWireDrawPass() {
m_wireDrawPass.reset();
for (const auto view : m_wireDrawViews) vkDestroyImageView(m_device, view, nullptr);
m_wireDrawViews.clear();
}
Bool VulkanRenderer::SetupWireDraw(FrameContext::FrameData& frame, GLenum mode,
Flags<DrawSetupAspect> aspects, const DrawCmdParam& drawParams) {
auto& state = MG_Pipe::MGPipeApplier();
const auto program = WireProgramSource(state.BoundShaderCso);
if (!program.GetLinkStatus() || !program.GetSpirvStatus()) return false;
if (aspects & DrawSetupAspect::IndexBuffer || aspects & DrawSetupAspect::IndirectDrawBuffer ||
MGB_CTX->IsTransformFeedbackActive()) WireBufferLegacyFatal();
const auto* fbo = state.DrawFramebuffer();
if (!fbo) MagmaWireFatal("draw-framebuffer-record");
if (HasPendingRecordedWork() && !FlushPendingCommands()) MagmaWireFatal("draw-flush");
DestroyWireDrawPass();
ProgramFactory::CompileOptionFlags flags = GetShaderTransformFlags(m_swapchainObject.GetPreTransform());
const auto* programObject = &m_programFactory->GetOrCreateProgram(program, flags);
if (programObject->readsBaseVertexBuiltin) {
flags |= ProgramFactory::CompileOptionBit::ZeroBaseVertex;
programObject = &m_programFactory->GetOrCreateProgram(program, flags);
}
if (programObject->pointSizeCapabilityUnsupported) return false;
const auto& programObj = *programObject;
const auto vaoHandle = state.BoundVertexElements;
if (!MG_Pipe::MGPipeHandleIsNull(vaoHandle)) {
if (vaoHandle.Slot >= state.VertexElementsCsos.size()) MagmaWireFatal("vertex-elements-record");
const auto& vao = state.VertexElementsCsos[vaoHandle.Slot];
if (!vao.Live || vao.Gen != vaoHandle.Gen) MagmaWireFatal("vertex-elements-generation");
for (Uint32 i = 0; i < vao.AttributeCount; ++i)
if (vao.Attributes[i].Enabled && (programObj.activeVertexInputLocationMask & (1u << i)))
WireBufferLegacyFatal();
}
Vector<VkAttachmentDescription> attachments;
Vector<VkImageView> views;
Vector<VkAttachmentReference> colors;
VkAttachmentReference depth{VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL};
VkExtent2D extent{~0u, ~0u};
Uint32 layers = ~0u;
VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
Uint64 passHash = 0;
const auto add = [&](const MG_Pipe::MGPSurface& surface, VkImageAspectFlags aspect) -> Uint32 {
WireImage image = ResolveWireImage(*fbo, surface, aspect);
if (!image.image) return VK_ATTACHMENT_UNUSED;
m_textureManager->FlushPendingUploads();
const VkImageLayout layout = aspect == VK_IMAGE_ASPECT_COLOR_BIT ?
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
TransitionWireImage(image, layout);
VkImageViewCreateInfo info{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
info.image = image.image;
info.viewType = image.layers > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
info.format = image.format;
info.subresourceRange = {aspect, image.level, 1, image.layer, image.layers};
if (image.format == VK_FORMAT_D24_UNORM_S8_UINT || image.format == VK_FORMAT_D32_SFLOAT_S8_UINT)
info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
VkImageView view = VK_NULL_HANDLE;
VK_VERIFY(vkCreateImageView(m_device, &info, nullptr, &view));
m_wireDrawViews.push_back(view);
views.push_back(view);
VkAttachmentDescription description{};
description.format = image.format;
description.samples = image.samples;
description.loadOp = description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
description.storeOp = description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
description.initialLayout = description.finalLayout = layout;
attachments.push_back(description);
extent.width = std::min(extent.width, image.extent.width);
extent.height = std::min(extent.height, image.extent.height);
layers = std::min(layers, image.layers);
samples = image.samples;
passHash = XXH64(&description, sizeof(description), passHash);
if (!image.isDefault && surface.Kind == MG_Pipe::kMGPipeSurfaceKindTexture)
m_textureManager->MarkWireTextureGpuWritten(image.storage, image.level, image.layer, image.layers);
if (image.isDefault) {
if (aspect == VK_IMAGE_ASPECT_COLOR_BIT) m_swapchainObject.SetImageContentDefined(m_imageIndexAcquired, true);
else m_swapchainObject.SetDepthStencilContentDefined(m_imageIndexAcquired, true);
}
return static_cast<Uint32>(attachments.size() - 1);
};
for (Uint32 i = 0; i < MG_Pipe::kMGPipeMaxColorAttachments; ++i) {
const Int32 slot = fbo->DrawBuffers[i];
const Uint32 index = slot < 0 ? VK_ATTACHMENT_UNUSED : add(fbo->Color[slot], VK_IMAGE_ASPECT_COLOR_BIT);
colors.push_back({index, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL});
}
while (!colors.empty() && colors.back().attachment == VK_ATTACHMENT_UNUSED) colors.pop_back();
if (fbo->IsDefault || fbo->Depth.Kind != MG_Pipe::kMGPipeSurfaceKindNone)
depth.attachment = add(fbo->Depth, VK_IMAGE_ASPECT_DEPTH_BIT);
else if (fbo->Stencil.Kind != MG_Pipe::kMGPipeSurfaceKindNone)
depth.attachment = add(fbo->Stencil, VK_IMAGE_ASPECT_STENCIL_BIT);
if (attachments.empty()) return false;
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = static_cast<Uint32>(colors.size());
subpass.pColorAttachments = colors.data();
subpass.pDepthStencilAttachment = depth.attachment != VK_ATTACHMENT_UNUSED ? &depth : nullptr;
VkRenderPassCreateInfo passInfo{VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO};
passInfo.attachmentCount = static_cast<Uint32>(attachments.size());
passInfo.pAttachments = attachments.data();
passInfo.subpassCount = 1;
passInfo.pSubpasses = &subpass;
m_wireDrawPass = MakeUnique<RenderPassEntry>();
auto& pass = *m_wireDrawPass;
VK_VERIFY(vkCreateRenderPass(m_device, &passInfo, nullptr, &pass.renderPass));
VkFramebufferCreateInfo fbInfo{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};
fbInfo.renderPass = pass.renderPass;
fbInfo.attachmentCount = static_cast<Uint32>(views.size());
fbInfo.pAttachments = views.data();
fbInfo.width = extent.width; fbInfo.height = extent.height; fbInfo.layers = layers;
VK_VERIFY(vkCreateFramebuffer(m_device, &fbInfo, nullptr, &pass.framebuffer));
pass.attachmentCount = static_cast<Uint32>(attachments.size());
pass.colorAttachmentCount = static_cast<Uint32>(colors.size());
pass.hasDepthStencilAttachment = depth.attachment != VK_ATTACHMENT_UNUSED;
pass.sampleCount = samples;
pass.extent = {static_cast<Int>(extent.width), static_cast<Int>(extent.height)};
pass.layers = layers;
pass.hash = pass.compatibilityHash = XXH64(colors.data(), colors.size() * sizeof(colors[0]), passHash);
VertexInputStateFactory::BackendVertexInputState input{};
const auto pipeline = GetOrCreatePipelineWithInput(mode, program, programObj, flags, input, pass, false);
if (pipeline == VK_NULL_HANDLE) return false;
if (!frame.isCommandRecording) m_frameContext.BeginCommandRecording();
if (!m_uniformManager->BindProgramUniformBuffers(frame.commandBuffer, program, programObj,
m_frameContext.GetCurrentFrameIndex(), VK_PIPELINE_BIND_POINT_GRAPHICS)) return false;
Uint32 binding = 0;
for (Uint32 location = 0; location < kMaxVertexAttribs; ++location) {
if (!(programObj.activeVertexInputLocationMask & (1u << location))) continue;
VkFormat format = VK_FORMAT_UNDEFINED;
const void* data = nullptr;
VkDeviceSize size = 0;
if (!TryGetCurrentVertexAttributeUploadPayload(MGB_CTX->GetCurrentVertexAttribute(location),
programObj.vertexInputTypes[location], format, data, size)) return false;
BufferSlice slice{};
if (!m_bufferManager.UploadTransient(BufferKind::Vertex, m_frameContext.GetCurrentFrameIndex(),
data, size, 16, slice)) return false;
vkCmdBindVertexBuffers(frame.commandBuffer, binding++, 1, &slice.buffer, &slice.offset);
}
if (!VkRenderPassManager::BeginRenderPass(frame.commandBuffer, pass)) return false;
vkCmdBindPipeline(frame.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
ApplyDynamicDrawStateTail(frame, pass.extent, fbo->IsDefault,
ResolveDrawViewportCount(programObj.writesViewportIndexBuiltin));
(void)drawParams;
return true;
}
void VulkanRenderer::DispatchWireCompute(GLuint x, GLuint y, GLuint z) {
if (HasPendingRecordedWork() && !FlushPendingCommands()) MagmaWireFatal("dispatch-flush");
auto& frame = m_frameContext.GetCurrent();
const auto program = WireProgramSource(MG_Pipe::MGPipeApplier().DispatchProgram);
if (!program.GetLinkStatus() || !program.GetSpirvStatus()) return;
const auto& programObj = m_programFactory->GetOrCreateProgram(program, {});
const auto pipeline = GetOrCreateComputePipeline(programObj);
if (!pipeline) return;
if (!frame.isCommandRecording) m_frameContext.BeginCommandRecording();
if (!m_uniformManager->BindProgramUniformBuffers(frame.commandBuffer, program, programObj,
m_frameContext.GetCurrentFrameIndex(), VK_PIPELINE_BIND_POINT_COMPUTE)) return;
vkCmdBindPipeline(frame.commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
vkCmdDispatch(frame.commandBuffer, x, y, z);
}
#endif
+2 -1
View File
@@ -113,7 +113,8 @@ namespace MobileGL::MG_Backend {
switch (type) {
case BackendType::DirectGLES: return MG_Pipe::kMGPipeSubsystemsMigratedAtP5e;
case BackendType::DirectVulkan:
return MG_Pipe::kMGPipeSubsystemsMigratedAtP4a & ~MG_Pipe::kMGPipeSubsystemResources;
return (MG_Pipe::kMGPipeSubsystemsMigratedAtP4a | MG_Pipe::kMGPipeSubsystemBufferBindings) &
~MG_Pipe::kMGPipeSubsystemResources;
default: return 0;
}
}
+1 -1
View File
@@ -2383,7 +2383,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_TRANSPORT=inproc"
"MOBILEGL_ITEST_REQUIRE_GPU=1" "MGITEST_SPLIT_LANE=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
foreach(fmSlot ClearBufferfi ClearBufferfv ClearBufferiv ClearBufferuiv ClearNamedFramebufferfi ClearNamedFramebufferfv ClearNamedFramebufferiv ClearNamedFramebufferuiv CopyTexImage2D CopyTexSubImage2D GenerateMipmap GenerateMipmapPackedFloat GenerateMipmapDepth)
foreach(fmSlot PartialTextureUploadPreservesGpuClear TextureViewClearTargetsItsRootMipAndLayer ClearBufferfi ClearBufferfv ClearBufferiv ClearBufferuiv ClearNamedFramebufferfi ClearNamedFramebufferfv ClearNamedFramebufferiv ClearNamedFramebufferuiv CopyTexImage2D CopyTexSubImage2D GenerateMipmap GenerateMipmapPackedFloat GenerateMipmapDepth)
gtest_discover_tests(MobileGLIntegrationTest
TEST_PREFIX "DirectVulkan.Split.Fm."
TEST_FILTER "F1WireScenario.${fmSlot}Pixels"