mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[MG_Backend] (Disaggregated): P5f fm - resolve uniform descriptors from server records
This commit is contained in:
@@ -29,6 +29,9 @@
|
||||
#include "MG_Util/Metrics/TextureMetrics.h"
|
||||
#include "MG_Util/ShaderTranspiler/Types.h"
|
||||
#include <Config.h>
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
#include <MG_Pipe/PipeApply.h>
|
||||
#endif
|
||||
#include <vulkan/utility/vk_format_utils.h>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
@@ -37,6 +40,149 @@
|
||||
#include <limits>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
[[noreturn]] static void WireDescriptorFatal(const char* detail) {
|
||||
MGLOG_F("MGPipe: Fatal{UnmigratedVerb, \"Magma:%s\"}", detail);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
[[noreturn]] static void WireBufferDescriptorDebt() {
|
||||
MGLOG_F("MGPipe: Fatal{RoleViolation, \"buffer-legacy-arm\"} - Magma buffer descriptors remain P7");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
static VkImageViewType WireViewType(MG_Pipe::MGPipeResourceTarget target) {
|
||||
using T = MG_Pipe::MGPipeResourceTarget;
|
||||
switch (target) {
|
||||
case T::Tex1D: return VK_IMAGE_VIEW_TYPE_1D;
|
||||
case T::Tex1DArray: return VK_IMAGE_VIEW_TYPE_1D_ARRAY;
|
||||
case T::Tex3D: return VK_IMAGE_VIEW_TYPE_3D;
|
||||
case T::TexCube: return VK_IMAGE_VIEW_TYPE_CUBE;
|
||||
case T::TexCubeArray: return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
|
||||
case T::Tex2DArray: case T::Tex2DMSArray: return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
default: return VK_IMAGE_VIEW_TYPE_2D;
|
||||
}
|
||||
}
|
||||
|
||||
static VkComponentSwizzle WireSwizzle(Uint8 value) {
|
||||
switch (static_cast<TextureSwizzleParam>(value)) {
|
||||
case TextureSwizzleParam::Red: return VK_COMPONENT_SWIZZLE_R;
|
||||
case TextureSwizzleParam::Green: return VK_COMPONENT_SWIZZLE_G;
|
||||
case TextureSwizzleParam::Blue: return VK_COMPONENT_SWIZZLE_B;
|
||||
case TextureSwizzleParam::Alpha: return VK_COMPONENT_SWIZZLE_A;
|
||||
case TextureSwizzleParam::Zero: return VK_COMPONENT_SWIZZLE_ZERO;
|
||||
case TextureSwizzleParam::One: return VK_COMPONENT_SWIZZLE_ONE;
|
||||
default: WireDescriptorFatal("sampler-swizzle");
|
||||
}
|
||||
}
|
||||
|
||||
Bool UniformManager::ResolveWireImageDescriptor(
|
||||
VkCommandBuffer commandBuffer, const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding, Uint32 element,
|
||||
Bool storage, VkDescriptorImageInfo& out) const {
|
||||
out = {};
|
||||
auto& state = MG_Pipe::MGPipeApplier();
|
||||
const Int baseLocation = programObj.samplerUniformLocationByBinding[binding];
|
||||
const Int location = baseLocation + static_cast<Int>(element);
|
||||
if (baseLocation < 0 || !program.UniformLocationsAliasSameUniform(baseLocation, location)) return false;
|
||||
const Int unit = program.GetUniformSamplerOrImageUnitIndex(static_cast<Uint>(location));
|
||||
if (unit < 0 || static_cast<Uint32>(unit) >=
|
||||
(storage ? MG_Pipe::kMGPipeMaxImageUnits : MG_Pipe::kMGPipeMaxTextureUnits)) return false;
|
||||
const auto handle = storage ? state.BoundShaderImages[unit].Res : state.BoundSamplerViews[unit].Texture;
|
||||
// The old placeholder constructor enters the guarded client allocator. Keep that
|
||||
// P7 boundary explicit instead of constructing a frontend texture on the server.
|
||||
if (MG_Pipe::MGPipeHandleIsNull(handle)) WireDescriptorFatal("unbound-image-placeholder@P7");
|
||||
if (handle.Slot >= state.TextureResources.size()) WireDescriptorFatal("image-record");
|
||||
const auto& record = state.TextureResources[handle.Slot];
|
||||
if (!record.Live || record.Gen != handle.Gen) WireDescriptorFatal("image-record-generation");
|
||||
auto* resource = m_textureManager->SyncTextureResourceByHandle(handle);
|
||||
if (!resource) WireDescriptorFatal("image-resource");
|
||||
m_textureManager->FlushPendingUploads();
|
||||
|
||||
Uint32 level = storage ? state.BoundShaderImages[unit].Level : record.Params.BaseLevel;
|
||||
Uint32 layer = storage && !state.BoundShaderImages[unit].Layered ? state.BoundShaderImages[unit].Layer : 0;
|
||||
const Uint32 localLevel = level;
|
||||
const Uint32 localLayer = layer;
|
||||
Uint32 layers = 0;
|
||||
VkFormat aliasFormat = VK_FORMAT_UNDEFINED;
|
||||
const auto root = m_textureManager->ResolveWireTextureStorage(handle, level, layer, &aliasFormat, &layers);
|
||||
if (MG_Pipe::MGPipeHandleIsNull(root)) WireDescriptorFatal("image-view-window");
|
||||
const auto target = static_cast<MG_Pipe::MGPipeResourceTarget>(record.Desc.Target);
|
||||
VkImageViewType type = WireViewType(target);
|
||||
if (storage && !state.BoundShaderImages[unit].Layered) {
|
||||
layers = 1;
|
||||
type = target == MG_Pipe::MGPipeResourceTarget::Tex1DArray ? VK_IMAGE_VIEW_TYPE_1D : VK_IMAGE_VIEW_TYPE_2D;
|
||||
}
|
||||
if (type == VK_IMAGE_VIEW_TYPE_3D) layers = 1;
|
||||
Uint32 levels = 1;
|
||||
if (!storage) {
|
||||
if (record.Params.MaxLevel < localLevel || record.Desc.Levels <= localLevel) return false;
|
||||
levels = std::min<Uint32>(record.Desc.Levels - localLevel,
|
||||
record.Params.MaxLevel - localLevel + 1);
|
||||
const auto viewHandle = state.BoundSamplerViews[unit].View;
|
||||
if (!MG_Pipe::MGPipeHandleIsNull(viewHandle) && viewHandle.Slot < state.SamplerViewCsos.size()) {
|
||||
const auto& view = state.SamplerViewCsos[viewHandle.Slot];
|
||||
if (!view.Live || view.Gen != viewHandle.Gen || view.View.NumLevels <= localLevel)
|
||||
WireDescriptorFatal("sampler-view-record");
|
||||
levels = std::min<Uint32>(levels, view.View.NumLevels - localLevel);
|
||||
}
|
||||
levels = std::min(levels, resource->mipLevels - level);
|
||||
}
|
||||
VkFormat format = aliasFormat == VK_FORMAT_UNDEFINED ? resource->format : aliasFormat;
|
||||
const auto domain = programObj.samplerNumericDomainByBinding[binding];
|
||||
if (storage) {
|
||||
format = ResolveStorageImageViewFormat(programObj.storageImageFormatByBinding[binding],
|
||||
state.BoundShaderImages[unit].InternalFormat, format,
|
||||
programObj.storageImageUsesBindingFormatByBinding[binding]);
|
||||
} else if (resource->aspect == VK_IMAGE_ASPECT_COLOR_BIT) {
|
||||
format = VkTextureManager::ResolveSampledImageViewFormat(format, domain);
|
||||
}
|
||||
if (format == VK_FORMAT_UNDEFINED) return false;
|
||||
VkImageAspectFlags aspect = resource->aspect;
|
||||
if (!storage && (aspect & VK_IMAGE_ASPECT_DEPTH_BIT))
|
||||
aspect = record.Params.DepthStencilMode == MG_Pipe::kMGPipeDepthStencilModeStencil
|
||||
? VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
|
||||
// All wire image descriptors use GENERAL. A texture sampled and image-bound in
|
||||
// the same shader then has one truthful layout, including views of the same owner.
|
||||
if (!VkTextureManager::TransitionImageLayout(commandBuffer, resource->image, resource->layout,
|
||||
VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
resource->layout == VK_IMAGE_LAYOUT_UNDEFINED ? 0 : VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
|
||||
VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, resource->aspect, 0, resource->mipLevels))
|
||||
WireDescriptorFatal("image-descriptor-transition");
|
||||
VkImageViewCreateInfo info{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
|
||||
info.image = resource->image;
|
||||
info.viewType = type;
|
||||
info.format = format;
|
||||
info.subresourceRange = {aspect, level, levels, layer, layers};
|
||||
if (!storage) {
|
||||
info.components = {WireSwizzle(record.Params.Swizzle[0]), WireSwizzle(record.Params.Swizzle[1]),
|
||||
WireSwizzle(record.Params.Swizzle[2]), WireSwizzle(record.Params.Swizzle[3])};
|
||||
}
|
||||
VkImageView view = VK_NULL_HANDLE;
|
||||
if (vkCreateImageView(m_device, &info, nullptr, &view) != VK_SUCCESS) return false;
|
||||
m_frames[m_wireFrameIndex].wireImageViews.push_back(view);
|
||||
out.imageView = view;
|
||||
out.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
if (storage) {
|
||||
if (state.BoundShaderImages[unit].Access != MG_Pipe::kMGPipeImageAccessReadOnly)
|
||||
m_textureManager->MarkWireTextureGpuWritten(handle, localLevel, localLayer, layers);
|
||||
} else {
|
||||
auto samplerHandle = state.BoundSamplerStates[unit];
|
||||
if (MG_Pipe::MGPipeHandleIsNull(samplerHandle)) samplerHandle = record.Params.BuiltinSampler;
|
||||
if (MG_Pipe::MGPipeHandleIsNull(samplerHandle) || samplerHandle.Slot >= state.SamplerCsos.size())
|
||||
WireDescriptorFatal("sampler-record");
|
||||
const auto& sampler = state.SamplerCsos[samplerHandle.Slot];
|
||||
if (!sampler.Live || sampler.Gen != samplerHandle.Gen) WireDescriptorFatal("sampler-record-generation");
|
||||
out.sampler = m_samplerManager->GetOrCreateSamplerFromParameters(sampler.Params,
|
||||
static_cast<TextureInternalFormat>(record.Desc.InternalFormat),
|
||||
domain == SamplerNumericDomain::SignedInteger || domain == SamplerNumericDomain::UnsignedInteger,
|
||||
levels);
|
||||
if (!out.sampler) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
namespace {
|
||||
constexpr Uint kFallbackTexture2DExternalIndex = 0xFFFFFF00u;
|
||||
// One id for every storage-image placeholder. They are never reachable through GL - no
|
||||
@@ -234,7 +380,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// per array element, so the element's location is the base plus its index - bounded by the
|
||||
// array's real extent so a descriptorCount that outran the reflection cannot walk onto the
|
||||
// next uniform. Element 0 is the ordinary non-array case and costs nothing extra.
|
||||
static Int ResolveDescriptorElementLocation(const MG_State::GLState::ProgramObject& program, Int baseLocation,
|
||||
static Int ResolveDescriptorElementLocation(const MagmaProgramSource& program, Int baseLocation,
|
||||
Uint32 element) {
|
||||
if (baseLocation < 0 || element == 0) {
|
||||
return baseLocation;
|
||||
@@ -252,7 +398,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
: 1u;
|
||||
}
|
||||
|
||||
static Int ResolveSamplerUnitIndex(const MG_State::GLState::ProgramObject& program, Int location, Uint32 binding) {
|
||||
static Int ResolveSamplerUnitIndex(const MagmaProgramSource& program, Int location, Uint32 binding) {
|
||||
MOBILEGL_ASSERT(location >= -1, "ResolveSamplerUnitIndex: invalid sampler location for binding %u", binding);
|
||||
if (location < 0) {
|
||||
return 0;
|
||||
@@ -349,6 +495,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_unboundStorageImageTextures.clear();
|
||||
for (auto& frame : m_frames) {
|
||||
if (m_device != VK_NULL_HANDLE) {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
for (const auto view : frame.wireImageViews) vkDestroyImageView(m_device, view, nullptr);
|
||||
frame.wireImageViews.clear();
|
||||
#endif
|
||||
for (auto& view : frame.texelBufferViews) {
|
||||
if (view != VK_NULL_HANDLE) {
|
||||
vkDestroyBufferView(m_device, view, nullptr);
|
||||
@@ -390,6 +540,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void UniformManager::BeginFrame(Uint32 frameIndex) {
|
||||
MOBILEGL_ASSERT(frameIndex < m_frames.size(), "UniformDescriptorBinder::BeginFrame invalid frame index");
|
||||
auto& frame = m_frames[frameIndex];
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
m_wireFrameIndex = frameIndex;
|
||||
for (const auto view : frame.wireImageViews) vkDestroyImageView(m_device, view, nullptr);
|
||||
frame.wireImageViews.clear();
|
||||
#endif
|
||||
for (auto& view : frame.texelBufferViews) {
|
||||
if (view != VK_NULL_HANDLE) {
|
||||
vkDestroyBufferView(m_device, view, nullptr);
|
||||
@@ -467,11 +622,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
Bool UniformManager::ResolveSamplerDescriptor(VkCommandBuffer commandBuffer,
|
||||
const MG_State::GLState::ProgramObject& program,
|
||||
const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Uint32 binding, Uint32 element,
|
||||
VkDescriptorImageInfo& outImageInfo,
|
||||
Bool trustUnchangedHint) const {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
if (program.IsWire()) return ResolveWireImageDescriptor(commandBuffer, program, programObj, binding, element, false, outImageInfo);
|
||||
#endif
|
||||
MOBILEGL_ASSERT(m_textureManager != nullptr, "ResolveSamplerDescriptor: texture manager is null");
|
||||
MOBILEGL_ASSERT(m_samplerManager != nullptr, "ResolveSamplerDescriptor: sampler manager is null");
|
||||
// The whole-descriptor memo below is keyed by binding alone, so it describes a binding
|
||||
@@ -753,7 +911,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
Bool UniformManager::ProgramSamplesOnlySingleLevelTextures(
|
||||
const MG_State::GLState::ProgramObject& program, const ProgramFactory::VkProgramObject& programObj) {
|
||||
const MagmaProgramSource& program, const ProgramFactory::VkProgramObject& programObj) {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
if (program.IsWire()) return false; // Conservative optimization gate; no frontend probe.
|
||||
#endif
|
||||
// A declined program never draws (see VkProgramObject::declinedDescriptors), and its
|
||||
// declined binding has no resolvable uniform location - so there is nothing to prove
|
||||
// about the textures it would have sampled.
|
||||
@@ -810,7 +971,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return sawSampler;
|
||||
}
|
||||
|
||||
Bool UniformManager::ResolveSamplerTexture(const MG_State::GLState::ProgramObject& program,
|
||||
Bool UniformManager::ResolveSamplerTexture(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
SharedPtr<MG_State::GLState::ITextureObject>& outTexture) {
|
||||
outTexture.reset();
|
||||
@@ -837,7 +998,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
MG_State::GLState::ITextureObject* UniformManager::ResolveSamplerTextureRaw(
|
||||
const MG_State::GLState::ProgramObject& program, const ProgramFactory::VkProgramObject& programObj,
|
||||
const MagmaProgramSource& program, const ProgramFactory::VkProgramObject& programObj,
|
||||
Uint32 binding, Uint32 element) {
|
||||
MOBILEGL_ASSERT(MGB_CTX_LIVE, "ResolveSamplerTextureRaw: GL context is null");
|
||||
MOBILEGL_ASSERT(binding < programObj.samplerUniformLocationByBinding.size(),
|
||||
@@ -864,10 +1025,28 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return texture;
|
||||
}
|
||||
|
||||
Bool UniformManager::ResolveTexelBufferDescriptor(const MG_State::GLState::ProgramObject& program,
|
||||
Bool UniformManager::ResolveTexelBufferDescriptor(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Uint32 binding, Uint32 frameIndex,
|
||||
VkBufferView& outBufferView) {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
if (program.IsWire()) {
|
||||
const Int location = programObj.samplerUniformLocationByBinding[binding];
|
||||
const Int unit = location < 0 ? 0 : program.GetUniformSamplerOrImageUnitIndex(static_cast<Uint>(location));
|
||||
const auto& state = MG_Pipe::MGPipeApplier();
|
||||
if (unit < 0 || static_cast<Uint32>(unit) >= MG_Pipe::kMGPipeMaxTextureUnits) return false;
|
||||
const auto handle = state.BoundSamplerViews[unit].Texture;
|
||||
if (!MG_Pipe::MGPipeHandleIsNull(handle)) {
|
||||
if (handle.Slot >= state.TextureResources.size()) WireDescriptorFatal("texel-buffer-record");
|
||||
const auto& record = state.TextureResources[handle.Slot];
|
||||
if (!record.Live || record.Gen != handle.Gen) WireDescriptorFatal("texel-buffer-generation");
|
||||
if (!MG_Pipe::MGPipeHandleIsNull(record.Desc.BufferForTexBuffer)) WireBufferDescriptorDebt();
|
||||
}
|
||||
outBufferView = AcquireUnboundTexelBufferView(VK_FORMAT_UNDEFINED,
|
||||
programObj.samplerNumericDomainByBinding[binding], false);
|
||||
return outBufferView != VK_NULL_HANDLE;
|
||||
}
|
||||
#endif
|
||||
outBufferView = VK_NULL_HANDLE;
|
||||
MOBILEGL_ASSERT(m_bufferManager != nullptr, "ResolveTexelBufferDescriptor: buffer manager is null");
|
||||
MOBILEGL_ASSERT(frameIndex < m_frames.size(), "ResolveTexelBufferDescriptor: frame index out of range");
|
||||
@@ -988,10 +1167,28 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// binding at all for a uniform the shader still read, and lavapipe segfaulted inside pipeline
|
||||
// creation on the JIT worker thread. KHR-GL44.multi_bind.dispatch_bind_image_textures is the
|
||||
// case that carries it.
|
||||
Bool UniformManager::ResolveStorageTexelBufferDescriptor(const MG_State::GLState::ProgramObject& program,
|
||||
Bool UniformManager::ResolveStorageTexelBufferDescriptor(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Uint32 binding, Uint32 frameIndex,
|
||||
VkBufferView& outBufferView) {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
if (program.IsWire()) {
|
||||
const Int location = programObj.samplerUniformLocationByBinding[binding];
|
||||
const Int unit = location < 0 ? 0 : program.GetUniformSamplerOrImageUnitIndex(static_cast<Uint>(location));
|
||||
const auto& state = MG_Pipe::MGPipeApplier();
|
||||
if (unit < 0 || static_cast<Uint32>(unit) >= MG_Pipe::kMGPipeMaxImageUnits) return false;
|
||||
const auto handle = state.BoundShaderImages[unit].Res;
|
||||
if (!MG_Pipe::MGPipeHandleIsNull(handle)) {
|
||||
if (handle.Slot >= state.TextureResources.size()) WireDescriptorFatal("texel-buffer-record");
|
||||
const auto& record = state.TextureResources[handle.Slot];
|
||||
if (!record.Live || record.Gen != handle.Gen) WireDescriptorFatal("texel-buffer-generation");
|
||||
if (!MG_Pipe::MGPipeHandleIsNull(record.Desc.BufferForTexBuffer)) WireBufferDescriptorDebt();
|
||||
}
|
||||
outBufferView = AcquireUnboundTexelBufferView(programObj.storageImageFormatByBinding[binding],
|
||||
programObj.samplerNumericDomainByBinding[binding], true);
|
||||
return outBufferView != VK_NULL_HANDLE;
|
||||
}
|
||||
#endif
|
||||
outBufferView = VK_NULL_HANDLE;
|
||||
MOBILEGL_ASSERT(m_bufferManager != nullptr, "ResolveStorageTexelBufferDescriptor: buffer manager is null");
|
||||
MOBILEGL_ASSERT(MGB_CTX_LIVE, "ResolveStorageTexelBufferDescriptor: GL context is null");
|
||||
@@ -1154,7 +1351,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool UniformManager::ResolveStorageBufferDescriptor(const MG_State::GLState::ProgramObject& program,
|
||||
Bool UniformManager::ResolveStorageBufferDescriptor(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Uint32 binding, Uint32 element,
|
||||
VkDescriptorBufferInfo& outBufferInfo) const {
|
||||
@@ -1195,7 +1392,26 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const GLuint frontendBinding =
|
||||
isAtomicCounterBlock
|
||||
? static_cast<GLuint>(atomicCounterBinding)
|
||||
: GetShaderStorageBlockBinding(program, static_cast<GLuint>(blockIndex)) + element;
|
||||
:
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
(program.IsWire() ? program.GetShaderStorageBlockBinding(static_cast<GLuint>(blockIndex)) :
|
||||
GetShaderStorageBlockBinding(*program.Frontend(), static_cast<GLuint>(blockIndex))) + element;
|
||||
#else
|
||||
GetShaderStorageBlockBinding(program, static_cast<GLuint>(blockIndex)) + element;
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
if (program.IsWire()) {
|
||||
const auto& state = MG_Pipe::MGPipeApplier();
|
||||
const Uint32 cls = isAtomicCounterBlock ? 2u : 1u;
|
||||
if (frontendBinding < state.ShaderBufferCount[cls] &&
|
||||
!MG_Pipe::MGPipeHandleIsNull(state.BoundShaderBuffers[cls][frontendBinding].Res))
|
||||
WireBufferDescriptorDebt();
|
||||
const BufferSlice placeholder = m_bufferManager->AcquireUnboundStorageDescriptor();
|
||||
if (!placeholder.IsValid()) return false;
|
||||
outBufferInfo = {placeholder.buffer, placeholder.offset, placeholder.size};
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
const Uint32 bindingPointCount =
|
||||
static_cast<Uint32>(MGB_CTX->GetBufferBindingPointCount(bufferTarget));
|
||||
MOBILEGL_ASSERT(frontendBinding < bindingPointCount,
|
||||
@@ -1274,10 +1490,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
Bool UniformManager::ResolveStorageImageDescriptor(VkCommandBuffer commandBuffer,
|
||||
const MG_State::GLState::ProgramObject& program,
|
||||
const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Uint32 binding, Uint32 element,
|
||||
VkDescriptorImageInfo& outImageInfo) const {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
if (program.IsWire()) return ResolveWireImageDescriptor(commandBuffer, program, programObj, binding, element, true, outImageInfo);
|
||||
#endif
|
||||
outImageInfo = {};
|
||||
MOBILEGL_ASSERT(m_textureManager != nullptr, "ResolveStorageImageDescriptor: texture manager is null");
|
||||
MOBILEGL_ASSERT(MGB_CTX_LIVE, "ResolveStorageImageDescriptor: GL context is null");
|
||||
@@ -1657,7 +1876,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return texture;
|
||||
}
|
||||
|
||||
Bool UniformManager::ResolveSampledBinding(const MG_State::GLState::ProgramObject& program,
|
||||
Bool UniformManager::ResolveSampledBinding(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Uint32 binding, Uint32 element,
|
||||
MG_State::GLState::ITextureObject*& outTexture,
|
||||
@@ -1726,10 +1945,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool UniformManager::CollectSampledTextures(const MG_State::GLState::ProgramObject& program,
|
||||
Bool UniformManager::CollectSampledTextures(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Vector<MG_State::GLState::ITextureObject*>& outTextures,
|
||||
Vector<SampledBindingRecord>* outBindingRecords) {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
if (program.IsWire()) { outTextures.clear(); if (outBindingRecords) outBindingRecords->clear(); return true; }
|
||||
#endif
|
||||
outTextures.clear();
|
||||
if (outBindingRecords != nullptr) {
|
||||
outBindingRecords->clear();
|
||||
@@ -1773,9 +1995,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool UniformManager::SampledBindingsUnchanged(const MG_State::GLState::ProgramObject& program,
|
||||
Bool UniformManager::SampledBindingsUnchanged(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
const Vector<SampledBindingRecord>& previousRecords) const {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
if (program.IsWire()) return false;
|
||||
#endif
|
||||
// A declined program takes the full path every time and is refused there.
|
||||
if (programObj.declinedDescriptors) {
|
||||
return false;
|
||||
@@ -1816,9 +2041,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
Bool UniformManager::CollectStorageImageTextures(
|
||||
const MG_State::GLState::ProgramObject& program,
|
||||
const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Vector<MG_State::GLState::ITextureObject*>& outTextures) const {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
if (program.IsWire()) { outTextures.clear(); return true; }
|
||||
#endif
|
||||
outTextures.clear();
|
||||
MOBILEGL_ASSERT(MGB_CTX_LIVE,
|
||||
"CollectStorageImageTextures: GL context is null");
|
||||
@@ -1898,9 +2126,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
Bool UniformManager::CollectSamplerImageFeedback(
|
||||
const MG_State::GLState::ProgramObject& program,
|
||||
const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Vector<SamplerImageFeedbackBinding>& outBindings) const {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
if (program.IsWire()) { outBindings.clear(); return true; }
|
||||
#endif
|
||||
outBindings.clear();
|
||||
MOBILEGL_ASSERT(MGB_CTX_LIVE,
|
||||
"CollectSamplerImageFeedback: GL context is null");
|
||||
@@ -1976,7 +2207,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool UniformManager::ResolveUniformBufferPayload(const MG_State::GLState::ProgramObject& program,
|
||||
Bool UniformManager::ResolveUniformBufferPayload(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
Uint32 arrayElement, UboBindResult& out) const {
|
||||
const void* outData = nullptr;
|
||||
@@ -2002,6 +2233,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return outData != nullptr && outSize > 0;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// Global uniforms above are record bytes; named UBOs retain P7's buffer boundary.
|
||||
if (program.IsWire()) WireBufferDescriptorDebt();
|
||||
#endif
|
||||
|
||||
MOBILEGL_ASSERT(binding < programObj.uniformBlockIndexByBinding.size(),
|
||||
"ResolveUniformBufferPayload: UBO mapping binding %u out of range", binding);
|
||||
Int blockIndex = programObj.uniformBlockIndexByBinding[binding];
|
||||
@@ -2265,7 +2501,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
Bool UniformManager::ResolveDynamicUboDescriptor(const MG_State::GLState::ProgramObject& program,
|
||||
Bool UniformManager::ResolveDynamicUboDescriptor(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Uint32 binding, Uint32 arrayElement, Uint32 frameIndex,
|
||||
VkBuffer& outBuffer, VkDeviceSize& outRange,
|
||||
@@ -2361,7 +2597,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
Bool UniformManager::BindProgramUniformBuffers(VkCommandBuffer commandBuffer,
|
||||
const MG_State::GLState::ProgramObject& program,
|
||||
const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Uint32 frameIndex,
|
||||
VkPipelineBindPoint bindPoint,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ProgramFactory.h"
|
||||
#include "MagmaProgramSource.h"
|
||||
#include "VkBufferManager.h"
|
||||
#include "VkSamplerManager.h"
|
||||
#include "VkTextureManager.h"
|
||||
@@ -78,7 +79,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Uint64 textureLifetimeId = 0;
|
||||
Uint64 samplerLifetimeId = 0;
|
||||
};
|
||||
Bool CollectSampledTextures(const MG_State::GLState::ProgramObject& program,
|
||||
Bool CollectSampledTextures(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Vector<MG_State::GLState::ITextureObject*>& outTextures,
|
||||
Vector<SampledBindingRecord>* outBindingRecords = nullptr);
|
||||
@@ -87,14 +88,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// (texture, effective sampler) pair. A texture bind generation bump alone (e.g. a
|
||||
// redundant glBindSampler, which always bumps it) does not prove the sampled set
|
||||
// moved; this walk does, without rebuilding the set or falling off the fast path.
|
||||
Bool SampledBindingsUnchanged(const MG_State::GLState::ProgramObject& program,
|
||||
Bool SampledBindingsUnchanged(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
const Vector<SampledBindingRecord>& previousRecords) const;
|
||||
Bool CollectStorageImageTextures(const MG_State::GLState::ProgramObject& program,
|
||||
Bool CollectStorageImageTextures(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Vector<MG_State::GLState::ITextureObject*>& outTextures) const;
|
||||
Bool CollectSamplerImageFeedback(
|
||||
const MG_State::GLState::ProgramObject& program,
|
||||
const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Vector<SamplerImageFeedbackBinding>& outBindings) const;
|
||||
static Bool SamplerOverlapsWritableImageSubresource(Int samplerBaseLevel, Int samplerMaxLevel,
|
||||
@@ -106,7 +107,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// epochs AND per-resource layout values - so the per-binding cached
|
||||
// VkDescriptorImageInfo may be reused without re-running the resolve chain.
|
||||
Bool BindProgramUniformBuffers(VkCommandBuffer commandBuffer,
|
||||
const MG_State::GLState::ProgramObject& program,
|
||||
const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Uint32 frameIndex,
|
||||
VkPipelineBindPoint bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
@@ -127,7 +128,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// ExplicitLod0Sampling SPIR-V rewrite safe to request. Deliberately conservative: it reads
|
||||
// only GL state, so a texture that ends up single-level for another reason (one uploaded
|
||||
// level under a wide level range) merely misses the rewrite.
|
||||
static Bool ProgramSamplesOnlySingleLevelTextures(const MG_State::GLState::ProgramObject& program,
|
||||
static Bool ProgramSamplesOnlySingleLevelTextures(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj);
|
||||
|
||||
private:
|
||||
@@ -155,12 +156,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Vector<DescriptorPoolBucket> descriptorPools;
|
||||
UnorderedMap<VkDescriptorSetLayout, DescriptorSetCacheEntry> descriptorSetCacheByLayout;
|
||||
Vector<VkBufferView> texelBufferViews;
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
mutable Vector<VkImageView> wireImageViews;
|
||||
#endif
|
||||
Uint32 activeDescriptorPoolIndex = 0;
|
||||
Uint32 allocatedSetsThisFrame = 0;
|
||||
Uint32 peakAllocatedSetsThisFrame = 0;
|
||||
};
|
||||
|
||||
static Bool ResolveSamplerTexture(const MG_State::GLState::ProgramObject& program,
|
||||
static Bool ResolveSamplerTexture(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
SharedPtr<MG_State::GLState::ITextureObject>& outTexture);
|
||||
// Shared per-binding resolution for CollectSampledTextures and
|
||||
@@ -169,7 +173,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// exists), effective sampler = unit override else the texture's own sampler.
|
||||
// False = the binding is skipped (unbound with a non-2D fallback target).
|
||||
// `element` indexes a sampler array inside the binding; see ResolveSamplerDescriptor.
|
||||
Bool ResolveSampledBinding(const MG_State::GLState::ProgramObject& program,
|
||||
Bool ResolveSampledBinding(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding, Uint32 element,
|
||||
MG_State::GLState::ITextureObject*& outTexture,
|
||||
const MG_State::GLState::SamplerObject*& outSampler) const;
|
||||
@@ -177,7 +181,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// the bound texture stays alive through the draw via GL binding state, so callers that
|
||||
// only need the pointer skip the SharedPtr copy's atomic refcount churn.
|
||||
static MG_State::GLState::ITextureObject* ResolveSamplerTextureRaw(
|
||||
const MG_State::GLState::ProgramObject& program,
|
||||
const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding, Uint32 element);
|
||||
// `numericDomain` is the sampler's class, and it matters only for the multisample arm -
|
||||
// see GetFallbackMultisampleTexture for why the single-sampled fallback can ignore it.
|
||||
@@ -224,13 +228,19 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// (see BindProgramUniformBuffers' samplerDescriptorsUnchangedHint for the proof
|
||||
// obligations the caller carries). The cache is keyed by binding alone, so it is
|
||||
// used ONLY for single-descriptor bindings - see m_samplerResolveMemo.
|
||||
Bool ResolveSamplerDescriptor(VkCommandBuffer commandBuffer, const MG_State::GLState::ProgramObject& program,
|
||||
Bool ResolveSamplerDescriptor(VkCommandBuffer commandBuffer, const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
Uint32 element, VkDescriptorImageInfo& outImageInfo,
|
||||
Bool trustUnchangedHint = false) const;
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
Bool ResolveWireImageDescriptor(VkCommandBuffer commandBuffer, const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
Uint32 element, Bool storage, VkDescriptorImageInfo& out) const;
|
||||
Uint32 m_wireFrameIndex = 0;
|
||||
#endif
|
||||
Bool ResolveSamplerDescriptorOverride(const SamplerBindingOverride& samplerBindingOverride,
|
||||
VkDescriptorImageInfo& outImageInfo) const;
|
||||
Bool ResolveTexelBufferDescriptor(const MG_State::GLState::ProgramObject& program,
|
||||
Bool ResolveTexelBufferDescriptor(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
Uint32 frameIndex, VkBufferView& outBufferView);
|
||||
// GLSL `imageBuffer`: the same VkBufferView descriptor as the sampled texel buffer above,
|
||||
@@ -238,19 +248,19 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// made GPU-resident-writable because the shader may store to it. No `element` parameter:
|
||||
// an imageBuffer ARRAY is refused at program creation, so a binding is always one
|
||||
// descriptor (see the array gate in RemapDescriptorBindingsForVulkan).
|
||||
Bool ResolveStorageTexelBufferDescriptor(const MG_State::GLState::ProgramObject& program,
|
||||
Bool ResolveStorageTexelBufferDescriptor(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
Uint32 frameIndex, VkBufferView& outBufferView);
|
||||
// `element` indexes a block INSTANCE array's descriptors; it is 0 for every ordinary
|
||||
// block. Each element resolves through its own GL storage block, and so its own GL
|
||||
// binding point, buffer and glBindBufferRange window.
|
||||
Bool ResolveStorageBufferDescriptor(const MG_State::GLState::ProgramObject& program,
|
||||
Bool ResolveStorageBufferDescriptor(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
Uint32 element, VkDescriptorBufferInfo& outBufferInfo) const;
|
||||
// `element` indexes an image ARRAY inside one binding; each element carries its own
|
||||
// independently assigned GL image unit.
|
||||
Bool ResolveStorageImageDescriptor(VkCommandBuffer commandBuffer,
|
||||
const MG_State::GLState::ProgramObject& program,
|
||||
const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
Uint32 element, VkDescriptorImageInfo& outImageInfo) const;
|
||||
// Result of resolving a UBO binding: either a zero-copy direct bind to the app's resident
|
||||
@@ -263,14 +273,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const void* payload = nullptr; // fallback UploadTransient path
|
||||
VkDeviceSize payloadSize = 0;
|
||||
};
|
||||
Bool ResolveUniformBufferPayload(const MG_State::GLState::ProgramObject& program,
|
||||
Bool ResolveUniformBufferPayload(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
Uint32 arrayElement, UboBindResult& out) const;
|
||||
// Shared resolution of one dynamic-UBO binding element into the
|
||||
// (buffer, range, dynamicOffset) triple the descriptor consumes: direct
|
||||
// bind, global-slice reuse, or transient upload. Used by the full walk
|
||||
// and by the dynamic-offset-only rebind (see FastRebindMemo).
|
||||
Bool ResolveDynamicUboDescriptor(const MG_State::GLState::ProgramObject& program,
|
||||
Bool ResolveDynamicUboDescriptor(const MagmaProgramSource& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
Uint32 arrayElement, Uint32 frameIndex, VkBuffer& outBuffer,
|
||||
VkDeviceSize& outRange, Uint32& outDynamicOffset);
|
||||
|
||||
Reference in New Issue
Block a user