mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
[Fix] (MG_Backend/DirectVulkan, MG_State/TextureState, MG_Test): harden default-fbo clear lifetime tracking
This commit is contained in:
@@ -17,6 +17,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
target <= TextureUploadTarget::CubeMapNegativeZ;
|
||||
}
|
||||
|
||||
static Bool PendingClearMatchesTextureIdentity(const PendingClearKey& key, const TextureIdentity& identity) {
|
||||
return key.texture == identity.texture && key.textureLifetimeId == identity.lifetimeId;
|
||||
}
|
||||
|
||||
static Uint32 ResolveAttachmentBaseArrayLayer(TextureUploadTarget target) {
|
||||
if (!IsCubeMapFaceUploadTarget(target)) {
|
||||
return 0;
|
||||
@@ -42,6 +46,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Uint32 baseArrayLayer, Uint32 layerCount) {
|
||||
return PendingClearKey {
|
||||
.texture = texture,
|
||||
.textureLifetimeId = texture ? texture->GetLifetimeId() : 0,
|
||||
.mipLevel = mipLevel,
|
||||
.baseArrayLayer = baseArrayLayer,
|
||||
.layerCount = layerCount,
|
||||
@@ -66,7 +71,74 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
void VkClearManager::Shutdown() {
|
||||
const std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_pendingClears.clear();
|
||||
m_aliveObjects.clear();
|
||||
}
|
||||
|
||||
TextureIdentity VkClearManager::MakeTextureIdentity(MG_State::GLState::ITextureObject* texture) {
|
||||
return TextureIdentity {
|
||||
.texture = texture,
|
||||
.lifetimeId = texture ? texture->GetLifetimeId() : 0,
|
||||
};
|
||||
}
|
||||
|
||||
void VkClearManager::MergeClearPayload(ClearAttachmentPayload& dst, const ClearAttachmentPayload& src) {
|
||||
dst.mask |= src.mask;
|
||||
if ((src.mask & GL_COLOR_BUFFER_BIT) != 0) {
|
||||
dst.color = src.color;
|
||||
}
|
||||
if ((src.mask & GL_DEPTH_BUFFER_BIT) != 0) {
|
||||
dst.depth = src.depth;
|
||||
}
|
||||
if ((src.mask & GL_STENCIL_BUFFER_BIT) != 0) {
|
||||
dst.stencil = src.stencil;
|
||||
}
|
||||
}
|
||||
|
||||
void VkClearManager::ErasePendingClearsForTextureLocked(const TextureIdentity& identity) {
|
||||
Vector<PendingClearKey> keysToErase;
|
||||
keysToErase.reserve(m_pendingClears.size());
|
||||
for (auto it = m_pendingClears.begin(); it != m_pendingClears.end(); ++it) {
|
||||
if (PendingClearMatchesTextureIdentity(it->first, identity)) {
|
||||
keysToErase.emplace_back(it->first);
|
||||
}
|
||||
}
|
||||
for (const auto& key : keysToErase) {
|
||||
m_pendingClears.erase(key);
|
||||
}
|
||||
m_aliveObjects.erase(identity);
|
||||
}
|
||||
|
||||
Bool VkClearManager::LockTextureIdentityLocked(const TextureIdentity& identity,
|
||||
SharedPtr<MG_State::GLState::ITextureObject>& outTexture) {
|
||||
outTexture.reset();
|
||||
if (identity.texture == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto aliveIt = m_aliveObjects.find(identity);
|
||||
if (aliveIt == m_aliveObjects.end()) {
|
||||
ErasePendingClearsForTextureLocked(identity);
|
||||
return false;
|
||||
}
|
||||
|
||||
outTexture = aliveIt->second.lock();
|
||||
if (!outTexture || outTexture.get() != identity.texture || outTexture->GetLifetimeId() != identity.lifetimeId) {
|
||||
ErasePendingClearsForTextureLocked(identity);
|
||||
outTexture.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool VkClearManager::LockTextureLocked(const PendingClearKey& key,
|
||||
SharedPtr<MG_State::GLState::ITextureObject>& outTexture) {
|
||||
return LockTextureIdentityLocked(TextureIdentity{
|
||||
.texture = key.texture,
|
||||
.lifetimeId = key.textureLifetimeId,
|
||||
}, outTexture);
|
||||
}
|
||||
|
||||
void VkClearManager::QueueClear(GLbitfield mask, const ClearFramebufferPayload& clearPayload,
|
||||
@@ -121,25 +193,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
void VkClearManager::QueueClear(const ClearAttachmentPayload& clearPayload,
|
||||
const SharedPtr<MG_State::GLState::ITextureObject>& texture) {
|
||||
if (clearPayload.mask == 0) {
|
||||
if (clearPayload.mask == 0 || !texture) {
|
||||
return;
|
||||
}
|
||||
WeakPtr<MG_State::GLState::ITextureObject> weakTexturePtr = texture;
|
||||
if (weakTexturePtr.expired())
|
||||
return;
|
||||
auto* pTexture = weakTexturePtr.lock().get();
|
||||
m_aliveObjects[pTexture] = weakTexturePtr;
|
||||
auto& pending = m_pendingClears[MakePendingClearKey(pTexture)];
|
||||
pending.mask |= clearPayload.mask;
|
||||
if (clearPayload.mask & GL_COLOR_BUFFER_BIT) {
|
||||
pending.color = clearPayload.color;
|
||||
}
|
||||
if (clearPayload.mask & GL_DEPTH_BUFFER_BIT) {
|
||||
pending.depth = clearPayload.depth;
|
||||
}
|
||||
if (clearPayload.mask & GL_STENCIL_BUFFER_BIT) {
|
||||
pending.stencil = clearPayload.stencil;
|
||||
}
|
||||
|
||||
const PendingClearKey key = MakePendingClearKey(texture.get());
|
||||
const std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_aliveObjects[MakeTextureIdentity(texture.get())] = texture;
|
||||
auto& pending = m_pendingClears[key];
|
||||
MergeClearPayload(pending, clearPayload);
|
||||
}
|
||||
|
||||
void VkClearManager::QueueClear(const ClearAttachmentPayload& clearPayload,
|
||||
@@ -151,35 +213,42 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
if (!texture) {
|
||||
return;
|
||||
}
|
||||
WeakPtr<MG_State::GLState::ITextureObject> weakTexturePtr = texture;
|
||||
if (weakTexturePtr.expired()) {
|
||||
return;
|
||||
}
|
||||
auto* pTexture = weakTexturePtr.lock().get();
|
||||
m_aliveObjects[pTexture] = weakTexturePtr;
|
||||
auto& pending = m_pendingClears[MakePendingClearKey(attachment)];
|
||||
pending.mask |= clearPayload.mask;
|
||||
if (clearPayload.mask & GL_COLOR_BUFFER_BIT) {
|
||||
pending.color = clearPayload.color;
|
||||
}
|
||||
if (clearPayload.mask & GL_DEPTH_BUFFER_BIT) {
|
||||
pending.depth = clearPayload.depth;
|
||||
}
|
||||
if (clearPayload.mask & GL_STENCIL_BUFFER_BIT) {
|
||||
pending.stencil = clearPayload.stencil;
|
||||
}
|
||||
|
||||
const PendingClearKey key = MakePendingClearKey(attachment);
|
||||
const std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_aliveObjects[MakeTextureIdentity(texture.get())] = texture;
|
||||
auto& pending = m_pendingClears[key];
|
||||
MergeClearPayload(pending, clearPayload);
|
||||
}
|
||||
|
||||
Bool VkClearManager::HasPendingClear(MG_State::GLState::ITextureObject* texture) {
|
||||
if (texture == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return std::any_of(m_pendingClears.begin(), m_pendingClears.end(),
|
||||
[texture](const auto& item) { return item.first.texture == texture; });
|
||||
|
||||
const Uint64 lifetimeId = texture->GetLifetimeId();
|
||||
const std::lock_guard<std::mutex> lock(m_mutex);
|
||||
for (auto it = m_pendingClears.begin(); it != m_pendingClears.end(); ++it) {
|
||||
if (it->first.texture == texture && it->first.textureLifetimeId == lifetimeId) {
|
||||
SharedPtr<MG_State::GLState::ITextureObject> liveTexture;
|
||||
return LockTextureLocked(it->first, liveTexture);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Bool VkClearManager::HasPendingClear(const PendingClearKey& key) {
|
||||
return key.texture != nullptr && m_pendingClears.find(key) != m_pendingClears.end();
|
||||
if (key.texture == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::lock_guard<std::mutex> lock(m_mutex);
|
||||
if (m_pendingClears.find(key) == m_pendingClears.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SharedPtr<MG_State::GLState::ITextureObject> liveTexture;
|
||||
return LockTextureLocked(key, liveTexture);
|
||||
}
|
||||
|
||||
Bool VkClearManager::HasPendingClear(const MG_State::GLState::FramebufferAttachmentObject& attachment) {
|
||||
@@ -190,20 +259,30 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
Bool VkClearManager::GetPendingClear(const PendingClearKey& key, ClearAttachmentPayload& outPayload) {
|
||||
if (key.texture == nullptr || m_aliveObjects.find(key.texture) == m_aliveObjects.end()) {
|
||||
SharedPtr<MG_State::GLState::ITextureObject> liveTexture;
|
||||
return GetPendingClear(key, outPayload, liveTexture);
|
||||
}
|
||||
|
||||
Bool VkClearManager::GetPendingClear(const PendingClearKey& key, ClearAttachmentPayload& outPayload,
|
||||
SharedPtr<MG_State::GLState::ITextureObject>& outTexture) {
|
||||
if (key.texture == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::lock_guard<std::mutex> lock(m_mutex);
|
||||
if (!LockTextureLocked(key, outTexture)) {
|
||||
return false;
|
||||
}
|
||||
auto it = m_pendingClears.find(key);
|
||||
if (it == m_pendingClears.end()) {
|
||||
MGLOG_D("%s: Failed getting pending clear for texture %d mip=%u layer=%u count=%u", __func__,
|
||||
key.texture ? key.texture->GetExternalIndex() : 0, key.mipLevel, key.baseArrayLayer, key.layerCount);
|
||||
outTexture.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
outPayload = it->second;
|
||||
MGLOG_D("%s: Got pending clear for texture %d (%s), mip=%u layer=%u count=%u mask=0x%x clear value: color = (%.2f, %.2f, %.2f, %.2f), depth = (%.2f), stencil = (%u)", __func__,
|
||||
key.texture->GetExternalIndex(),
|
||||
MG_Util::ConvertTextureInternalFormatToString(key.texture->GetFormat()).c_str(),
|
||||
MGLOG_D("%s: Got pending clear for texture@%p lifetime=%llu, mip=%u layer=%u count=%u mask=0x%x clear value: color = (%.2f, %.2f, %.2f, %.2f), depth = (%.2f), stencil = (%u)", __func__,
|
||||
static_cast<void*>(key.texture),
|
||||
static_cast<unsigned long long>(key.textureLifetimeId),
|
||||
key.mipLevel, key.baseArrayLayer, key.layerCount,
|
||||
static_cast<Uint32>(outPayload.mask),
|
||||
outPayload.color[0], outPayload.color[1], outPayload.color[2], outPayload.color[3],
|
||||
@@ -224,14 +303,20 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Bool VkClearManager::GetPendingClears(MG_State::GLState::ITextureObject* texture,
|
||||
Vector<PendingClearEntry>& outEntries) {
|
||||
outEntries.clear();
|
||||
if (texture == nullptr || m_aliveObjects.find(texture) == m_aliveObjects.end()) {
|
||||
if (texture == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (const auto& [key, payload] : m_pendingClears) {
|
||||
if (key.texture != texture) {
|
||||
continue;
|
||||
|
||||
const Uint64 lifetimeId = texture->GetLifetimeId();
|
||||
const std::lock_guard<std::mutex> lock(m_mutex);
|
||||
SharedPtr<MG_State::GLState::ITextureObject> liveTexture;
|
||||
if (!LockTextureIdentityLocked(MakeTextureIdentity(texture), liveTexture)) {
|
||||
return false;
|
||||
}
|
||||
for (auto it = m_pendingClears.begin(); it != m_pendingClears.end(); ++it) {
|
||||
if (it->first.texture == texture && it->first.textureLifetimeId == lifetimeId) {
|
||||
outEntries.emplace_back(PendingClearEntry{.key = it->first, .payload = it->second});
|
||||
}
|
||||
outEntries.emplace_back(PendingClearEntry{.key = key, .payload = payload});
|
||||
}
|
||||
return !outEntries.empty();
|
||||
}
|
||||
@@ -240,24 +325,29 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
if (texture == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const TextureIdentity identity = MakeTextureIdentity(texture);
|
||||
MGLOG_D("%s: Pop all pending clears for texture %d", __func__, texture->GetExternalIndex());
|
||||
m_aliveObjects.erase(texture);
|
||||
for (auto it = m_pendingClears.begin(); it != m_pendingClears.end();) {
|
||||
if (it->first.texture == texture) {
|
||||
it = m_pendingClears.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
const std::lock_guard<std::mutex> lock(m_mutex);
|
||||
ErasePendingClearsForTextureLocked(identity);
|
||||
}
|
||||
|
||||
void VkClearManager::PopPendingClear(const PendingClearKey& key) {
|
||||
if (key.texture == nullptr) {
|
||||
return;
|
||||
}
|
||||
MGLOG_D("%s: Pop pending clear for texture %d mip=%u layer=%u count=%u", __func__,
|
||||
key.texture->GetExternalIndex(), key.mipLevel, key.baseArrayLayer, key.layerCount);
|
||||
m_pendingClears.erase(key);
|
||||
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(m_mutex);
|
||||
auto it = m_pendingClears.find(key);
|
||||
if (it != m_pendingClears.end()) {
|
||||
m_pendingClears.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
MGLOG_D("%s: Pop pending clear for texture@%p lifetime=%llu mip=%u layer=%u count=%u", __func__,
|
||||
static_cast<void*>(key.texture), static_cast<unsigned long long>(key.textureLifetimeId),
|
||||
key.mipLevel, key.baseArrayLayer, key.layerCount);
|
||||
}
|
||||
|
||||
void VkClearManager::PopPendingClear(const MG_State::GLState::FramebufferAttachmentObject& attachment) {
|
||||
@@ -268,26 +358,26 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
SizeT VkClearManager::CollectGarbage() {
|
||||
const std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_gcCounter++;
|
||||
if (m_gcCounter != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SizeT count = 0;
|
||||
for (auto it = m_aliveObjects.begin(); it != m_aliveObjects.end();) {
|
||||
auto current = it++;
|
||||
if (current->second.expired()) {
|
||||
for (auto clearIt = m_pendingClears.begin(); clearIt != m_pendingClears.end();) {
|
||||
if (clearIt->first.texture == current->first) {
|
||||
clearIt = m_pendingClears.erase(clearIt);
|
||||
} else {
|
||||
++clearIt;
|
||||
}
|
||||
}
|
||||
m_aliveObjects.erase(current);
|
||||
++count;
|
||||
Vector<TextureIdentity> expiredTextures;
|
||||
expiredTextures.reserve(m_aliveObjects.size());
|
||||
for (auto it = m_aliveObjects.begin(); it != m_aliveObjects.end(); ++it) {
|
||||
if (it->second.expired()) {
|
||||
expiredTextures.emplace_back(it->first);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
if (expiredTextures.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (const auto& identity : expiredTextures) {
|
||||
ErasePendingClearsForTextureLocked(identity);
|
||||
}
|
||||
return expiredTextures.size();
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "MG_Util/Math/VectorTypes.h"
|
||||
|
||||
#include <Includes.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
struct ClearFramebufferPayload {
|
||||
@@ -31,16 +32,27 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
struct PendingClearKey {
|
||||
MG_State::GLState::ITextureObject* texture = nullptr;
|
||||
Uint64 textureLifetimeId = 0;
|
||||
Uint32 mipLevel = 0;
|
||||
Uint32 baseArrayLayer = 0;
|
||||
Uint32 layerCount = 1;
|
||||
|
||||
Bool operator==(const PendingClearKey& other) const {
|
||||
return texture == other.texture && mipLevel == other.mipLevel &&
|
||||
return texture == other.texture && textureLifetimeId == other.textureLifetimeId &&
|
||||
mipLevel == other.mipLevel &&
|
||||
baseArrayLayer == other.baseArrayLayer && layerCount == other.layerCount;
|
||||
}
|
||||
};
|
||||
|
||||
struct TextureIdentity {
|
||||
MG_State::GLState::ITextureObject* texture = nullptr;
|
||||
Uint64 lifetimeId = 0;
|
||||
|
||||
Bool operator==(const TextureIdentity& other) const {
|
||||
return texture == other.texture && lifetimeId == other.lifetimeId;
|
||||
}
|
||||
};
|
||||
|
||||
struct PendingClearEntry {
|
||||
PendingClearKey key{};
|
||||
ClearAttachmentPayload payload{};
|
||||
@@ -49,10 +61,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
struct PendingClearKeyHash {
|
||||
SizeT operator()(const PendingClearKey& key) const {
|
||||
const SizeT textureHash = std::hash<MG_State::GLState::ITextureObject*>{}(key.texture);
|
||||
const SizeT textureLifetimeHash = std::hash<Uint64>{}(key.textureLifetimeId);
|
||||
const SizeT mipHash = std::hash<Uint32>{}(key.mipLevel);
|
||||
const SizeT layerHash = std::hash<Uint32>{}(key.baseArrayLayer);
|
||||
const SizeT layerCountHash = std::hash<Uint32>{}(key.layerCount);
|
||||
SizeT hash = textureHash;
|
||||
hash ^= textureLifetimeHash + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||
hash ^= mipHash + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||
hash ^= layerHash + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||
hash ^= layerCountHash + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||
@@ -60,6 +74,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
};
|
||||
|
||||
struct TextureIdentityHash {
|
||||
SizeT operator()(const TextureIdentity& key) const {
|
||||
SizeT hash = std::hash<MG_State::GLState::ITextureObject*>{}(key.texture);
|
||||
hash ^= std::hash<Uint64>{}(key.lifetimeId) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
class VkClearManager {
|
||||
public:
|
||||
static PendingClearKey MakePendingClearKey(const MG_State::GLState::FramebufferAttachmentObject& attachment);
|
||||
@@ -79,6 +101,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Bool HasPendingClear(const PendingClearKey& key);
|
||||
Bool HasPendingClear(const MG_State::GLState::FramebufferAttachmentObject& attachment);
|
||||
Bool GetPendingClear(const PendingClearKey& key, ClearAttachmentPayload& outPayload);
|
||||
Bool GetPendingClear(const PendingClearKey& key, ClearAttachmentPayload& outPayload,
|
||||
SharedPtr<MG_State::GLState::ITextureObject>& outTexture);
|
||||
Bool GetPendingClear(const MG_State::GLState::FramebufferAttachmentObject& attachment,
|
||||
ClearAttachmentPayload& outPayload);
|
||||
Bool GetPendingClears(MG_State::GLState::ITextureObject* texture, Vector<PendingClearEntry>& outEntries);
|
||||
@@ -87,8 +111,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void PopPendingClear(const MG_State::GLState::FramebufferAttachmentObject& attachment);
|
||||
SizeT CollectGarbage();
|
||||
private:
|
||||
static TextureIdentity MakeTextureIdentity(MG_State::GLState::ITextureObject* texture);
|
||||
static void MergeClearPayload(ClearAttachmentPayload& dst, const ClearAttachmentPayload& src);
|
||||
void ErasePendingClearsForTextureLocked(const TextureIdentity& identity);
|
||||
Bool LockTextureIdentityLocked(const TextureIdentity& identity,
|
||||
SharedPtr<MG_State::GLState::ITextureObject>& outTexture);
|
||||
Bool LockTextureLocked(const PendingClearKey& key,
|
||||
SharedPtr<MG_State::GLState::ITextureObject>& outTexture);
|
||||
|
||||
Uint8 m_gcCounter = 0;
|
||||
UnorderedMap<PendingClearKey, ClearAttachmentPayload, PendingClearKeyHash> m_pendingClears;
|
||||
UnorderedMap<MG_State::GLState::ITextureObject*, WeakPtr<MG_State::GLState::ITextureObject>> m_aliveObjects;
|
||||
mutable std::mutex m_mutex;
|
||||
std::unordered_map<PendingClearKey, ClearAttachmentPayload, PendingClearKeyHash> m_pendingClears;
|
||||
std::unordered_map<TextureIdentity, WeakPtr<MG_State::GLState::ITextureObject>, TextureIdentityHash> m_aliveObjects;
|
||||
};
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
@@ -104,6 +104,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return info;
|
||||
}
|
||||
|
||||
IntVec2 ResolveRenderPassFramebufferExtent(Bool isDefaultFbo, const TextureSize& attachmentExtent,
|
||||
VkExtent2D swapchainExtent) {
|
||||
if (isDefaultFbo) {
|
||||
return {static_cast<Int>(swapchainExtent.width), static_cast<Int>(swapchainExtent.height)};
|
||||
}
|
||||
return {attachmentExtent.x(), attachmentExtent.y()};
|
||||
}
|
||||
|
||||
VkRenderPassManager::VkRenderPassManager(VkDevice device,
|
||||
const VulkanRendererConfig& config, VkClearManager& clearManager, VkTextureManager& textureManager,
|
||||
SwapchainObject& swapchainObject):
|
||||
@@ -131,7 +139,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkRenderPassManager::HashType VkRenderPassManager::ComputeHash(
|
||||
const MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex, Bool includePendingClear) const {
|
||||
XXHASH_VERIFY(XXH64_reset(m_hashState, m_config.CacheVersion));
|
||||
const Bool isDefaultFbo = (&fbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO.get());
|
||||
const Bool isDefaultFbo = fbo.IsDefaultFramebuffer();
|
||||
if (isDefaultFbo) {
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &swapchainImageIndex, sizeof(swapchainImageIndex)));
|
||||
}
|
||||
@@ -162,6 +170,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
contentPtr = att.GetRenderbuffer().get();
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &contentPtr, sizeof(contentPtr)));
|
||||
if (att.IsTexture()) {
|
||||
const Uint64 textureLifetimeId = att.GetTexture()->GetLifetimeId();
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &textureLifetimeId, sizeof(textureLifetimeId)));
|
||||
const Int textureLevel = att.GetTextureLevel();
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &textureLevel, sizeof(textureLevel)));
|
||||
const TextureUploadTarget textureUploadTarget = att.GetTextureUploadTarget();
|
||||
@@ -196,8 +206,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
VkImageLayout currentLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
if (isDefaultFbo) {
|
||||
if (attachment >= FramebufferAttachmentType::Color0 &&
|
||||
attachment <= FramebufferAttachmentType::Color31) {
|
||||
const Bool isDefaultColorAttachment =
|
||||
attachment == FramebufferAttachmentType::Color0 ||
|
||||
(attachment >= FramebufferAttachmentType::FrontLeft &&
|
||||
attachment <= FramebufferAttachmentType::BackRight);
|
||||
if (isDefaultColorAttachment) {
|
||||
currentLayout = m_swapchainObject.GetImageLayout(swapchainImageIndex);
|
||||
} else if (attachment == FramebufferAttachmentType::Depth ||
|
||||
attachment == FramebufferAttachmentType::Stencil) {
|
||||
@@ -269,13 +282,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
if (it != m_renderPasses.end())
|
||||
return it->second;
|
||||
|
||||
Bool isDefaultFbo = (&fbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO.get());
|
||||
Bool isDefaultFbo = fbo.IsDefaultFramebuffer();
|
||||
// Color attachment
|
||||
auto& drawbufs = fbo.GetDrawBuffers();
|
||||
const Uint32 colorAttachmentSlotCount = static_cast<Uint32>(drawbufs.size());
|
||||
|
||||
Int width = 0;
|
||||
Int height = 0;
|
||||
const VkExtent2D swapchainExtent = m_swapchainObject.GetExtent();
|
||||
// Default framebuffer attachments are frontend placeholders; Vulkan framebuffer extent must match the swapchain.
|
||||
const IntVec2 defaultFramebufferExtent =
|
||||
ResolveRenderPassFramebufferExtent(isDefaultFbo, {0, 0, 0}, swapchainExtent);
|
||||
Int width = defaultFramebufferExtent.x();
|
||||
Int height = defaultFramebufferExtent.y();
|
||||
Vector<VkAttachmentDescription> attachmentDescriptions;
|
||||
attachmentDescriptions.reserve(colorAttachmentSlotCount + 1);
|
||||
// Keep the full GL draw buffer slot span so fragment outputs targeting GL_NONE map to VK_ATTACHMENT_UNUSED.
|
||||
@@ -351,10 +368,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
.key = VkClearManager::MakePendingClearKey(att)
|
||||
});
|
||||
}
|
||||
const IntVec2 attachmentExtent =
|
||||
ResolveRenderPassFramebufferExtent(isDefaultFbo, att.GetSize(), swapchainExtent);
|
||||
if (width == 0)
|
||||
width = att.GetSize().x();
|
||||
width = attachmentExtent.x();
|
||||
if (height == 0)
|
||||
height = att.GetSize().y();
|
||||
height = attachmentExtent.y();
|
||||
|
||||
if (isDefaultFbo) {
|
||||
const auto& swapchainViews = m_swapchainObject.GetImageViews();
|
||||
@@ -379,7 +398,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
trackedColorLayout = textureResource->layout;
|
||||
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
|
||||
.target = TrackedAttachmentTarget::Texture,
|
||||
.texture = texture,
|
||||
.texture = att.GetTexture(),
|
||||
.textureMipLevel = attachmentMipLevel,
|
||||
.finalLayout = desc.finalLayout,
|
||||
});
|
||||
@@ -505,7 +524,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
texture.GetExternalIndex());
|
||||
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
|
||||
.target = TrackedAttachmentTarget::Texture,
|
||||
.texture = &texture,
|
||||
.texture = selectedDepthStencilAttachment->GetTexture(),
|
||||
.textureMipLevel = attachmentMipLevel,
|
||||
.finalLayout = depthAttachmentDescription.finalLayout,
|
||||
});
|
||||
@@ -519,9 +538,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
texture, attachmentMipLevel, baseArrayLayer, layerCount, attachmentViewType));
|
||||
MOBILEGL_ASSERT(attachmentViews.back() != VK_NULL_HANDLE,
|
||||
"GetOrCreateRenderPass: GetOrCreateAttachmentView failed at depth attachment");
|
||||
const IntVec2 attachmentExtent =
|
||||
ResolveRenderPassFramebufferExtent(isDefaultFbo, selectedDepthStencilAttachment->GetSize(),
|
||||
swapchainExtent);
|
||||
if (width == 0 || height == 0) {
|
||||
width = selectedDepthStencilAttachment->GetSize().x();
|
||||
height = selectedDepthStencilAttachment->GetSize().y();
|
||||
width = attachmentExtent.x();
|
||||
height = attachmentExtent.y();
|
||||
}
|
||||
}
|
||||
attachmentDescriptions.emplace_back(depthAttachmentDescription);
|
||||
@@ -617,7 +639,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
continue;
|
||||
}
|
||||
ClearAttachmentPayload clearPayload{};
|
||||
if (!s_clearManager->GetPendingClear(pending.key, clearPayload)) {
|
||||
SharedPtr<MG_State::GLState::ITextureObject> liveTexture;
|
||||
if (!s_clearManager->GetPendingClear(pending.key, clearPayload, liveTexture)) {
|
||||
continue;
|
||||
}
|
||||
if ((clearPayload.mask & GL_COLOR_BUFFER_BIT) != 0) {
|
||||
@@ -625,7 +648,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
clearPayload.color.x(),
|
||||
clearPayload.color.y(),
|
||||
clearPayload.color.z(),
|
||||
ResolveColorClearAlpha(pending.key.texture, clearPayload.color.w())
|
||||
ResolveColorClearAlpha(liveTexture.get(), clearPayload.color.w())
|
||||
};
|
||||
}
|
||||
if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) {
|
||||
@@ -660,11 +683,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
switch (trackedAttachment.target) {
|
||||
case TrackedAttachmentTarget::Texture:
|
||||
MOBILEGL_ASSERT(s_textureManager != nullptr, "EndRenderPass: texture manager is null");
|
||||
s_textureManager->UpdateTrackedImageLayoutAfterAttachmentWrite(
|
||||
commandBuffer,
|
||||
trackedAttachment.texture,
|
||||
trackedAttachment.textureMipLevel,
|
||||
trackedAttachment.finalLayout);
|
||||
if (const auto texture = trackedAttachment.texture.lock()) {
|
||||
s_textureManager->UpdateTrackedImageLayoutAfterAttachmentWrite(
|
||||
commandBuffer,
|
||||
texture.get(),
|
||||
trackedAttachment.textureMipLevel,
|
||||
trackedAttachment.finalLayout);
|
||||
}
|
||||
break;
|
||||
case TrackedAttachmentTarget::SwapchainColor:
|
||||
MOBILEGL_ASSERT(s_swapchainObject != nullptr, "EndRenderPass: swapchain object is null");
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
struct TrackedAttachmentLayoutInfo {
|
||||
TrackedAttachmentTarget target = TrackedAttachmentTarget::Texture;
|
||||
MG_State::GLState::ITextureObject* texture = nullptr;
|
||||
WeakPtr<MG_State::GLState::ITextureObject> texture;
|
||||
Uint32 textureMipLevel = 0;
|
||||
Uint32 swapchainImageIndex = 0;
|
||||
VkImageLayout finalLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
@@ -45,6 +45,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
DepthStencilAttachmentLoadInfo ResolveDepthStencilAttachmentLoadInfo(
|
||||
VkImageLayout trackedLayout, Bool clearDepth, Bool clearStencil);
|
||||
IntVec2 ResolveRenderPassFramebufferExtent(Bool isDefaultFbo, const TextureSize& attachmentExtent,
|
||||
VkExtent2D swapchainExtent);
|
||||
|
||||
struct RenderPassEntry {
|
||||
static inline VkDevice s_device;
|
||||
|
||||
@@ -156,6 +156,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
}
|
||||
|
||||
VkTextureManager::TextureIdentity VkTextureManager::MakeTextureIdentity(
|
||||
MG_State::GLState::ITextureObject* texture) {
|
||||
return TextureIdentity{
|
||||
.texture = texture,
|
||||
.lifetimeId = texture ? texture->GetLifetimeId() : 0,
|
||||
};
|
||||
}
|
||||
|
||||
static void GetImageTransitionDestinationState(VkImageLayout newLayout,
|
||||
VkPipelineStageFlags& outDstStageMask,
|
||||
VkAccessFlags& outDstAccessMask) {
|
||||
@@ -580,6 +588,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void VkTextureManager::Shutdown() {
|
||||
DestroyDeferredReleases();
|
||||
m_textureResources.clear();
|
||||
m_aliveObjects.clear();
|
||||
|
||||
m_device = VK_NULL_HANDLE;
|
||||
m_physicalDevice = VK_NULL_HANDLE;
|
||||
@@ -600,28 +609,56 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
CollectDeferredReleases(frameIndex);
|
||||
}
|
||||
|
||||
void VkTextureManager::EraseTrackedTexture(const TextureIdentity& identity) {
|
||||
auto resourceIt = m_textureResources.find(identity);
|
||||
if (resourceIt != m_textureResources.end()) {
|
||||
DeferResourceRelease(Move(resourceIt->second));
|
||||
m_textureResources.erase(resourceIt);
|
||||
}
|
||||
m_aliveObjects.erase(identity);
|
||||
}
|
||||
|
||||
void VkTextureManager::PruneStaleTextureAliases(MG_State::GLState::ITextureObject* texture) {
|
||||
if (texture == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<TextureIdentity> staleAliases;
|
||||
staleAliases.reserve(m_aliveObjects.size());
|
||||
for (auto it = m_aliveObjects.begin(); it != m_aliveObjects.end(); ++it) {
|
||||
if (it->first.texture != texture) {
|
||||
continue;
|
||||
}
|
||||
const auto liveTexture = it->second.lock();
|
||||
if (!liveTexture || liveTexture.get() != texture ||
|
||||
liveTexture->GetLifetimeId() != it->first.lifetimeId) {
|
||||
staleAliases.emplace_back(it->first);
|
||||
}
|
||||
}
|
||||
for (const auto& identity : staleAliases) {
|
||||
EraseTrackedTexture(identity);
|
||||
}
|
||||
}
|
||||
|
||||
VkTextureManager::TextureResource* VkTextureManager::SyncTextureAndGetDescriptor(MG_State::GLState::ITextureObject& texture) {
|
||||
MOBILEGL_ASSERT(m_device != VK_NULL_HANDLE, "SyncTextureAndGetDescriptor: m_device == VK_NULL_HANDLE");
|
||||
|
||||
auto aliveIt = m_aliveObjects.find(&texture);
|
||||
const TextureIdentity identity = MakeTextureIdentity(&texture);
|
||||
auto aliveIt = m_aliveObjects.find(identity);
|
||||
if (aliveIt != m_aliveObjects.end() && aliveIt->second.expired()) {
|
||||
auto resourceIt = m_textureResources.find(&texture);
|
||||
if (resourceIt != m_textureResources.end()) {
|
||||
DeferResourceRelease(Move(resourceIt->second));
|
||||
m_textureResources.erase(resourceIt);
|
||||
}
|
||||
m_aliveObjects.erase(aliveIt);
|
||||
EraseTrackedTexture(aliveIt->first);
|
||||
}
|
||||
|
||||
const auto& liveTexture = MG_State::pGLContext->GetTextureObject(texture.GetExternalIndex());
|
||||
if (liveTexture && liveTexture.get() == &texture) {
|
||||
m_aliveObjects[&texture] = WeakPtr<MG_State::GLState::ITextureObject>(liveTexture);
|
||||
m_aliveObjects[identity] = WeakPtr<MG_State::GLState::ITextureObject>(liveTexture);
|
||||
PruneStaleTextureAliases(&texture);
|
||||
}
|
||||
|
||||
auto it = m_textureResources.find(&texture);
|
||||
auto it = m_textureResources.find(identity);
|
||||
if (it == m_textureResources.end()) {
|
||||
TextureResource initial{};
|
||||
auto [insertIt, _] = m_textureResources.emplace(&texture, Move(initial));
|
||||
auto [insertIt, _] = m_textureResources.emplace(identity, Move(initial));
|
||||
it = insertIt;
|
||||
}
|
||||
|
||||
@@ -737,7 +774,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
void VkTextureManager::UpdateTrackedImageLayout(MG_State::GLState::ITextureObject* texture, VkImageLayout newLayout) {
|
||||
MOBILEGL_ASSERT(texture != nullptr, "UpdateTrackedImageLayout: texture is null");
|
||||
auto it = m_textureResources.find(texture);
|
||||
auto it = m_textureResources.find(MakeTextureIdentity(texture));
|
||||
MOBILEGL_ASSERT(it != m_textureResources.end(),
|
||||
"UpdateTrackedImageLayout: textureId=%d has no tracked resource", texture->GetExternalIndex());
|
||||
MOBILEGL_ASSERT(it->second.image != VK_NULL_HANDLE,
|
||||
@@ -750,7 +787,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Uint32 writtenMipLevel,
|
||||
VkImageLayout newLayout) {
|
||||
MOBILEGL_ASSERT(texture != nullptr, "UpdateTrackedImageLayoutAfterAttachmentWrite: texture is null");
|
||||
auto it = m_textureResources.find(texture);
|
||||
auto it = m_textureResources.find(MakeTextureIdentity(texture));
|
||||
MOBILEGL_ASSERT(it != m_textureResources.end(),
|
||||
"UpdateTrackedImageLayoutAfterAttachmentWrite: textureId=%d has no tracked resource",
|
||||
texture->GetExternalIndex());
|
||||
@@ -920,20 +957,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
if (m_gcCounter != 0) {
|
||||
return 0;
|
||||
}
|
||||
SizeT count = 0;
|
||||
for (auto it = m_aliveObjects.begin(); it != m_aliveObjects.end();) {
|
||||
auto current = it++;
|
||||
if (current->second.expired()) {
|
||||
auto resourceIt = m_textureResources.find(current->first);
|
||||
if (resourceIt != m_textureResources.end()) {
|
||||
DeferResourceRelease(Move(resourceIt->second));
|
||||
m_textureResources.erase(resourceIt);
|
||||
}
|
||||
m_aliveObjects.erase(current);
|
||||
++count;
|
||||
|
||||
Vector<MG_State::GLState::ITextureObject*> expiredTextures;
|
||||
expiredTextures.reserve(m_aliveObjects.size());
|
||||
for (auto it = m_aliveObjects.begin(); it != m_aliveObjects.end(); ++it) {
|
||||
if (it->second.expired()) {
|
||||
expiredTextures.emplace_back(it->first.texture);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
for (auto* texture : expiredTextures) {
|
||||
PruneStaleTextureAliases(texture);
|
||||
}
|
||||
return expiredTextures.size();
|
||||
}
|
||||
|
||||
Bool VkTextureManager::SyncTexture(MG_State::GLState::ITextureObject &texture,
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <Includes.h>
|
||||
#include <MG_State/GLState/TextureState/TextureObject.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace MobileGL::MG_State::GLState {
|
||||
class ITextureObject;
|
||||
@@ -20,6 +21,23 @@ class ITextureObject;
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
class VkTextureManager {
|
||||
public:
|
||||
struct TextureIdentity {
|
||||
MG_State::GLState::ITextureObject* texture = nullptr;
|
||||
Uint64 lifetimeId = 0;
|
||||
|
||||
Bool operator==(const TextureIdentity& other) const {
|
||||
return texture == other.texture && lifetimeId == other.lifetimeId;
|
||||
}
|
||||
};
|
||||
|
||||
struct TextureIdentityHash {
|
||||
SizeT operator()(const TextureIdentity& key) const {
|
||||
SizeT hash = std::hash<MG_State::GLState::ITextureObject*>{}(key.texture);
|
||||
hash ^= std::hash<Uint64>{}(key.lifetimeId) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
struct InitInfo {
|
||||
VkDevice device = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
|
||||
@@ -212,6 +230,9 @@ private:
|
||||
void DeferViewRelease(VkImageView view);
|
||||
void CollectDeferredReleases(Uint32 frameIndex);
|
||||
void DestroyDeferredReleases();
|
||||
static TextureIdentity MakeTextureIdentity(MG_State::GLState::ITextureObject* texture);
|
||||
void EraseTrackedTexture(const TextureIdentity& identity);
|
||||
void PruneStaleTextureAliases(MG_State::GLState::ITextureObject* texture);
|
||||
|
||||
VkDevice m_device = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
|
||||
@@ -221,8 +242,8 @@ private:
|
||||
Uint32 m_currentFrameIndex = 0;
|
||||
|
||||
Uint8 m_gcCounter = 0;
|
||||
UnorderedMap<MG_State::GLState::ITextureObject*, WeakPtr<MG_State::GLState::ITextureObject>> m_aliveObjects;
|
||||
UnorderedMap<MG_State::GLState::ITextureObject*, TextureResource> m_textureResources;
|
||||
std::unordered_map<TextureIdentity, WeakPtr<MG_State::GLState::ITextureObject>, TextureIdentityHash> m_aliveObjects;
|
||||
std::unordered_map<TextureIdentity, TextureResource, TextureIdentityHash> m_textureResources;
|
||||
Vector<Vector<TextureResource>> m_deferredReleases;
|
||||
Vector<Vector<VkImageView>> m_deferredViewReleases;
|
||||
};
|
||||
|
||||
@@ -689,7 +689,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
if (trackedAttachment.target != TrackedAttachmentTarget::Texture) {
|
||||
continue;
|
||||
}
|
||||
if (trackedAttachment.texture == &texture) {
|
||||
const auto trackedTexture = trackedAttachment.texture.lock();
|
||||
if (trackedTexture && trackedTexture.get() == &texture) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1064,9 +1065,33 @@ void main() {
|
||||
static Bool ResolveColorBlitBinding(MG_State::GLState::FramebufferObject& fbo, Bool isReadFramebuffer,
|
||||
Uint32 swapchainImageIndex, SwapchainObject& swapchainObject,
|
||||
VkTextureManager& textureManager, BlitImageBinding& outBinding) {
|
||||
const Bool isDefaultFbo = (&fbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO.get());
|
||||
const Bool isDefaultFbo = fbo.IsDefaultFramebuffer();
|
||||
const FramebufferAttachmentType attachmentType =
|
||||
isReadFramebuffer ? fbo.GetReadBuffer() : fbo.GetDrawBuffers()[0];
|
||||
outBinding.label = isReadFramebuffer ? "read" : "draw";
|
||||
|
||||
if (isDefaultFbo) {
|
||||
const Bool defaultColorAttachment =
|
||||
attachmentType == FramebufferAttachmentType::Color0 ||
|
||||
(attachmentType >= FramebufferAttachmentType::FrontLeft &&
|
||||
attachmentType <= FramebufferAttachmentType::BackRight);
|
||||
if (!defaultColorAttachment) {
|
||||
MGLOG_E("BlitFramebuffer skipped: default framebuffer color attachment %d is not supported",
|
||||
static_cast<Int>(attachmentType));
|
||||
return false;
|
||||
}
|
||||
outBinding.image = swapchainObject.GetImage(swapchainImageIndex);
|
||||
outBinding.trackedLayout = nullptr;
|
||||
outBinding.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
const auto extent = swapchainObject.GetExtent();
|
||||
outBinding.extent = {static_cast<Int>(extent.width), static_cast<Int>(extent.height)};
|
||||
outBinding.mipLevel = 0;
|
||||
outBinding.mipLevelCount = 1;
|
||||
outBinding.baseArrayLayer = 0;
|
||||
outBinding.layerCount = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (attachmentType < FramebufferAttachmentType::Color0 || attachmentType > FramebufferAttachmentType::Color31) {
|
||||
MGLOG_E("BlitFramebuffer only supports color attachments right now (attachment=%d)",
|
||||
static_cast<Int>(attachmentType));
|
||||
@@ -1090,20 +1115,6 @@ void main() {
|
||||
|
||||
auto* texture = attachment.GetTexture().get();
|
||||
MOBILEGL_ASSERT(texture != nullptr, "ResolveColorBlitBinding: texture attachment is null");
|
||||
outBinding.label = isReadFramebuffer ? "read" : "draw";
|
||||
|
||||
if (isDefaultFbo) {
|
||||
outBinding.image = swapchainObject.GetImage(swapchainImageIndex);
|
||||
outBinding.trackedLayout = nullptr;
|
||||
outBinding.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
const auto extent = swapchainObject.GetExtent();
|
||||
outBinding.extent = {static_cast<Int>(extent.width), static_cast<Int>(extent.height)};
|
||||
outBinding.mipLevel = 0;
|
||||
outBinding.mipLevelCount = 1;
|
||||
outBinding.baseArrayLayer = 0;
|
||||
outBinding.layerCount = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* resource = textureManager.SyncTextureAndGetDescriptor(*texture);
|
||||
if (resource == nullptr) {
|
||||
@@ -1134,8 +1145,7 @@ void main() {
|
||||
VkTextureManager& textureManager,
|
||||
VkImageAspectFlags requiredAspectMask,
|
||||
BlitImageBinding& outBinding) {
|
||||
const Bool isDefaultFbo =
|
||||
(&fbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO.get());
|
||||
const Bool isDefaultFbo = fbo.IsDefaultFramebuffer();
|
||||
const auto attachmentType = ResolveFramebufferCopyAttachmentType(fbo, isReadFramebuffer, requiredAspectMask);
|
||||
if (attachmentType == FramebufferAttachmentType::None) {
|
||||
MGLOG_E("BlitFramebuffer skipped: unsupported aspect mask=0x%x",
|
||||
@@ -1250,8 +1260,7 @@ void main() {
|
||||
VkTextureManager& textureManager,
|
||||
VkImageAspectFlags requiredAspectMask,
|
||||
BlitImageBinding& outBinding) {
|
||||
const Bool isDefaultFbo =
|
||||
(&fbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO.get());
|
||||
const Bool isDefaultFbo = fbo.IsDefaultFramebuffer();
|
||||
const auto attachmentType = ResolveFramebufferCopyAttachmentType(fbo, true, requiredAspectMask);
|
||||
if (attachmentType == FramebufferAttachmentType::None) {
|
||||
MGLOG_E("CopyTexSubImage2D skipped: unsupported source aspect mask=0x%x",
|
||||
@@ -1545,7 +1554,7 @@ void main() {
|
||||
ProgramFactory::CompileOptionFlags flags = ProgramFactory::CompileOptionBit::PositionZRemap;
|
||||
const auto& currentDrawFBO =
|
||||
MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
|
||||
if (currentDrawFBO == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO) {
|
||||
if (currentDrawFBO != nullptr && currentDrawFBO->IsDefaultFramebuffer()) {
|
||||
flags |= ProgramFactory::CompileOptionBit::PositionYFlip;
|
||||
switch (preTransform) {
|
||||
case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
|
||||
@@ -2703,8 +2712,7 @@ void main() {
|
||||
const auto& drawFboBinding =
|
||||
MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
|
||||
MOBILEGL_ASSERT(drawFboBinding != nullptr, "GetOrCreatePipeline: draw framebuffer is null");
|
||||
const Bool isDefaultDrawFbo =
|
||||
drawFboBinding.get() == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO.get();
|
||||
const Bool isDefaultDrawFbo = drawFboBinding->IsDefaultFramebuffer();
|
||||
const auto& drawBuffers = drawFboBinding->GetDrawBuffers();
|
||||
auto resolveCompleteColorAttachmentTexture = [&](Uint32 drawBufferIndex) -> MG_State::GLState::ITextureObject* {
|
||||
if (isDefaultDrawFbo || drawBufferIndex >= drawBuffers.size()) {
|
||||
@@ -3430,8 +3438,7 @@ void main() {
|
||||
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
|
||||
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
|
||||
GLenum filter) {
|
||||
const Bool drawIsDefaultFbo =
|
||||
(&drawFbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO.get());
|
||||
const Bool drawIsDefaultFbo = drawFbo.IsDefaultFramebuffer();
|
||||
if (!drawIsDefaultFbo) {
|
||||
return false;
|
||||
}
|
||||
@@ -3607,10 +3614,8 @@ void main() {
|
||||
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
|
||||
}
|
||||
|
||||
const Bool readIsDefaultFbo =
|
||||
(readFbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO);
|
||||
const Bool drawIsDefaultFbo =
|
||||
(drawFbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO);
|
||||
const Bool readIsDefaultFbo = readFbo->IsDefaultFramebuffer();
|
||||
const Bool drawIsDefaultFbo = drawFbo->IsDefaultFramebuffer();
|
||||
if (isColorBlit && drawIsDefaultFbo &&
|
||||
RequiresShaderBlitToDefaultFramebuffer(m_swapchainObject.GetPreTransform())) {
|
||||
if (TryBlitToDefaultFramebufferWithShader(frame, *readFbo, *drawFbo,
|
||||
@@ -3959,8 +3964,7 @@ void main() {
|
||||
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
|
||||
}
|
||||
|
||||
const Bool readIsDefaultFbo =
|
||||
(readFbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO);
|
||||
const Bool readIsDefaultFbo = readFbo->IsDefaultFramebuffer();
|
||||
|
||||
BlitImageBinding dstBinding{};
|
||||
if (!ResolveTextureCopyDestinationBinding(*destinationTexture, static_cast<Uint32>(level), *m_textureManager,
|
||||
@@ -4161,8 +4165,7 @@ void main() {
|
||||
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
|
||||
}
|
||||
|
||||
const Bool readIsDefaultFbo =
|
||||
(readFbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO);
|
||||
const Bool readIsDefaultFbo = readFbo->IsDefaultFramebuffer();
|
||||
BlitImageBinding srcBinding{};
|
||||
if (!ResolveColorBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager,
|
||||
srcBinding)) {
|
||||
@@ -5582,7 +5585,8 @@ void main() {
|
||||
}
|
||||
|
||||
ClearAttachmentPayload clearPayload{};
|
||||
if (!m_clearManager->GetPendingClear(pending.key, clearPayload)) {
|
||||
SharedPtr<MG_State::GLState::ITextureObject> liveTexture;
|
||||
if (!m_clearManager->GetPendingClear(pending.key, clearPayload, liveTexture)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -5595,7 +5599,7 @@ void main() {
|
||||
clearPayload.color.x(),
|
||||
clearPayload.color.y(),
|
||||
clearPayload.color.z(),
|
||||
ResolveColorClearAlpha(pending.key.texture, clearPayload.color.w())
|
||||
ResolveColorClearAlpha(liveTexture.get(), clearPayload.color.w())
|
||||
};
|
||||
} else {
|
||||
if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) {
|
||||
|
||||
@@ -35,7 +35,10 @@ namespace MobileGL::MG_Impl {
|
||||
stencilTex->AllocateStorage(TextureUploadTarget::Texture2D, 0, {{512, 512, 1}, 0});
|
||||
// stencilTex->SetMipmapLevel({{512, 512, 1}, 0, false, 0, {nullptr, 0}});
|
||||
fbo0->AttachTexture(FramebufferAttachmentType::Color0, colorTex);
|
||||
fbo0->AttachTexture(FramebufferAttachmentType::FrontLeft, colorTex);
|
||||
fbo0->AttachTexture(FramebufferAttachmentType::FrontRight, colorTex);
|
||||
fbo0->AttachTexture(FramebufferAttachmentType::BackLeft, colorTex);
|
||||
fbo0->AttachTexture(FramebufferAttachmentType::BackRight, colorTex);
|
||||
fbo0->AttachTexture(FramebufferAttachmentType::Depth, depthTex);
|
||||
fbo0->AttachTexture(FramebufferAttachmentType::Stencil, stencilTex);
|
||||
GLImpl::FramebufferImpl::pDefaultFramebufferInfo =
|
||||
|
||||
@@ -131,6 +131,7 @@ namespace MobileGL {
|
||||
Uint16 GetObjectVersion() const { return m_objectVersion; }
|
||||
|
||||
Uint GetExternalIndex() const;
|
||||
Bool IsDefaultFramebuffer() const { return m_externalIndex == 0; }
|
||||
|
||||
private:
|
||||
void BumpAttachmentVersion(FramebufferAttachmentType type);
|
||||
|
||||
@@ -13,9 +13,15 @@
|
||||
namespace MobileGL {
|
||||
namespace MG_State {
|
||||
namespace GLState {
|
||||
static std::atomic<Uint64> s_nextTextureLifetimeId = 1;
|
||||
|
||||
// TextureObjectBase implementations
|
||||
Uint64 TextureObjectBase::AllocateLifetimeId() {
|
||||
return s_nextTextureLifetimeId.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
TextureObjectBase::TextureObjectBase(TextureTarget target, Uint externalIndex)
|
||||
: m_target(target), m_externalIndex(externalIndex) {
|
||||
: m_externalIndex(externalIndex), m_lifetimeId(AllocateLifetimeId()), m_target(target) {
|
||||
m_sampler = MakeShared<SamplerObject>(0);
|
||||
}
|
||||
|
||||
@@ -155,6 +161,10 @@ namespace MobileGL {
|
||||
m_fixedSampleLocations = fixedSampleLocations;
|
||||
}
|
||||
|
||||
Uint64 TextureObjectBase::GetLifetimeId() const {
|
||||
return m_lifetimeId;
|
||||
}
|
||||
|
||||
Uint TextureObjectWithOneMipmap::GetMipmapLevelCount() const {
|
||||
return m_textureStorage.GetLevelCount();
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
virtual void SetSamples(Int samples) = 0;
|
||||
virtual Bool HasFixedSampleLocations() const = 0;
|
||||
virtual void SetFixedSampleLocations(Bool fixedSampleLocations) = 0;
|
||||
virtual Uint64 GetLifetimeId() const = 0;
|
||||
|
||||
protected:
|
||||
virtual Uint GetIndexOfTextureUploadTarget(TextureUploadTarget target) const = 0;
|
||||
@@ -75,9 +76,13 @@ namespace MobileGL::MG_State::GLState {
|
||||
void SetSamples(Int samples) override;
|
||||
Bool HasFixedSampleLocations() const override;
|
||||
void SetFixedSampleLocations(Bool fixedSampleLocations) override;
|
||||
Uint64 GetLifetimeId() const override;
|
||||
|
||||
protected:
|
||||
static Uint64 AllocateLifetimeId();
|
||||
|
||||
const Uint m_externalIndex;
|
||||
const Uint64 m_lifetimeId;
|
||||
const TextureTarget m_target = TextureTarget::Unknown;
|
||||
TextureInternalFormat m_internalFormat = TextureInternalFormat::Unknown;
|
||||
SharedPtr<SamplerObject> m_sampler = nullptr;
|
||||
|
||||
@@ -105,6 +105,20 @@ TEST_F(FramebufferTest, CreateFramebuffersCreatesObjectsImmediately) {
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(FramebufferTest, DefaultFramebufferIdentityTracksFramebufferNameZero) {
|
||||
const auto defaultFramebuffer = MG_State::pGLContext->GetFramebufferObject(0);
|
||||
ASSERT_NE(defaultFramebuffer, nullptr);
|
||||
EXPECT_TRUE(defaultFramebuffer->IsDefaultFramebuffer());
|
||||
const auto defaultFramebufferCopy = *defaultFramebuffer;
|
||||
EXPECT_TRUE(defaultFramebufferCopy.IsDefaultFramebuffer());
|
||||
|
||||
GLuint framebuffer = 0;
|
||||
MG_Impl::GLImpl::CreateFramebuffers(1, &framebuffer);
|
||||
const auto userFramebuffer = MG_State::pGLContext->GetFramebufferObject(framebuffer);
|
||||
ASSERT_NE(userFramebuffer, nullptr);
|
||||
EXPECT_FALSE(userFramebuffer->IsDefaultFramebuffer());
|
||||
}
|
||||
|
||||
TEST_F(FramebufferTest, NamedFramebufferTextureAttachesWithoutChangingBindings) {
|
||||
GLuint framebuffer = 0;
|
||||
MG_Impl::GLImpl::CreateFramebuffers(1, &framebuffer);
|
||||
@@ -259,6 +273,46 @@ TEST_F(FramebufferTest, NamedFramebufferDrawBuffersDoNotModifyDefaultFramebuffer
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(FramebufferTest, DefaultFramebufferReadBufferAcceptsGLBackAlias) {
|
||||
const auto defaultRead =
|
||||
MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();
|
||||
|
||||
MG_Impl::GLImpl::ReadBuffer(GL_BACK);
|
||||
|
||||
EXPECT_EQ(defaultRead->GetReadBuffer(), FramebufferAttachmentType::BackLeft);
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(FramebufferTest, DefaultFramebufferDrawBufferAcceptsGLFrontAlias) {
|
||||
const auto defaultDraw =
|
||||
MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
|
||||
|
||||
MG_Impl::GLImpl::DrawBuffer(GL_FRONT);
|
||||
|
||||
EXPECT_EQ(defaultDraw->GetDrawBuffers()[0], FramebufferAttachmentType::FrontLeft);
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(FramebufferTest, DefaultFramebufferProvidesTextureAttachmentsForFrontAndBackAliases) {
|
||||
const auto defaultFramebuffer =
|
||||
MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
|
||||
ASSERT_NE(defaultFramebuffer, nullptr);
|
||||
|
||||
const auto& frontLeft = defaultFramebuffer->GetAttachment(FramebufferAttachmentType::FrontLeft);
|
||||
const auto& frontRight = defaultFramebuffer->GetAttachment(FramebufferAttachmentType::FrontRight);
|
||||
const auto& backLeft = defaultFramebuffer->GetAttachment(FramebufferAttachmentType::BackLeft);
|
||||
const auto& backRight = defaultFramebuffer->GetAttachment(FramebufferAttachmentType::BackRight);
|
||||
|
||||
EXPECT_TRUE(frontLeft.IsTexture());
|
||||
EXPECT_TRUE(frontRight.IsTexture());
|
||||
EXPECT_TRUE(backLeft.IsTexture());
|
||||
EXPECT_TRUE(backRight.IsTexture());
|
||||
EXPECT_TRUE(frontLeft.IsComplete());
|
||||
EXPECT_TRUE(frontRight.IsComplete());
|
||||
EXPECT_TRUE(backLeft.IsComplete());
|
||||
EXPECT_TRUE(backRight.IsComplete());
|
||||
}
|
||||
|
||||
TEST_F(FramebufferTest, ClearNamedFramebufferfvUsesNamedObjectWithoutChangingBindings) {
|
||||
GLuint framebuffer = 0;
|
||||
MG_Impl::GLImpl::CreateFramebuffers(1, &framebuffer);
|
||||
|
||||
@@ -165,6 +165,18 @@ TEST(DirectVulkanSanity, AdvertisesTextureStorageForDirectStateAccess) {
|
||||
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_texture_storage), extensions.end());
|
||||
}
|
||||
|
||||
TEST(DirectVulkanSanity, RenderPassExtentUsesSwapchainSizeOnlyForDefaultFramebuffer) {
|
||||
using MobileGL::MG_Backend::DirectVulkan::ResolveRenderPassFramebufferExtent;
|
||||
|
||||
const MobileGL::TextureSize attachmentExtent = {512, 512, 1};
|
||||
const VkExtent2D swapchainExtent = {3200u, 1440u};
|
||||
|
||||
EXPECT_EQ(ResolveRenderPassFramebufferExtent(true, attachmentExtent, swapchainExtent),
|
||||
MobileGL::IntVec2(3200, 1440));
|
||||
EXPECT_EQ(ResolveRenderPassFramebufferExtent(false, attachmentExtent, swapchainExtent),
|
||||
MobileGL::IntVec2(512, 512));
|
||||
}
|
||||
|
||||
TEST(DirectVulkanSanity, AdvertisesVoxyRequiredRenderingExtensionsWithoutRaisingGLVersion) {
|
||||
MobileGL::MG_Backend::DirectVulkan::BackendObject_DirectVulkan backend;
|
||||
const auto& rendererInfo = backend.GetRendererInfo().RendererGLInfo;
|
||||
|
||||
@@ -18,3 +18,21 @@ target_link_libraries(
|
||||
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(TextureTest)
|
||||
|
||||
add_executable(
|
||||
VkClearManagerTest
|
||||
VkClearManagerTest.cpp
|
||||
)
|
||||
|
||||
target_include_directories(VkClearManagerTest PRIVATE
|
||||
${MGL_ROOT}/include
|
||||
${MGL_ROOT}/MobileGL
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
VkClearManagerTest PRIVATE
|
||||
GTest::gtest_main
|
||||
${LINK_LIBRARIES}
|
||||
)
|
||||
|
||||
gtest_discover_tests(VkClearManagerTest)
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
// MobileGL - MobileGL/MG_Test/Texture/VkClearManagerTest.cpp
|
||||
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||
// Licensed under the GNU Lesser General Public License v3.0:
|
||||
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// End of Source File Header
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Includes.h"
|
||||
#include "Init.h"
|
||||
#include <MG_Backend/DirectVulkan/Renderer/VkClearManager.h>
|
||||
#include <MG_Impl/GLImpl/Texture/GL_Texture.h>
|
||||
#include <MG_State/GLState/Core.h>
|
||||
|
||||
using namespace MobileGL;
|
||||
using namespace MobileGL::MG_Backend::DirectVulkan;
|
||||
|
||||
class VkClearManagerTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override { MobileGL::Initialize(); }
|
||||
};
|
||||
|
||||
TEST_F(VkClearManagerTest, CollectGarbageRemovesExpiredTexturesAndTheirPendingClears) {
|
||||
VkClearManager clearManager;
|
||||
ASSERT_TRUE(clearManager.Initialize());
|
||||
|
||||
constexpr SizeT kTextureCount = 64;
|
||||
Vector<GLuint> textureNames(kTextureCount, 0);
|
||||
Vector<PendingClearKey> pendingKeys;
|
||||
pendingKeys.reserve(kTextureCount);
|
||||
|
||||
const ClearAttachmentPayload clearPayload{
|
||||
.mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
|
||||
.color = FloatVec4(0.25f, 0.5f, 0.75f, 1.0f),
|
||||
.depth = 0.5f,
|
||||
};
|
||||
|
||||
for (SizeT i = 0; i < kTextureCount; ++i) {
|
||||
MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &textureNames[i]);
|
||||
ASSERT_NE(textureNames[i], 0u);
|
||||
|
||||
{
|
||||
const auto textureObject = MG_State::pGLContext->GetTextureObject(textureNames[i]);
|
||||
ASSERT_NE(textureObject, nullptr);
|
||||
|
||||
const PendingClearKey key = VkClearManager::MakePendingClearKey(textureObject.get());
|
||||
clearManager.QueueClear(clearPayload, textureObject);
|
||||
|
||||
EXPECT_TRUE(clearManager.HasPendingClear(key));
|
||||
pendingKeys.emplace_back(key);
|
||||
}
|
||||
}
|
||||
|
||||
MG_Impl::GLImpl::DeleteTextures(static_cast<GLsizei>(textureNames.size()), textureNames.data());
|
||||
|
||||
for (Int i = 0; i < 255; ++i) {
|
||||
EXPECT_EQ(clearManager.CollectGarbage(), 0u);
|
||||
}
|
||||
EXPECT_EQ(clearManager.CollectGarbage(), kTextureCount);
|
||||
|
||||
ClearAttachmentPayload outPayload{};
|
||||
for (const auto& key : pendingKeys) {
|
||||
EXPECT_FALSE(clearManager.HasPendingClear(key));
|
||||
EXPECT_FALSE(clearManager.GetPendingClear(key, outPayload));
|
||||
}
|
||||
|
||||
GLuint freshTexture = 0;
|
||||
MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &freshTexture);
|
||||
ASSERT_NE(freshTexture, 0u);
|
||||
|
||||
const auto freshTextureObject = MG_State::pGLContext->GetTextureObject(freshTexture);
|
||||
ASSERT_NE(freshTextureObject, nullptr);
|
||||
EXPECT_FALSE(clearManager.HasPendingClear(freshTextureObject.get()));
|
||||
|
||||
MG_Impl::GLImpl::DeleteTextures(1, &freshTexture);
|
||||
clearManager.Shutdown();
|
||||
}
|
||||
|
||||
TEST_F(VkClearManagerTest, StalePendingClearsAreRejectedBeforePeriodicGarbageCollection) {
|
||||
VkClearManager clearManager;
|
||||
ASSERT_TRUE(clearManager.Initialize());
|
||||
|
||||
GLuint texture = 0;
|
||||
MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture);
|
||||
ASSERT_NE(texture, 0u);
|
||||
|
||||
PendingClearKey key{};
|
||||
{
|
||||
const auto textureObject = MG_State::pGLContext->GetTextureObject(texture);
|
||||
ASSERT_NE(textureObject, nullptr);
|
||||
|
||||
key = VkClearManager::MakePendingClearKey(textureObject.get());
|
||||
clearManager.QueueClear(ClearAttachmentPayload{
|
||||
.mask = GL_COLOR_BUFFER_BIT,
|
||||
.color = FloatVec4(1.0f, 0.25f, 0.5f, 0.75f),
|
||||
}, textureObject);
|
||||
EXPECT_TRUE(clearManager.HasPendingClear(key));
|
||||
}
|
||||
|
||||
MG_Impl::GLImpl::DeleteTextures(1, &texture);
|
||||
|
||||
ClearAttachmentPayload outPayload{};
|
||||
EXPECT_FALSE(clearManager.HasPendingClear(key));
|
||||
EXPECT_FALSE(clearManager.GetPendingClear(key, outPayload));
|
||||
EXPECT_FALSE(clearManager.HasPendingClear(key));
|
||||
|
||||
clearManager.Shutdown();
|
||||
}
|
||||
@@ -36,10 +36,12 @@ namespace MobileGL {
|
||||
return FramebufferAttachmentType::Depth;
|
||||
case GL_STENCIL_ATTACHMENT:
|
||||
return FramebufferAttachmentType::Stencil;
|
||||
case GL_FRONT:
|
||||
case GL_FRONT_LEFT:
|
||||
return FramebufferAttachmentType::FrontLeft;
|
||||
case GL_FRONT_RIGHT:
|
||||
return FramebufferAttachmentType::FrontRight;
|
||||
case GL_BACK:
|
||||
case GL_BACK_LEFT:
|
||||
return FramebufferAttachmentType::BackLeft;
|
||||
case GL_BACK_RIGHT:
|
||||
@@ -59,4 +61,4 @@ namespace MobileGL {
|
||||
}
|
||||
}
|
||||
} // namespace MG_Util
|
||||
} // namespace MobileGL
|
||||
} // namespace MobileGL
|
||||
|
||||
Reference in New Issue
Block a user