mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[MG_Backend] (P7): a range past the device's native limit is clamped, not refused
`uniform-block-native-range@P7` and `storage-buffer-native-range@P7` both become
clamps with an MGLOG_E_ONCE (rule I (a)). Neither is reachable from a conformant
program: GL_MAX_UNIFORM_BLOCK_SIZE is published straight from the same
maxUniformBufferRange and GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT /
GL_MAX_SHADER_STORAGE_BLOCK_SIZE from the same storage limits
(BackendLoaders/Vulkan/Loader.cpp), so a block this big fails to link long before
a draw. Refusing was a Fatal guarding a shape nothing can produce.
The monolith arm is the argument. It binds whatever the GL range asked for and
never consults maxStorageBufferRange at all (ResolveStorageBufferDescriptor's
resident path), so the wire arm was inventing a death the other backend does not
have. If the two limits ever disagree, the honest answer is the bytes the device
CAN name: everything past the clamp is outside the descriptor and reads back as
zero under robustBufferAccess, which is inside GL's "undefined" for a block the
implementation never promised to hold. Clamping keeps the range constant across
draws, so the descriptor-set reuse hash is unaffected.
red-once (R-16). Neither site is reachable from a test, so the red is the same
two cases with the device limit forced down to 16 bytes, both shapes built and
run back to back on the Split and Spawn arms:
uniform-block-native-range (reflected block 32, limit forced to 16)
before Subprocess aborted - Fatal{UnmigratedVerb, "Magma:uniform-block-native-range@P7"}
after BOTH ARMS PASSED. The clamp keeps every byte the descriptor can name,
so the case's own pixel is unchanged, and the role log carries
"reflected uniform block 0 is 32 bytes, past this device's
maxUniformBufferRange of 16; the binding is clamped to the limit
and the remainder reads zero"
storage-buffer-native-range (block 'Output' 24 bytes, limit forced to 16)
before Subprocess aborted - Fatal{UnmigratedVerb, "Magma:storage-buffer-native-range@P7"}
after ***Failed on the data and NOT on a Fatal - the session lives, 8 of the
24 bytes are genuinely past what the forced limit can name, and the
role log carries "block 'Output' bound 24 bytes, past this device's
maxStorageBufferRange of 16; the binding is clamped to the limit"
restored (this commit as committed): 4/4 green, no force markers left.
Lanes: integration-magma-split 74/74, -spawn 53/53, -tcp 55/55,
integration-magma-buffers 20/20.
This commit is contained in:
@@ -1554,7 +1554,19 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
(slice.offset + start) % m_wireStorageOffsetAlignment != 0)
|
||||
WireDescriptorFatal(isAtomicCounterBlock ? "unaligned-atomic-buffer-range@P7"
|
||||
: "unaligned-storage-buffer-range@P7");
|
||||
if (size > m_wireMaxStorageRange) WireDescriptorFatal("storage-buffer-native-range@P7");
|
||||
if (size > m_wireMaxStorageRange) {
|
||||
// P7 A.3: clamp, do not refuse. The monolith arm binds whatever the GL range
|
||||
// asked for and never consults maxStorageBufferRange at all (see the resident
|
||||
// path below), so refusing here was the wire arm inventing a death the other
|
||||
// backend does not have. A clamped binding keeps every byte the device can
|
||||
// name reachable; past it the shader is out of the descriptor and
|
||||
// robustBufferAccess answers zero, which beats losing the draw outright.
|
||||
MGLOG_E_ONCE("ResolveStorageBufferDescriptor: block '%s' bound %llu bytes, past this device's "
|
||||
"maxStorageBufferRange of %llu; the binding is clamped to the limit",
|
||||
blockName.c_str(), static_cast<unsigned long long>(size),
|
||||
static_cast<unsigned long long>(m_wireMaxStorageRange));
|
||||
size = m_wireMaxStorageRange;
|
||||
}
|
||||
outBufferInfo = {slice.buffer, slice.offset + start, size};
|
||||
// Shader writes stay in the canonical server VkBuffer. The event marks the
|
||||
// client's shadow stale; neither this path nor later acquires seed it back.
|
||||
@@ -2362,9 +2374,25 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Uint32 blockIndex, Uint32 bindingPoint, UboBindResult& out) const {
|
||||
if (bindingPoint >= MG_Pipe::kMGPipeMaxBufferBindingPoints)
|
||||
WireDescriptorFatal("uniform-buffer-binding-point");
|
||||
const VkDeviceSize blockSize = program.GetUBOSizeAt(blockIndex);
|
||||
VkDeviceSize blockSize = program.GetUBOSizeAt(blockIndex);
|
||||
if (blockSize == 0) return false;
|
||||
if (blockSize > m_wireMaxUniformRange) WireDescriptorFatal("uniform-block-native-range@P7");
|
||||
if (blockSize > m_wireMaxUniformRange) {
|
||||
// P7 A.3: clamp, do not refuse. A conformant program cannot get here -
|
||||
// GL_MAX_UNIFORM_BLOCK_SIZE is published straight from this same
|
||||
// maxUniformBufferRange (BackendLoaders/Vulkan/Loader.cpp), so a block this big
|
||||
// fails to link long before a draw - which makes refusing it a Fatal for a shape
|
||||
// nothing can produce. If the two ever disagree, the honest answer is the bytes
|
||||
// the device CAN name: everything past the clamp is outside the descriptor and
|
||||
// reads back as zero under robustBufferAccess, which is inside GL's "undefined"
|
||||
// for a block the implementation never promised to hold. Clamping keeps the range
|
||||
// constant across draws, so the descriptor-set reuse hash is unaffected.
|
||||
MGLOG_E_ONCE("ResolveWireUniformBufferPayload: reflected uniform block %u is %llu bytes, past this "
|
||||
"device's maxUniformBufferRange of %llu; the binding is clamped to the limit and the "
|
||||
"remainder reads zero",
|
||||
blockIndex, static_cast<unsigned long long>(blockSize),
|
||||
static_cast<unsigned long long>(m_wireMaxUniformRange));
|
||||
blockSize = m_wireMaxUniformRange;
|
||||
}
|
||||
const auto& range = MG_Pipe::MGPipeApplier().BoundShaderBuffers[
|
||||
MG_Pipe::kMGPipeShaderBufferClassUniform][bindingPoint];
|
||||
BufferSlice source{};
|
||||
|
||||
Reference in New Issue
Block a user