mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 12:48:32 +09:00
[Fix] (Espryt): allocate texture storage from the pushed descriptor, honour a metadata-only respecify's sticky mask, and arm the server's own re-dirty in the applier's pending set instead of a model the handle arm never reads
This commit is contained in:
@@ -3825,6 +3825,67 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
}
|
||||
|
||||
Bool RearmPipeTextureLevelUpload(MG_Pipe::MGPipeHandle res, Uint16 packedTarget, Uint16 level,
|
||||
const MG_Pipe::MGPBox& wholeLevel) {
|
||||
// THE SERVER RE-DIRTYING THE CLIENT'S MODEL IS THE ONE DIRECTION D-D5's INVERSION DOES
|
||||
// NOT COVER, and this is the server-side answer to it (esprytobj review M-1).
|
||||
//
|
||||
// D-D5 moves the rect model and the emission cursor to the CLIENT, which clears its own
|
||||
// flags at emission - so on the handle arm IsStorageDirty is already false for every
|
||||
// level the applier accepted, and the upload loop reads the applier's pending set
|
||||
// instead. But three sites in this backend still write into the frontend dirty model,
|
||||
// and the pending set cannot see them:
|
||||
//
|
||||
// Managers.cpp RequireImageBindableStorage - re-dirties every defined level so the
|
||||
// widened image carrier is replayed rather than allocated empty (LIVE,
|
||||
// and it is this helper's only caller);
|
||||
// DirectGLES.cpp:7406 GenerateThreeChannelFloatMipmapOnCpu - same shape, milder
|
||||
// outcome (package E's file; E's verification round calls this helper
|
||||
// there, or states why not);
|
||||
// DirectGLES.cpp:6735 the CLEAR half (MarkStorageDirty(..., false)) after a
|
||||
// glGetTexImage shadow refresh - it clears rather than dirties, so
|
||||
// nothing is owed and there is nothing to re-arm.
|
||||
//
|
||||
// The alternative the review offered - OR the frontend flag back into the per-level
|
||||
// question - was NOT taken: it would put two authorities back on the handle arm, and
|
||||
// "the server never clears the client's flag" (D-D5) would then have to grow an
|
||||
// exception for the levels the OR consumed. Re-arming the set keeps ONE authority; the
|
||||
// consume path is unchanged and clears exactly what it uploaded.
|
||||
//
|
||||
// The entry is WHOLE-LEVEL and carries no regions (RegionCount 0 = "the union box is the
|
||||
// whole story", D-D3), because a replay owes every texel of the level, and it MERGES
|
||||
// with an entry the client already emitted rather than duplicating it - a whole-level
|
||||
// box subsumes any scatter behind it. Returns false, loudly, when the set is at its
|
||||
// bound: a dropped replay is the bug this exists to stop, so it must not be silent.
|
||||
auto& applier = MG_Pipe::MGPipeApplier();
|
||||
if (MG_Pipe::MGPipeHandleIsNull(res) || res.Slot >= applier.TextureResources.size()) return false;
|
||||
auto& record = applier.TextureResources[res.Slot];
|
||||
if (!record.Live || record.Gen != res.Gen) return false;
|
||||
const Uint8 uploadHalf = MG_Pipe::MGPipeSubDataUploadTargetOf(packedTarget);
|
||||
for (auto& pending : record.PendingUploads) {
|
||||
if (MG_Pipe::MGPipeSubDataUploadTargetOf(pending.UploadTarget) != uploadHalf ||
|
||||
pending.Level != level) {
|
||||
continue;
|
||||
}
|
||||
pending.UnionBox = wholeLevel;
|
||||
pending.Regions.clear();
|
||||
return true;
|
||||
}
|
||||
if (record.PendingUploads.size() >= MG_Pipe::kMGPipeMaxPendingUploads) {
|
||||
MGLOG_E_ONCE("MGPipe: texture handle {%u, %u} already holds the applier's %u pending "
|
||||
"uploads, so the server's own re-dirty of (uploadTarget=%u, level=%u) "
|
||||
"cannot be armed - that level will not be replayed",
|
||||
res.Slot, res.Gen, MG_Pipe::kMGPipeMaxPendingUploads, uploadHalf, level);
|
||||
return false;
|
||||
}
|
||||
MG_Pipe::MGPipeResourceRecord::PendingUpload entry;
|
||||
entry.UploadTarget = packedTarget;
|
||||
entry.Level = level;
|
||||
entry.UnionBox = wholeLevel;
|
||||
record.PendingUploads.push_back(std::move(entry));
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace SamplerViewImpl {
|
||||
BackendSamplerViewTable g_backendSamplerViews;
|
||||
|
||||
@@ -5166,6 +5227,30 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// split-only arm and the monolith body of MGPipeUnmigratedEmulation is a no-op.
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
MG_Pipe::MGPipeUnmigratedEmulation("texture-remint-pull");
|
||||
#endif
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// P4a (review M-1): ON THE HANDLE ARM THE RE-DIRTY HAS TO REACH THE APPLIER'S SET,
|
||||
// not only the frontend's model. The client cleared its own flags when it emitted
|
||||
// those levels, so MarkStorageDirty below re-arms a model the handle arm does not
|
||||
// read: the pending set would stay empty, MGB_LEVEL_NEEDS_UPLOAD would answer false
|
||||
// for every level, and the widened image carrier this transition schedules would be
|
||||
// allocated EMPTY - the very bug the paragraph above says "survived so long",
|
||||
// re-introduced one arm over. The dispatch that triggered the bind would read zeroes.
|
||||
//
|
||||
// Both models are written, not one: the frontend's because the legacy arm reads it
|
||||
// and because the client's own NoteLevelDirty hook rides on it, the applier's
|
||||
// because that is what this arm consumes.
|
||||
const MG_Pipe::MGPipeHandle rearmRes =
|
||||
TextureResourceSubsystemEnabled() ? g_backendTextureObjects.HandleOf(stateTextureObject.get())
|
||||
: MG_Pipe::kMGPipeNullHandle;
|
||||
const Uint32 rearmResourceTarget =
|
||||
MG_Pipe::MGPipeResourceTargetForTextureTarget(stateTextureObject->GetTarget());
|
||||
if (TextureResourceSubsystemEnabled() && MG_Pipe::MGPipeHandleIsNull(rearmRes)) {
|
||||
MGLOG_E_ONCE("MGPipe: texture %u needs image-bindable storage but has no handle on the "
|
||||
"handle arm, so the levels it owes cannot be re-armed in the applier's "
|
||||
"pending set - they would be allocated empty",
|
||||
stateTextureObject->GetExternalIndex());
|
||||
}
|
||||
#endif
|
||||
if (auto* mipmapObject = MG_State::GLState::AsMipmapTexture(stateTextureObject.get())) {
|
||||
const auto levelCount = mipmapObject->GetMipmapLevelCount();
|
||||
@@ -5175,6 +5260,21 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (levelTexelSize.x() <= 0 || levelTexelSize.y() <= 0) continue;
|
||||
if (mipmapObject->GetMipmapByteSize(uploadTarget, level) == 0) continue;
|
||||
mipmapObject->MarkStorageDirty(uploadTarget, level, true);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
if (!MG_Pipe::MGPipeHandleIsNull(rearmRes)) {
|
||||
const MG_Pipe::MGPBox wholeLevel{0,
|
||||
0,
|
||||
0,
|
||||
static_cast<Uint32>(levelTexelSize.x()),
|
||||
static_cast<Uint32>(levelTexelSize.y()),
|
||||
static_cast<Uint32>(std::max(levelTexelSize.z(), 1))};
|
||||
RearmPipeTextureLevelUpload(
|
||||
rearmRes,
|
||||
MG_Pipe::MGPipePackSubDataTarget(rearmResourceTarget,
|
||||
static_cast<Uint32>(uploadTarget)),
|
||||
static_cast<Uint16>(level), wholeLevel);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6111,6 +6211,57 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
#else
|
||||
#define MGB_LEVEL_NEEDS_UPLOAD(obj, tgt, lvl) ((obj)->IsStorageDirty(tgt, lvl))
|
||||
#define MGB_LEVEL_UPLOAD_DONE(obj, tgt, lvl) (obj)->MarkStorageDirty(tgt, lvl, false)
|
||||
#endif
|
||||
|
||||
// THE STORAGE SHAPE, and on the handle arm it is the DESCRIPTOR's (C.3's d2: "the
|
||||
// m_prevTextureInfo probe re-keyed onto the resource record's Serial AND Desc"; review
|
||||
// M-2). v1 moved the parameters and the uploads and left the allocation reading the
|
||||
// frontend object, which put TWO AUTHORITIES on one value inside one twin -
|
||||
// SyncTextureParamsToBackend already answers MGB_TEXPARAM_FORMAT from
|
||||
// Desc.InternalFormat while this function allocated from GetFormat(). In monolith they
|
||||
// agree, so no lane can expose the split; under split the descriptor is all there is.
|
||||
//
|
||||
// WHAT THE DESCRIPTOR ANSWERS: internal format, base extent, level count, sample count,
|
||||
// fixed sample locations, immutability, storage kind and the buffer-texture window. What
|
||||
// it does NOT answer, and what still comes from the frontend shadow, is the PER-LEVEL
|
||||
// extent and the level BYTES - MGPResourceDesc carries one base extent, the per-level
|
||||
// sizes are derived from it, and the texels themselves ride on sub-data. A level the
|
||||
// descriptor's Levels claims and the shadow has not defined reads back {0,0,0} and the
|
||||
// per-level loops skip it, which is the same arm they take for a sparse chain today.
|
||||
//
|
||||
// MACROS, NOT LOCALS, FOR THE SAME REASON AS THE PAIR ABOVE (DV-9, and it was measured):
|
||||
// routing these through locals resized SyncTextureParamsToBackend by +9 bytes in the
|
||||
// PULL build and P4a's admitted-resize set is EMPTY. Each takes the object it would
|
||||
// otherwise have been spelled on, so the pull expansion is the pre-P4a expression with
|
||||
// one pair of parentheses around the receiver. All are #undef'd with the pair above.
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
#define MGB_STORAGE_FORMAT(obj) \
|
||||
(pushedStorage != nullptr ? static_cast<TextureInternalFormat>(pushedStorage->Desc.InternalFormat) \
|
||||
: (obj)->GetFormat())
|
||||
#define MGB_STORAGE_BASE_SIZE(obj) \
|
||||
(pushedStorage != nullptr ? IntVec3{static_cast<Int>(pushedStorage->Desc.Width), \
|
||||
static_cast<Int>(pushedStorage->Desc.Height), \
|
||||
static_cast<Int>(pushedStorage->Desc.Depth)} \
|
||||
: (obj)->GetBaseSize())
|
||||
#define MGB_STORAGE_LEVELS(obj) \
|
||||
(pushedStorage != nullptr ? static_cast<Uint>(pushedStorage->Desc.Levels) : (obj)->GetMipmapLevelCount())
|
||||
#define MGB_STORAGE_SAMPLES(obj) \
|
||||
(pushedStorage != nullptr ? static_cast<Int>(pushedStorage->Desc.Samples) : (obj)->GetSamples())
|
||||
#define MGB_STORAGE_FIXED_SAMPLE_LOCATIONS(obj) \
|
||||
(pushedStorage != nullptr ? pushedStorage->Desc.FixedSampleLocations != 0 : (obj)->HasFixedSampleLocations())
|
||||
#define MGB_STORAGE_IMMUTABLE(obj) \
|
||||
(pushedStorage != nullptr ? pushedStorage->Desc.Immutable != 0 : (obj)->IsImmutable())
|
||||
#define MGB_STORAGE_KIND(obj) \
|
||||
(pushedStorage != nullptr ? static_cast<TextureStorageType>(pushedStorage->Desc.StorageKind) \
|
||||
: (obj)->GetStorageType())
|
||||
#else
|
||||
#define MGB_STORAGE_FORMAT(obj) ((obj)->GetFormat())
|
||||
#define MGB_STORAGE_BASE_SIZE(obj) ((obj)->GetBaseSize())
|
||||
#define MGB_STORAGE_LEVELS(obj) ((obj)->GetMipmapLevelCount())
|
||||
#define MGB_STORAGE_SAMPLES(obj) ((obj)->GetSamples())
|
||||
#define MGB_STORAGE_FIXED_SAMPLE_LOCATIONS(obj) ((obj)->HasFixedSampleLocations())
|
||||
#define MGB_STORAGE_IMMUTABLE(obj) ((obj)->IsImmutable())
|
||||
#define MGB_STORAGE_KIND(obj) ((obj)->GetStorageType())
|
||||
#endif
|
||||
|
||||
void BackendTextureObject::SyncMipmapsToBackend(
|
||||
@@ -6156,6 +6307,32 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
stateTextureObject->GetExternalIndex(), pushedRes.Slot, pushedRes.Gen);
|
||||
return;
|
||||
}
|
||||
// THE METADATA RESPECIFY, HONOURED (ID-18 M4, review M-2). BindMask and
|
||||
// ImageBindableHint are STICKY facts the client discovers AFTER allocation - a
|
||||
// texture first bound as a shader image - and an IMMUTABLE texture never has a
|
||||
// later redefinition to carry them, so they arrive on a respecify that restates
|
||||
// the storage the resource already has. The applier classifies such a record as a
|
||||
// metadata update: no reallocation ack, no pending-upload clear, Serial moved.
|
||||
// THE TWIN'S HALF IS TO RE-DERIVE ITS STORAGE FLAGS FROM THE NEW MASK AND
|
||||
// RECREATE ONLY WHERE THE BACKEND NEEDS THE FLAG AT CREATION - and image
|
||||
// bindability is exactly such a flag on this backend: the carrier may be channel-
|
||||
// widened, which is a different allocation, not a different binding.
|
||||
//
|
||||
// RequireImageBindableStorage IS that path and it is idempotent, so this is the
|
||||
// whole of the re-derivation: it sets the sticky flag, clears m_isInitialized so
|
||||
// the storage below is regenerated, re-arms every defined level in BOTH dirty
|
||||
// models (see its own comment) so the widened carrier is replayed rather than
|
||||
// allocated empty, and forces the parameter resync the widening's swizzle needs.
|
||||
// Arriving on the CREATE instead - the common case once B publishes the hint -
|
||||
// costs nothing: m_isInitialized is already false and there are no levels to
|
||||
// replay.
|
||||
const Bool pushedWantsImageBindableStorage =
|
||||
pushedStorage->Desc.ImageBindableHint != 0 ||
|
||||
(pushedStorage->Desc.BindMask & MG_Pipe::kMGPipeBindShaderImage) != 0;
|
||||
if (pushedWantsImageBindableStorage && !m_imageBindableStorageRequired) {
|
||||
RequireImageBindableStorage(stateTextureObject);
|
||||
}
|
||||
|
||||
// ONE compare replaces the whole cheap-gate trio AND the content version. The
|
||||
// applier bumps Serial on every respecify and every sub-data it applies to this
|
||||
// resource, which is exactly the union those four keys covered - without the
|
||||
@@ -6282,20 +6459,20 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) {
|
||||
MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str());
|
||||
});
|
||||
const auto baseSize = stateTextureObject->GetBaseSize();
|
||||
StateTextureBasicInfo currentTextureInfo = {stateTextureObject->GetFormat(),
|
||||
const auto baseSize = MGB_STORAGE_BASE_SIZE(stateTextureObject);
|
||||
StateTextureBasicInfo currentTextureInfo = {MGB_STORAGE_FORMAT(stateTextureObject),
|
||||
static_cast<SizeT>(baseSize.x()),
|
||||
static_cast<SizeT>(baseSize.y()),
|
||||
static_cast<SizeT>(baseSize.z()),
|
||||
0,
|
||||
0,
|
||||
stateTextureObject->GetSamples(),
|
||||
stateTextureObject->HasFixedSampleLocations()};
|
||||
switch (stateTextureObject->GetStorageType()) {
|
||||
MGB_STORAGE_SAMPLES(stateTextureObject),
|
||||
MGB_STORAGE_FIXED_SAMPLE_LOCATIONS(stateTextureObject)};
|
||||
switch (MGB_STORAGE_KIND(stateTextureObject)) {
|
||||
case TextureStorageType::Mipmap: {
|
||||
auto* textureMipmapObject =
|
||||
static_cast<MG_State::GLState::TextureObjectMipmap*>(stateTextureObject.get());
|
||||
const auto mipmapCount = textureMipmapObject->GetMipmapLevelCount();
|
||||
const auto mipmapCount = MGB_STORAGE_LEVELS(textureMipmapObject);
|
||||
currentTextureInfo.mipmapLevels = mipmapCount;
|
||||
|
||||
Bool needsRegeneration = !m_isInitialized || (currentTextureInfo != m_prevTextureInfo);
|
||||
@@ -6310,13 +6487,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// TextureImpl::GetImageBindableStorageWidening for what widens and why.
|
||||
const TextureImpl::ImageBindableStorageWidening imageWidening =
|
||||
m_imageBindableStorageRequired
|
||||
? TextureImpl::GetImageBindableStorageWidening(textureMipmapObject->GetFormat())
|
||||
? TextureImpl::GetImageBindableStorageWidening(MGB_STORAGE_FORMAT(textureMipmapObject))
|
||||
: TextureImpl::ImageBindableStorageWidening{};
|
||||
|
||||
const Bool canAppendMipmaps =
|
||||
m_isInitialized &&
|
||||
!m_imageBindableStorageRequired &&
|
||||
!stateTextureObject->IsImmutable() &&
|
||||
!MGB_STORAGE_IMMUTABLE(stateTextureObject) &&
|
||||
currentTextureInfo.internalFormat == m_prevTextureInfo.internalFormat &&
|
||||
currentTextureInfo.width == m_prevTextureInfo.width &&
|
||||
currentTextureInfo.height == m_prevTextureInfo.height &&
|
||||
@@ -6329,14 +6506,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
|
||||
MGLOG_D("%s: Got texture info: %dx%dx%d, mips %d, format %s", __func__, baseSize.x(), baseSize.y(),
|
||||
baseSize.z(), mipmapCount,
|
||||
MG_Util::ConvertTextureInternalFormatToString(textureMipmapObject->GetFormat()).c_str());
|
||||
MG_Util::ConvertTextureInternalFormatToString(MGB_STORAGE_FORMAT(textureMipmapObject)).c_str());
|
||||
|
||||
if (canAppendMipmaps) {
|
||||
MGLOG_D("Texture mip count increased for backend ID %u, appending levels %zu..%zu",
|
||||
m_backendTextureId, m_prevTextureInfo.mipmapLevels, mipmapCount - 1);
|
||||
|
||||
GLenum glInternalFormat, glType, glFormat;
|
||||
TextureImpl::GenerateTextureFormatInfo(textureMipmapObject->GetFormat(), &glInternalFormat,
|
||||
TextureImpl::GenerateTextureFormatInfo(MGB_STORAGE_FORMAT(textureMipmapObject), &glInternalFormat,
|
||||
&glFormat, &glType, targetInternal);
|
||||
|
||||
const auto& uploadTargets = textureMipmapObject->GetUploadTargets();
|
||||
@@ -6360,10 +6537,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
Vector<Float> convertedUploadData;
|
||||
Vector<Uint8> widenedUploadData;
|
||||
const void* uploadData = PrepareFallbackUpload(
|
||||
textureMipmapObject->GetFormat(), targetInternal, levelTexelSize, pData,
|
||||
MGB_STORAGE_FORMAT(textureMipmapObject), targetInternal, levelTexelSize, pData,
|
||||
levelByteSize, glType, convertedUploadData, widenedUploadData);
|
||||
Vector<Uint8> packedUploadData;
|
||||
uploadData = PreparePackedNormUpload(textureMipmapObject->GetFormat(), levelTexelSize,
|
||||
uploadData = PreparePackedNormUpload(MGB_STORAGE_FORMAT(textureMipmapObject), levelTexelSize,
|
||||
uploadData, levelByteSize, &glType, packedUploadData);
|
||||
|
||||
DebugImpl::ErrorLopper::Clear();
|
||||
@@ -6416,7 +6593,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
|
||||
// Regenerate all mipmap levels
|
||||
GLenum glInternalFormat, glType, glFormat;
|
||||
TextureImpl::GenerateTextureFormatInfo(textureMipmapObject->GetFormat(), &glInternalFormat,
|
||||
TextureImpl::GenerateTextureFormatInfo(MGB_STORAGE_FORMAT(textureMipmapObject), &glInternalFormat,
|
||||
&glFormat, &glType, targetInternal);
|
||||
ApplyImageBindableStorageWidening(imageWidening, &glInternalFormat, &glFormat, &glType);
|
||||
|
||||
@@ -6430,8 +6607,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// stateTextureObject keeps the requested count so GL_TEXTURE_SAMPLES and
|
||||
// framebuffer completeness still report what the application asked for.
|
||||
const auto backendSamples = static_cast<GLsizei>(ClampSamplesToBackendSupport(
|
||||
GetFormatCapabilityTargetIndex(targetInternal), textureMipmapObject->GetFormat(),
|
||||
glFormat, static_cast<Int>(stateTextureObject->GetSamples())));
|
||||
GetFormatCapabilityTargetIndex(targetInternal), MGB_STORAGE_FORMAT(textureMipmapObject),
|
||||
glFormat, static_cast<Int>(MGB_STORAGE_SAMPLES(stateTextureObject))));
|
||||
// ES 3.1 8.19 requires width/height (and depth, for the array target) >= 1,
|
||||
// so a degenerate size has nothing to allocate and must not reach the
|
||||
// driver. The frontend deallocates such an image rather than defining it
|
||||
@@ -6449,14 +6626,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_GLESFuncs.glTexStorage2DMultisample(
|
||||
target, backendSamples, glInternalFormat,
|
||||
static_cast<GLsizei>(baseSize.x()), static_cast<GLsizei>(baseSize.y()),
|
||||
stateTextureObject->HasFixedSampleLocations() ? GL_TRUE : GL_FALSE);
|
||||
MGB_STORAGE_FIXED_SAMPLE_LOCATIONS(stateTextureObject) ? GL_TRUE : GL_FALSE);
|
||||
break;
|
||||
case TextureTarget::Texture2DMultisampleArray:
|
||||
g_GLESFuncs.glTexStorage3DMultisample(
|
||||
target, backendSamples, glInternalFormat,
|
||||
static_cast<GLsizei>(baseSize.x()), static_cast<GLsizei>(baseSize.y()),
|
||||
static_cast<GLsizei>(baseSize.z()),
|
||||
stateTextureObject->HasFixedSampleLocations() ? GL_TRUE : GL_FALSE);
|
||||
MGB_STORAGE_FIXED_SAMPLE_LOCATIONS(stateTextureObject) ? GL_TRUE : GL_FALSE);
|
||||
break;
|
||||
default:
|
||||
MOBILEGL_ASSERT(false, "Unexpected multisample target: %d",
|
||||
@@ -6482,7 +6659,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
MGB_LEVEL_UPLOAD_DONE(textureMipmapObject, uploadTarget, level);
|
||||
}
|
||||
}
|
||||
} else if (stateTextureObject->IsImmutable() || m_imageBindableStorageRequired) {
|
||||
} else if (MGB_STORAGE_IMMUTABLE(stateTextureObject) || m_imageBindableStorageRequired) {
|
||||
DebugImpl::ErrorLopper::Clear();
|
||||
BufferImpl::BindPixelUnpackBufferId(0); // no-op once the resting 0 state is pinned
|
||||
const IntVec3 storageSize = GetBackendUploadSize(targetInternal, baseSize);
|
||||
@@ -6528,11 +6705,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
Vector<Float> convertedUploadData;
|
||||
Vector<Uint8> widenedUploadData;
|
||||
const void* uploadData = PrepareFallbackUpload(
|
||||
textureMipmapObject->GetFormat(), targetInternal, levelTexelSize, pData,
|
||||
MGB_STORAGE_FORMAT(textureMipmapObject), targetInternal, levelTexelSize, pData,
|
||||
levelByteSize, glType, convertedUploadData, widenedUploadData);
|
||||
Vector<Uint8> packedUploadData;
|
||||
uploadData =
|
||||
PreparePackedNormUpload(textureMipmapObject->GetFormat(), levelTexelSize,
|
||||
PreparePackedNormUpload(MGB_STORAGE_FORMAT(textureMipmapObject), levelTexelSize,
|
||||
uploadData, levelByteSize, &glType, packedUploadData);
|
||||
Vector<Uint8> imageWidenedUploadData;
|
||||
uploadData = PrepareImageWidenedUpload(imageWidening, levelTexelSize, uploadData,
|
||||
@@ -6598,11 +6775,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
Vector<Float> convertedUploadData;
|
||||
Vector<Uint8> widenedUploadData;
|
||||
const void* uploadData = PrepareFallbackUpload(
|
||||
textureMipmapObject->GetFormat(), targetInternal, levelTexelSize, pData,
|
||||
MGB_STORAGE_FORMAT(textureMipmapObject), targetInternal, levelTexelSize, pData,
|
||||
levelByteSize, glType, convertedUploadData, widenedUploadData);
|
||||
Vector<Uint8> packedUploadData;
|
||||
uploadData =
|
||||
PreparePackedNormUpload(textureMipmapObject->GetFormat(), levelTexelSize,
|
||||
PreparePackedNormUpload(MGB_STORAGE_FORMAT(textureMipmapObject), levelTexelSize,
|
||||
uploadData, levelByteSize, &glType, packedUploadData);
|
||||
MGLOG_D("%s: target: %s: syncing mip %d: %dx%dx%d, byteSize = %d, pData = %p, "
|
||||
"levelDirty = %s",
|
||||
@@ -6672,9 +6849,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto mipmapCount = textureMipmapObject->GetMipmapLevelCount();
|
||||
const auto mipmapCount = MGB_STORAGE_LEVELS(textureMipmapObject);
|
||||
GLenum glInternalFormat, glType, glFormat;
|
||||
TextureImpl::GenerateTextureFormatInfo(textureMipmapObject->GetFormat(), &glInternalFormat,
|
||||
TextureImpl::GenerateTextureFormatInfo(MGB_STORAGE_FORMAT(textureMipmapObject), &glInternalFormat,
|
||||
&glFormat, &glType, targetInternal);
|
||||
// The storage this level is being written into was widened when it was minted
|
||||
// (see above), so the transfer pair has to describe the carrier here too - ES
|
||||
@@ -6714,10 +6891,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
Vector<Float> convertedUploadData;
|
||||
Vector<Uint8> widenedUploadData;
|
||||
const void* uploadData = PrepareFallbackUpload(
|
||||
textureMipmapObject->GetFormat(), targetInternal, texelSize, mipData, byteSize,
|
||||
MGB_STORAGE_FORMAT(textureMipmapObject), targetInternal, texelSize, mipData, byteSize,
|
||||
glType, convertedUploadData, widenedUploadData);
|
||||
Vector<Uint8> packedUploadData;
|
||||
uploadData = PreparePackedNormUpload(textureMipmapObject->GetFormat(), texelSize,
|
||||
uploadData = PreparePackedNormUpload(MGB_STORAGE_FORMAT(textureMipmapObject), texelSize,
|
||||
uploadData, byteSize, &glType, packedUploadData);
|
||||
// Leaves `uploadData` pointing at its own buffer when it fires, which
|
||||
// is exactly what takes the sub-rect fast path below out of play: that
|
||||
@@ -7088,13 +7265,45 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
stateTextureObject->GetExternalIndex());
|
||||
return;
|
||||
}
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// M-2: THE BUFFER-TEXTURE FIELDS ARE THE DESCRIPTOR'S TOO - BufferForTexBuffer
|
||||
// names the backing store and BufOffset/BufSize name the window. The frontend
|
||||
// binding slot above is still what hands over the BufferObject, because
|
||||
// EnsureBufferResourceForHandle takes it as the one question P3a's record cannot
|
||||
// answer (an emulated persistent map, D-A4's `frontend` argument) - but WHICH
|
||||
// buffer, and which window of it, are read from the record.
|
||||
MG_Pipe::MGPipeHandle pushedTexBuffer = MG_Pipe::kMGPipeNullHandle;
|
||||
if (pushedStorage != nullptr) {
|
||||
pushedTexBuffer = pushedStorage->Desc.BufferForTexBuffer;
|
||||
if (MG_Pipe::MGPipeHandleIsNull(pushedTexBuffer)) {
|
||||
MGLOG_E_ONCE("MGPipe: buffer texture %u's descriptor names no backing buffer on "
|
||||
"the handle arm, so it cannot be backed (handle {%u, %u})",
|
||||
stateTextureObject->GetExternalIndex(), pushedRes.Slot, pushedRes.Gen);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
auto bufferIndex = buffer->GetExternalIndex();
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// The memo key is the HANDLE's slot on the handle arm: a GL name is never an
|
||||
// identity (section 4.2.1), and the record's Serial covers a slot recycled at a
|
||||
// new generation because a respecify moves it.
|
||||
currentTextureInfo.bufferExternalIndex =
|
||||
pushedStorage != nullptr ? static_cast<Uint>(pushedTexBuffer.Slot) : bufferIndex;
|
||||
#else
|
||||
currentTextureInfo.bufferExternalIndex = bufferIndex;
|
||||
#endif
|
||||
|
||||
Bool needsRegeneration = !m_isInitialized || (currentTextureInfo != m_prevTextureInfo);
|
||||
|
||||
// Need to sync texture buffer if not synced yet
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
auto* backendBufferResource =
|
||||
pushedStorage != nullptr ? BufferImpl::EnsureBufferResourceForHandle(buffer, pushedTexBuffer)
|
||||
: BufferImpl::EnsureBufferResource(buffer);
|
||||
#else
|
||||
auto* backendBufferResource = BufferImpl::EnsureBufferResource(buffer);
|
||||
#endif
|
||||
if (!backendBufferResource || backendBufferResource->id == 0) {
|
||||
MGLOG_E_ONCE("Failed to sync backing buffer for texture buffer with ID: %u",
|
||||
stateTextureObject->GetExternalIndex());
|
||||
@@ -7105,7 +7314,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
auto backendId = backendBufferResource->id;
|
||||
|
||||
GLenum glInternalFormat, glType, glFormat;
|
||||
TextureImpl::GenerateTextureFormatInfo(textureBufferObject->GetFormat(), &glInternalFormat, &glFormat,
|
||||
TextureImpl::GenerateTextureFormatInfo(MGB_STORAGE_FORMAT(textureBufferObject), &glInternalFormat, &glFormat,
|
||||
&glType, TextureTarget::TextureBuffer);
|
||||
// The view half of the buffer-image SPLIT. A buffer texture has no storage of its
|
||||
// own to widen, but the VIEW its format describes can be re-described one
|
||||
@@ -7122,7 +7331,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// IMAGE binding needs the split, so only the image binding's name carries it.
|
||||
const GLenum bufferImageSplitFormat =
|
||||
m_imageBindableStorageRequired
|
||||
? TextureImpl::GetImageBindableBufferSplitFormat(textureBufferObject->GetFormat())
|
||||
? TextureImpl::GetImageBindableBufferSplitFormat(MGB_STORAGE_FORMAT(textureBufferObject))
|
||||
: GL_UNKNOWN_MGL;
|
||||
|
||||
if (needsRegeneration) {
|
||||
@@ -7157,8 +7366,25 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// whole-buffer forms report offset 0 and the buffer's current size, which
|
||||
// glTexBuffer expresses more directly (and works where the range entry point
|
||||
// is absent).
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// The WINDOW is carried: MGPResourceDesc::BufOffset / BufSize, with
|
||||
// kMGPipeWholeBuffer (~0) meaning "the whole store, resolved live" - which is
|
||||
// why the whole-buffer arm still asks the BUFFER for its size. That is the
|
||||
// buffer family's own object (P3a's, not this record's), and resolving it
|
||||
// here is exactly what the sentinel is documented to mean.
|
||||
const SizeT rangeOffset = pushedStorage != nullptr
|
||||
? static_cast<SizeT>(pushedStorage->Desc.BufOffset)
|
||||
: textureBufferObject->GetBufferRangeOffset();
|
||||
const SizeT rangeSize =
|
||||
pushedStorage == nullptr
|
||||
? textureBufferObject->GetBufferRangeSizeInBytes()
|
||||
: (pushedStorage->Desc.BufSize == MG_Pipe::kMGPipeWholeBuffer
|
||||
? buffer->GetSize()
|
||||
: static_cast<SizeT>(pushedStorage->Desc.BufSize));
|
||||
#else
|
||||
const SizeT rangeOffset = textureBufferObject->GetBufferRangeOffset();
|
||||
const SizeT rangeSize = textureBufferObject->GetBufferRangeSizeInBytes();
|
||||
#endif
|
||||
// Through CallTexBuffer/CallTexBufferRange rather than g_GLESFuncs directly:
|
||||
// the unsuffixed entry points are the ES 3.2 core spelling, and a driver
|
||||
// whose buffer textures come from EXT/OES_texture_buffer exports the
|
||||
@@ -7261,6 +7487,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
#undef MGB_LEVEL_NEEDS_UPLOAD
|
||||
#undef MGB_LEVEL_UPLOAD_DONE
|
||||
#undef MGB_STORAGE_FORMAT
|
||||
#undef MGB_STORAGE_BASE_SIZE
|
||||
#undef MGB_STORAGE_LEVELS
|
||||
#undef MGB_STORAGE_SAMPLES
|
||||
#undef MGB_STORAGE_FIXED_SAMPLE_LOCATIONS
|
||||
#undef MGB_STORAGE_IMMUTABLE
|
||||
#undef MGB_STORAGE_KIND
|
||||
#undef MGB_STORAGE_TEXBUFFER_HANDLE
|
||||
#undef MGB_STORAGE_TEXBUFFER_OFFSET
|
||||
#undef MGB_STORAGE_TEXBUFFER_SIZE
|
||||
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
const SamplerParameters* BackendTextureObject::ResolvePushedBuiltinSampler(
|
||||
|
||||
@@ -698,9 +698,23 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// target refreshes and skips - that today leave the frontend flag set. A naive move of the
|
||||
// clear to the client would lose exactly those texels. The set survives any number of
|
||||
// bails; ConsumePipeTextureUpload below is called ONLY where the level actually uploaded.
|
||||
//
|
||||
// `uploadTarget` is static_cast<Uint16>(MobileGL::TextureUploadTarget) - the HALF, not the
|
||||
// packed field. The stored key is MGPSubData::Target whole (low byte MGPipeResourceTarget,
|
||||
// high byte TextureUploadTarget, ID-12) and both functions decode it with
|
||||
// MGPipeSubDataUploadTargetOf; they are the only two places this package compares it.
|
||||
const MG_Pipe::MGPipeResourceRecord::PendingUpload* FindPipeTextureUpload(
|
||||
const MG_Pipe::MGPipeResourceRecord& record, Uint16 uploadTarget, Uint16 level);
|
||||
void ConsumePipeTextureUpload(MG_Pipe::MGPipeHandle res, Uint16 uploadTarget, Uint16 level);
|
||||
|
||||
// THE SERVER'S OWN RE-DIRTY, armed in the applier's set instead of in the frontend's model
|
||||
// (esprytobj review M-1). `packedTarget` is a full MGPSubData::Target built with
|
||||
// MGPipePackSubDataTarget, because the entry this writes has to be indistinguishable from
|
||||
// one the client emitted. Whole-level, no regions, merged with any entry already there;
|
||||
// false (and one named log line) when the applier's pending set is at its bound. The three
|
||||
// server-side MarkStorageDirty sites are enumerated at the definition.
|
||||
Bool RearmPipeTextureLevelUpload(MG_Pipe::MGPipeHandle res, Uint16 packedTarget, Uint16 level,
|
||||
const MG_Pipe::MGPBox& wholeLevel);
|
||||
#endif
|
||||
|
||||
namespace BufferImpl {
|
||||
|
||||
Reference in New Issue
Block a user