mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Fix] (GLState, DirectVulkan): bust the texture-sync skip when a re-spec moved only the shape
This commit is contained in:
@@ -1494,6 +1494,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
const auto* mipTexture = MG_State::GLState::AsMipmapTexture(&texture);
|
const auto* mipTexture = MG_State::GLState::AsMipmapTexture(&texture);
|
||||||
const Uint32 mipLevelCount = mipTexture != nullptr ? mipTexture->GetMipmapLevelCount() : 0u;
|
const Uint32 mipLevelCount = mipTexture != nullptr ? mipTexture->GetMipmapLevelCount() : 0u;
|
||||||
return resource.syncedContentVersion != texture.GetContentVersion() ||
|
return resource.syncedContentVersion != texture.GetContentVersion() ||
|
||||||
|
resource.syncedShapeVersion != texture.GetShapeVersion() ||
|
||||||
resource.syncedTextureParamsVersion != texture.GetTextureParamsVersion() ||
|
resource.syncedTextureParamsVersion != texture.GetTextureParamsVersion() ||
|
||||||
resource.syncedMipLevelCount != mipLevelCount;
|
resource.syncedMipLevelCount != mipLevelCount;
|
||||||
}
|
}
|
||||||
@@ -1593,11 +1594,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Bool VkTextureManager::SyncTexture(MG_State::GLState::ITextureObject &texture,
|
Bool VkTextureManager::SyncTexture(MG_State::GLState::ITextureObject &texture,
|
||||||
TextureResource &outResource) {
|
TextureResource &outResource) {
|
||||||
// Cross-draw fast path: if the resource is already built and neither the texture's
|
// Cross-draw fast path: if the resource is already built and neither the texture's
|
||||||
// pixel content (bumped in MarkStorageDirty) nor its params changed since the last
|
// pixel content (bumped in MarkStorageDirty), its SHAPE (bumped in BumpShapeVersion)
|
||||||
// sync, there is nothing to re-check or re-upload - skip CheckMipmapCompleteness,
|
// nor its params changed since the last sync, there is nothing to re-check or
|
||||||
// SyncTextureResource, SyncTextureViews and the per-level dirty scan. Layout is
|
// re-upload - skip CheckMipmapCompleteness, SyncTextureResource, SyncTextureViews and
|
||||||
// maintained separately by the transition path, so the resource still reflects truth.
|
// the per-level dirty scan. Layout is maintained separately by the transition path, so
|
||||||
|
// the resource still reflects truth. The shape version is NOT redundant with the
|
||||||
|
// content one: glTexImage2D(..., nullptr) re-specifies a level's size or format
|
||||||
|
// without dirtying a texel, which is exactly how a re-specified image-unit texture used
|
||||||
|
// to keep reporting its old imageSize().
|
||||||
const Uint64 syncingContentVersion = texture.GetContentVersion();
|
const Uint64 syncingContentVersion = texture.GetContentVersion();
|
||||||
|
const Uint64 syncingShapeVersion = texture.GetShapeVersion();
|
||||||
const auto* syncingMipTexture = MG_State::GLState::AsMipmapTexture(&texture);
|
const auto* syncingMipTexture = MG_State::GLState::AsMipmapTexture(&texture);
|
||||||
const Uint32 syncingMipLevelCount =
|
const Uint32 syncingMipLevelCount =
|
||||||
syncingMipTexture != nullptr ? syncingMipTexture->GetMipmapLevelCount() : 0u;
|
syncingMipTexture != nullptr ? syncingMipTexture->GetMipmapLevelCount() : 0u;
|
||||||
@@ -1609,6 +1615,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_storageImageTextures.find(MakeTextureIdentity(&texture)) != m_storageImageTextures.end();
|
m_storageImageTextures.find(MakeTextureIdentity(&texture)) != m_storageImageTextures.end();
|
||||||
if (outResource.image != VK_NULL_HANDLE && !storageUpgradePending &&
|
if (outResource.image != VK_NULL_HANDLE && !storageUpgradePending &&
|
||||||
outResource.syncedContentVersion == syncingContentVersion &&
|
outResource.syncedContentVersion == syncingContentVersion &&
|
||||||
|
outResource.syncedShapeVersion == syncingShapeVersion &&
|
||||||
outResource.syncedTextureParamsVersion == texture.GetTextureParamsVersion() &&
|
outResource.syncedTextureParamsVersion == texture.GetTextureParamsVersion() &&
|
||||||
outResource.syncedMipLevelCount == syncingMipLevelCount) {
|
outResource.syncedMipLevelCount == syncingMipLevelCount) {
|
||||||
return true;
|
return true;
|
||||||
@@ -1660,6 +1667,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
if (!hasDirtyMipLevel) {
|
if (!hasDirtyMipLevel) {
|
||||||
outResource.syncedContentVersion = syncingContentVersion;
|
outResource.syncedContentVersion = syncingContentVersion;
|
||||||
outResource.syncedMipLevelCount = syncingMipLevelCount;
|
outResource.syncedMipLevelCount = syncingMipLevelCount;
|
||||||
|
outResource.syncedShapeVersion = syncingShapeVersion;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1669,6 +1677,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
outResource.syncedContentVersion = syncingContentVersion;
|
outResource.syncedContentVersion = syncingContentVersion;
|
||||||
outResource.syncedMipLevelCount = syncingMipLevelCount;
|
outResource.syncedMipLevelCount = syncingMipLevelCount;
|
||||||
|
outResource.syncedShapeVersion = syncingShapeVersion;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -206,6 +206,12 @@ public:
|
|||||||
// as defense-in-depth: any path that grows the level set (which resizes the sampled view)
|
// as defense-in-depth: any path that grows the level set (which resizes the sampled view)
|
||||||
// busts the skip even if it failed to bump the content version.
|
// busts the skip even if it failed to bump the content version.
|
||||||
Uint32 syncedMipLevelCount = 0;
|
Uint32 syncedMipLevelCount = 0;
|
||||||
|
// Snapshot of ITextureObject::GetShapeVersion() at the last successful sync. The content
|
||||||
|
// version alone does NOT cover a re-specification: glTexImage2D(..., nullptr) on an
|
||||||
|
// already-defined level changes its size or format and dirties no texel, so it moves the
|
||||||
|
// shape version and nothing else. Without this in the early-out key the image, its views
|
||||||
|
// and therefore imageSize() all keep answering with the texture's PREVIOUS shape.
|
||||||
|
Uint64 syncedShapeVersion = 0;
|
||||||
|
|
||||||
TextureResource() = default;
|
TextureResource() = default;
|
||||||
TextureResource(const TextureResource&) = delete;
|
TextureResource(const TextureResource&) = delete;
|
||||||
@@ -237,6 +243,7 @@ public:
|
|||||||
std::swap(this->lastRecordingGeneration, that.lastRecordingGeneration);
|
std::swap(this->lastRecordingGeneration, that.lastRecordingGeneration);
|
||||||
std::swap(this->syncedContentVersion, that.syncedContentVersion);
|
std::swap(this->syncedContentVersion, that.syncedContentVersion);
|
||||||
std::swap(this->syncedMipLevelCount, that.syncedMipLevelCount);
|
std::swap(this->syncedMipLevelCount, that.syncedMipLevelCount);
|
||||||
|
std::swap(this->syncedShapeVersion, that.syncedShapeVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reset() {
|
void Reset() {
|
||||||
@@ -300,6 +307,7 @@ public:
|
|||||||
syncedTextureParamsVersion = 0;
|
syncedTextureParamsVersion = 0;
|
||||||
syncedContentVersion = 0;
|
syncedContentVersion = 0;
|
||||||
syncedMipLevelCount = 0;
|
syncedMipLevelCount = 0;
|
||||||
|
syncedShapeVersion = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
~TextureResource() {
|
~TextureResource() {
|
||||||
|
|||||||
@@ -250,6 +250,10 @@ namespace MobileGL {
|
|||||||
return m_contentVersion;
|
return m_contentVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Uint64 TextureObjectBase::GetShapeVersion() const {
|
||||||
|
return m_shapeVersion;
|
||||||
|
}
|
||||||
|
|
||||||
Bool TextureObjectBase::IsMipmapCompleteForFilterCached(Bool mipmapped) const {
|
Bool TextureObjectBase::IsMipmapCompleteForFilterCached(Bool mipmapped) const {
|
||||||
const int slot = mipmapped ? 1 : 0;
|
const int slot = mipmapped ? 1 : 0;
|
||||||
if (m_completeMemoShapeVersion[slot] == m_shapeVersion) {
|
if (m_completeMemoShapeVersion[slot] == m_shapeVersion) {
|
||||||
|
|||||||
@@ -55,6 +55,12 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
// Backends compare it against a per-resource snapshot to skip re-syncing unchanged
|
// Backends compare it against a per-resource snapshot to skip re-syncing unchanged
|
||||||
// textures across draws (e.g. the block atlas bound across a whole terrain batch).
|
// textures across draws (e.g. the block atlas bound across a whole terrain batch).
|
||||||
virtual Uint64 GetContentVersion() const = 0;
|
virtual Uint64 GetContentVersion() const = 0;
|
||||||
|
// Monotonic counter bumped on every SHAPE mutation - level sizes, the stored level
|
||||||
|
// set, the internal format, the level range (see BumpShapeVersion). Disjoint from the
|
||||||
|
// content version on purpose: glTexImage2D(..., nullptr) re-specifies a level's size
|
||||||
|
// without dirtying a single texel, so a backend that keys its "nothing changed since
|
||||||
|
// the last sync" skip on content alone keeps a resource of the OLD size alive.
|
||||||
|
virtual Uint64 GetShapeVersion() const = 0;
|
||||||
// Answers IsMipmapCompleteForFilter() from a memo. Sampling completeness is a
|
// Answers IsMipmapCompleteForFilter() from a memo. Sampling completeness is a
|
||||||
// property of the texture's SHAPE - level sizes, level count, level range,
|
// property of the texture's SHAPE - level sizes, level count, level range,
|
||||||
// internal format - and never of its texel content, but every draw asks about
|
// internal format - and never of its texel content, but every draw asks about
|
||||||
@@ -106,6 +112,7 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
void SetImmutableLevels(Uint levels) override;
|
void SetImmutableLevels(Uint levels) override;
|
||||||
Uint16 GetTextureParamsVersion() const override;
|
Uint16 GetTextureParamsVersion() const override;
|
||||||
Uint64 GetContentVersion() const override;
|
Uint64 GetContentVersion() const override;
|
||||||
|
Uint64 GetShapeVersion() const override;
|
||||||
Bool IsMipmapCompleteForFilterCached(Bool mipmapped) const override;
|
Bool IsMipmapCompleteForFilterCached(Bool mipmapped) const override;
|
||||||
// Bumps the content version without touching per-level storage-dirty flags. Used when the
|
// Bumps the content version without touching per-level storage-dirty flags. Used when the
|
||||||
// set of defined mip levels grows via GPU-side mip generation (glGenerateMipmap): the level
|
// set of defined mip levels grows via GPU-side mip generation (glGenerateMipmap): the level
|
||||||
|
|||||||
Reference in New Issue
Block a user