mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-13 06:38:31 +09:00
[Fix, Test] (MG_Backend/DirectGLES): backend framebuffer, renderbuffer and sampler twins release their driver ids - a framebuffer per readback leaked the driver into stale pixels
This commit is contained in:
@@ -1214,7 +1214,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (g_unitTextureSyncListValid &&
|
||||
g_unitTextureSyncListContextId == keys.contextId &&
|
||||
g_unitTextureSyncListMaxUnit == maxTouchedUnit &&
|
||||
g_unitTextureSyncListContextGeneration == g_textureContextGeneration &&
|
||||
g_unitTextureSyncListContextGeneration == g_backendContextGeneration &&
|
||||
g_unitTextureSyncListEpoch == unitBindingsEpoch &&
|
||||
g_unitTextureSyncListSamplingGeneration == samplingGeneration &&
|
||||
PairingsIntact(g_unitTextureSyncList)) {
|
||||
@@ -1246,7 +1246,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
g_unitTextureSyncListContextId = keys.contextId;
|
||||
g_unitTextureSyncListMaxUnit = maxTouchedUnit;
|
||||
g_unitTextureSyncListContextGeneration = g_textureContextGeneration;
|
||||
g_unitTextureSyncListContextGeneration = g_backendContextGeneration;
|
||||
g_unitTextureSyncListEpoch = unitBindingsEpoch;
|
||||
g_unitTextureSyncListSamplingGeneration = samplingGeneration;
|
||||
g_unitTextureSyncListValid = true;
|
||||
@@ -1274,7 +1274,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_fboTextureSyncListSlotVersion == fboSlotVersion &&
|
||||
g_fboTextureSyncListObjectVersion == fboObjectVersion &&
|
||||
g_fboTextureSyncListContextId == keys.contextId &&
|
||||
g_fboTextureSyncListContextGeneration == g_textureContextGeneration &&
|
||||
g_fboTextureSyncListContextGeneration == g_backendContextGeneration &&
|
||||
PairingsIntact(g_fboTextureSyncList);
|
||||
if (fboListValid) {
|
||||
for (const auto& entry : g_fboTextureSyncList) {
|
||||
@@ -1302,7 +1302,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_fboTextureSyncListSlotVersion = fboSlotVersion;
|
||||
g_fboTextureSyncListObjectVersion = fboObjectVersion;
|
||||
g_fboTextureSyncListContextId = keys.contextId;
|
||||
g_fboTextureSyncListContextGeneration = g_textureContextGeneration;
|
||||
g_fboTextureSyncListContextGeneration = g_backendContextGeneration;
|
||||
}
|
||||
} else {
|
||||
g_fboTextureSyncListFbo = nullptr;
|
||||
@@ -2423,7 +2423,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
static_cast<SizeT>(maxTouchedUnit + 1) * sizeof(SamplerImpl::g_boundSamplersCache[0]);
|
||||
if (g_unitSamplerWalkValid && g_unitSamplerWalkContextId == keys.contextId &&
|
||||
g_unitSamplerWalkEpoch == keys.unitBindingsEpoch && g_unitSamplerWalkMaxUnit == maxTouchedUnit &&
|
||||
g_unitSamplerWalkContextGeneration == TextureImpl::g_textureContextGeneration &&
|
||||
g_unitSamplerWalkContextGeneration == g_backendContextGeneration &&
|
||||
std::memcmp(g_unitSamplerWalkRows.data(), SamplerImpl::g_boundSamplersCache.data(), rowBytes) == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -2444,7 +2444,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_unitSamplerWalkContextId = keys.contextId;
|
||||
g_unitSamplerWalkEpoch = keys.unitBindingsEpoch;
|
||||
g_unitSamplerWalkMaxUnit = maxTouchedUnit;
|
||||
g_unitSamplerWalkContextGeneration = TextureImpl::g_textureContextGeneration;
|
||||
g_unitSamplerWalkContextGeneration = g_backendContextGeneration;
|
||||
std::memcpy(g_unitSamplerWalkRows.data(), SamplerImpl::g_boundSamplersCache.data(), rowBytes);
|
||||
g_unitSamplerWalkValid = true;
|
||||
}
|
||||
@@ -2554,7 +2554,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
memo.programBackendStateVersion ==
|
||||
(currentProgram ? currentProgram->GetBackendStateVersion() : 0) &&
|
||||
memo.programLinked == (currentProgram && currentProgram->GetLinkStatus()) &&
|
||||
memo.contextGeneration == TextureImpl::g_textureContextGeneration;
|
||||
memo.contextGeneration == g_backendContextGeneration;
|
||||
// Short-circuited: the shadow compare is only meaningful once the key (and with it the
|
||||
// snapshotted row count) matches.
|
||||
if (!keysMatch || std::memcmp(memo.boundTextures.data(), TextureImpl::g_boundTexturesCache.data(),
|
||||
@@ -2569,7 +2569,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
memo.programLifetimeId = currentProgram ? currentProgram->GetLifetimeId() : 0;
|
||||
memo.programBackendStateVersion = currentProgram ? currentProgram->GetBackendStateVersion() : 0;
|
||||
memo.programLinked = currentProgram && currentProgram->GetLinkStatus();
|
||||
memo.contextGeneration = TextureImpl::g_textureContextGeneration;
|
||||
memo.contextGeneration = g_backendContextGeneration;
|
||||
std::memcpy(memo.boundTextures.data(), TextureImpl::g_boundTexturesCache.data(), shadowBytes);
|
||||
memo.valid = true;
|
||||
}
|
||||
@@ -2744,7 +2744,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
samplerPassMemo.unitBindingsEpoch == keys.unitBindingsEpoch &&
|
||||
samplerPassMemo.samplingGeneration == keys.samplingGeneration &&
|
||||
samplerPassMemo.backendStateVersion == programBackendStateVersion &&
|
||||
samplerPassMemo.textureContextGeneration == TextureImpl::g_textureContextGeneration;
|
||||
samplerPassMemo.textureContextGeneration == g_backendContextGeneration;
|
||||
if (samplerPassClean) {
|
||||
for (Uint i = 0; i < samplerPassMemo.count; ++i) {
|
||||
if (SamplerImpl::g_boundSamplersCache[samplerPassMemo.units[i]] !=
|
||||
@@ -2843,7 +2843,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
samplerPassMemo.unitBindingsEpoch = keys.unitBindingsEpoch;
|
||||
samplerPassMemo.samplingGeneration = keys.samplingGeneration;
|
||||
samplerPassMemo.backendStateVersion = programBackendStateVersion;
|
||||
samplerPassMemo.textureContextGeneration = TextureImpl::g_textureContextGeneration;
|
||||
samplerPassMemo.textureContextGeneration = g_backendContextGeneration;
|
||||
samplerPassMemo.valid = true;
|
||||
}
|
||||
}
|
||||
@@ -3605,12 +3605,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s_resolveContextGeneration != TextureImpl::g_textureContextGeneration) {
|
||||
if (s_resolveContextGeneration != g_backendContextGeneration) {
|
||||
// The ids belonged to a dead context; the context reclaimed them with it.
|
||||
s_resolveFramebuffer = 0;
|
||||
s_resolveRenderbuffer = 0;
|
||||
s_resolveFormat = 0;
|
||||
s_resolveContextGeneration = TextureImpl::g_textureContextGeneration;
|
||||
s_resolveContextGeneration = g_backendContextGeneration;
|
||||
}
|
||||
if (s_resolveFramebuffer == 0) {
|
||||
g_GLESFuncs.glGenFramebuffers(1, &s_resolveFramebuffer);
|
||||
@@ -3758,7 +3758,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
|
||||
static Bool EnsureResources() {
|
||||
if (s_contextGeneration != TextureImpl::g_textureContextGeneration) {
|
||||
if (s_contextGeneration != g_backendContextGeneration) {
|
||||
// The ids belonged to a dead context; the context reclaimed them with it.
|
||||
s_framebuffer = 0;
|
||||
s_texture = 0;
|
||||
@@ -3769,7 +3769,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
s_depthProgram = 0;
|
||||
s_stencilProgram = 0;
|
||||
s_programsFailed = false;
|
||||
s_contextGeneration = TextureImpl::g_textureContextGeneration;
|
||||
s_contextGeneration = g_backendContextGeneration;
|
||||
}
|
||||
if (s_programsFailed) {
|
||||
return false;
|
||||
@@ -7482,7 +7482,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
PixelStoreImpl::InvalidatePackStateCache();
|
||||
// Texture ids belong to the dying context; wrappers destroyed later must
|
||||
// not glDeleteTextures a recycled name in a successor context.
|
||||
++TextureImpl::g_textureContextGeneration;
|
||||
++g_backendContextGeneration;
|
||||
g_backendContextOwnerThread.store(std::thread::id{}, std::memory_order_release);
|
||||
// Outstanding fence handles now refer to a dead context; treat them as
|
||||
// signaled from here on.
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
#include <regex>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectGLES {
|
||||
Uint g_backendContextGeneration = 1;
|
||||
|
||||
constexpr Bool PREFER_MAP_BUFFER_RANGE_FOR_BUFFER_SYNC = false;
|
||||
constexpr const char* BASE_INSTANCE_UNIFORM_NAME = "mg_BaseInstance";
|
||||
constexpr const char* DRAW_ID_UNIFORM_NAME = "mg_DrawID";
|
||||
@@ -1646,7 +1648,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||
#endif
|
||||
g_GLESFuncs.glGenTextures(1, &m_backendTextureId);
|
||||
m_contextGeneration = g_textureContextGeneration;
|
||||
m_contextGeneration = g_backendContextGeneration;
|
||||
if (m_backendTextureId == 0) {
|
||||
MGLOG_E("Failed to generate texture object.");
|
||||
MGLOG_E("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
|
||||
@@ -1673,7 +1675,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_contextGeneration == g_textureContextGeneration && g_GLESFuncs.glDeleteTextures) {
|
||||
if (m_contextGeneration == g_backendContextGeneration && g_GLESFuncs.glDeleteTextures) {
|
||||
g_GLESFuncs.glDeleteTextures(1, &m_backendTextureId);
|
||||
}
|
||||
m_backendTextureId = 0;
|
||||
@@ -1712,7 +1714,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
void BackendTextureObject::RecreateBackendTexture() {
|
||||
if (m_backendTextureId != 0) {
|
||||
ScratchFBOImpl::NoteTextureIdDeleted(m_backendTextureId);
|
||||
if (m_contextGeneration == g_textureContextGeneration) {
|
||||
if (m_contextGeneration == g_backendContextGeneration) {
|
||||
g_GLESFuncs.glDeleteTextures(1, &m_backendTextureId);
|
||||
}
|
||||
for (auto& unitCache : g_boundTexturesCache) {
|
||||
@@ -1725,7 +1727,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
|
||||
g_GLESFuncs.glGenTextures(1, &m_backendTextureId);
|
||||
m_contextGeneration = g_textureContextGeneration;
|
||||
m_contextGeneration = g_backendContextGeneration;
|
||||
if (m_backendTextureId == 0) {
|
||||
MGLOG_E("Failed to regenerate texture object.");
|
||||
MGLOG_E("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
|
||||
@@ -3081,7 +3083,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
|
||||
Uint g_activeTextureUnit = 0;
|
||||
Uint g_textureContextGeneration = 1;
|
||||
Array<Array<BackendTextureObject*, (SizeT)TextureTarget::TextureTargetCount>,
|
||||
MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS>
|
||||
g_boundTexturesCache;
|
||||
@@ -3100,6 +3101,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
m_backendColorSlots[i] = GL_COLOR_ATTACHMENT0 + i;
|
||||
}
|
||||
g_GLESFuncs.glGenFramebuffers(1, &m_backendFBOId);
|
||||
m_contextGeneration = g_backendContextGeneration;
|
||||
if (m_backendFBOId == 0) {
|
||||
MGLOG_E("Failed to generate framebuffer object.");
|
||||
MGLOG_E("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
|
||||
@@ -3108,6 +3110,22 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
}
|
||||
|
||||
BackendFramebufferObject::~BackendFramebufferObject() {
|
||||
if (InProcessTeardown()) {
|
||||
return; // see InProcessTeardown(): the driver may be unloaded already
|
||||
}
|
||||
if (m_backendFBOId == 0) {
|
||||
return;
|
||||
}
|
||||
// Scrub the binding shadow whether or not the id can still be deleted: a
|
||||
// recycled name must never satisfy the shadow's dedup.
|
||||
NoteFramebufferIdDeleted(m_backendFBOId);
|
||||
if (m_contextGeneration == g_backendContextGeneration && g_GLESFuncs.glDeleteFramebuffers) {
|
||||
g_GLESFuncs.glDeleteFramebuffers(1, &m_backendFBOId);
|
||||
}
|
||||
m_backendFBOId = 0;
|
||||
}
|
||||
|
||||
void BackendFramebufferObject::Bind(FramebufferTarget target) const {
|
||||
#ifdef TRACY_ENABLE
|
||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||
@@ -3163,6 +3181,17 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
return g_driverFBOBindings[idx];
|
||||
}
|
||||
|
||||
void NoteFramebufferIdDeleted(Uint id) {
|
||||
if (id == 0) {
|
||||
return;
|
||||
}
|
||||
for (SizeT idx = 0; idx < g_driverFBOBindings.size(); ++idx) {
|
||||
if (g_driverFBOBindingKnown[idx] && g_driverFBOBindings[idx] == id) {
|
||||
g_driverFBOBindings[idx] = 0; // glDeleteFramebuffers reverts a bound FBO to 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InvalidateFramebufferBindingCache() {
|
||||
g_driverFBOBindings = {0, 0};
|
||||
g_driverFBOBindingKnown = {false, false};
|
||||
@@ -4570,6 +4599,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||
#endif
|
||||
g_GLESFuncs.glGenSamplers(1, &m_backendSamplerId);
|
||||
m_contextGeneration = g_backendContextGeneration;
|
||||
if (m_backendSamplerId == 0) {
|
||||
MGLOG_E("Failed to generate sampler object.");
|
||||
MGLOG_E("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
|
||||
@@ -4578,6 +4608,26 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
}
|
||||
|
||||
BackendSamplerObject::~BackendSamplerObject() {
|
||||
if (InProcessTeardown()) {
|
||||
return; // see InProcessTeardown(): the driver may be unloaded already
|
||||
}
|
||||
if (m_backendSamplerId == 0) {
|
||||
return;
|
||||
}
|
||||
// Scrub the unit shadow whether or not the id can still be deleted - the next
|
||||
// twin can land on this heap address and would otherwise false-skip its Bind.
|
||||
for (auto& boundSampler : g_boundSamplersCache) {
|
||||
if (boundSampler == this) {
|
||||
boundSampler = nullptr; // glDeleteSamplers unbinds from every unit
|
||||
}
|
||||
}
|
||||
if (m_contextGeneration == g_backendContextGeneration && g_GLESFuncs.glDeleteSamplers) {
|
||||
g_GLESFuncs.glDeleteSamplers(1, &m_backendSamplerId);
|
||||
}
|
||||
m_backendSamplerId = 0;
|
||||
}
|
||||
|
||||
void BackendSamplerObject::SyncToBackend(
|
||||
const SharedPtr<MG_State::GLState::SamplerObject>& stateSamplerObject) {
|
||||
#ifdef TRACY_ENABLE
|
||||
@@ -4693,12 +4743,28 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||
#endif
|
||||
g_GLESFuncs.glGenRenderbuffers(1, &m_backendRBOId);
|
||||
m_contextGeneration = g_backendContextGeneration;
|
||||
if (m_backendRBOId == 0) {
|
||||
MGLOG_E("Failed to generate renderbuffer object.");
|
||||
MGLOG_E("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
BackendRenderbufferObject::~BackendRenderbufferObject() {
|
||||
if (InProcessTeardown()) {
|
||||
return; // see InProcessTeardown(): the driver may be unloaded already
|
||||
}
|
||||
if (m_backendRBOId == 0) {
|
||||
return;
|
||||
}
|
||||
// No driver-level renderbuffer-binding shadow exists (Bind() always issues the
|
||||
// call), so there is nothing to scrub here - only the id to release.
|
||||
if (m_contextGeneration == g_backendContextGeneration && g_GLESFuncs.glDeleteRenderbuffers) {
|
||||
g_GLESFuncs.glDeleteRenderbuffers(1, &m_backendRBOId);
|
||||
}
|
||||
m_backendRBOId = 0;
|
||||
}
|
||||
|
||||
void BackendRenderbufferObject::Bind() const {
|
||||
#ifdef TRACY_ENABLE
|
||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||
|
||||
@@ -36,6 +36,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
Bool InProcessTeardown();
|
||||
void EnsureProcessTeardownSentinel();
|
||||
|
||||
// Generation of the backend ES context that owns the driver ids currently handed
|
||||
// out. Bumped exactly once per DestroyEGLContext. Every backend twin that owns a
|
||||
// driver name (texture, framebuffer, renderbuffer, sampler) stamps this at
|
||||
// construction and compares it in its destructor: a twin outliving its context
|
||||
// must NOT glDelete* its id, because a successor context may already have recycled
|
||||
// that name and the delete would take out a live object of the new context.
|
||||
extern Uint g_backendContextGeneration;
|
||||
|
||||
// Which optional pieces of state a draw needs synchronized before it is issued.
|
||||
// Index/indirect buffer syncs and the instancing-related work are skipped for
|
||||
// draws that provably cannot read them.
|
||||
@@ -657,15 +665,20 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS>
|
||||
g_boundTexturesCache;
|
||||
extern Uint g_activeTextureUnit;
|
||||
// Bumped when the backend ES context is destroyed; texture ids stamped with
|
||||
// an older generation belong to a dead context and must not be deleted.
|
||||
extern Uint g_textureContextGeneration;
|
||||
} // namespace TextureImpl
|
||||
|
||||
namespace FramebufferImpl {
|
||||
class BackendFramebufferObject {
|
||||
public:
|
||||
BackendFramebufferObject();
|
||||
// Deletes the driver framebuffer and scrubs the binding shadow. Without it every
|
||||
// frontend glDeleteFramebuffers leaked one ES framebuffer for the process lifetime;
|
||||
// an app that creates a framebuffer per readback (GL CTS packed_pixels does ~3300
|
||||
// per case) walked the driver into hundreds of megabytes of dead framebuffers and
|
||||
// out of the resources a later attachment needs.
|
||||
~BackendFramebufferObject();
|
||||
BackendFramebufferObject(const BackendFramebufferObject&) = delete;
|
||||
BackendFramebufferObject& operator=(const BackendFramebufferObject&) = delete;
|
||||
void SyncToBackend(const SharedPtr<MG_State::GLState::FramebufferObject>& stateFBOObject,
|
||||
FramebufferTarget asTarget);
|
||||
// Apply only this FBO's read buffer (glReadBuffer) to the backend. Split out so it can
|
||||
@@ -680,6 +693,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
|
||||
private:
|
||||
Uint m_backendFBOId = 0;
|
||||
Uint m_contextGeneration = 0;
|
||||
|
||||
/* this will save buffers in its original form,
|
||||
reversion, absence or not consecutive are all allowed, as long as GL spec allows it
|
||||
@@ -821,6 +835,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
void BindFramebufferId(GLenum fbTarget, Uint id);
|
||||
Uint CurrentFramebufferBinding(FramebufferTarget target);
|
||||
void InvalidateFramebufferBindingCache();
|
||||
// A driver framebuffer id is about to be deleted: ES reverts every target that
|
||||
// currently binds it to 0, so the binding shadow has to follow or the next
|
||||
// BindFramebufferId(0) would be deduped away and leave the deleted name bound.
|
||||
void NoteFramebufferIdDeleted(Uint id);
|
||||
} // namespace FramebufferImpl
|
||||
|
||||
// Shared scratch framebuffers for the readback/copy/blit emulation paths, with a
|
||||
@@ -1087,12 +1105,19 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
class BackendSamplerObject {
|
||||
public:
|
||||
BackendSamplerObject();
|
||||
// Deletes the driver sampler and clears the units whose binding shadow still names
|
||||
// this twin (a recycled heap address would otherwise false-skip a later Bind).
|
||||
// Frontend glDeleteSamplers used to leak the backend id for the process lifetime.
|
||||
~BackendSamplerObject();
|
||||
BackendSamplerObject(const BackendSamplerObject&) = delete;
|
||||
BackendSamplerObject& operator=(const BackendSamplerObject&) = delete;
|
||||
void SyncToBackend(const SharedPtr<MG_State::GLState::SamplerObject>& stateSamplerObject);
|
||||
void Bind(Uint unit);
|
||||
Uint GetBackendSamplerId() const;
|
||||
|
||||
private:
|
||||
Uint m_backendSamplerId = 0;
|
||||
Uint m_contextGeneration = 0;
|
||||
Bool m_isInitialized = false;
|
||||
SamplerParameters m_cacheSamplerParameters;
|
||||
Uint16 m_syncedSamplerVersion = 0;
|
||||
@@ -1110,12 +1135,18 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
class BackendRenderbufferObject {
|
||||
public:
|
||||
BackendRenderbufferObject();
|
||||
// Deletes the driver renderbuffer; frontend glDeleteRenderbuffers used to leak it
|
||||
// (with its whole image allocation) for the process lifetime.
|
||||
~BackendRenderbufferObject();
|
||||
BackendRenderbufferObject(const BackendRenderbufferObject&) = delete;
|
||||
BackendRenderbufferObject& operator=(const BackendRenderbufferObject&) = delete;
|
||||
void SyncToBackend(const SharedPtr<MG_State::GLState::RenderbufferObject>& stateRBOObject);
|
||||
Uint GetBackendRenderbufferId() const { return m_backendRBOId; }
|
||||
void Bind() const;
|
||||
|
||||
private:
|
||||
Uint m_backendRBOId = 0;
|
||||
Uint m_contextGeneration = 0;
|
||||
Bool m_isInitialized = false;
|
||||
TextureInternalFormat m_cacheInternalFormat = TextureInternalFormat::Unknown;
|
||||
Int m_cacheWidth = 0;
|
||||
|
||||
Reference in New Issue
Block a user