From df799dd12466a319c5616027980db0dbcadaa7de Mon Sep 17 00:00:00 2001 From: degasus Date: Sat, 21 Nov 2015 10:20:34 +0100 Subject: [PATCH 1/5] VideoCommon: Create default implementation for state setters It's fine to pull those within the backends, so there is no need to enforce them to implement this interface. --- Source/Core/VideoBackends/OGL/Render.h | 4 ---- Source/Core/VideoCommon/RenderBase.h | 31 +++++++++++++------------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index a6544a6049..b939911cee 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -83,10 +83,6 @@ public: void SetInterlacingMode() override; void SetViewport() override; - // TODO: Implement and use these - void ApplyState(bool bUseDstAlpha) override {} - void RestoreState() override {} - void RenderText(const std::string& text, int left, int top, u32 color) override; void FlipImageData(u8 *data, int w, int h, int pixel_width = 3); diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index c717483d1e..ad53f2f20c 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -58,19 +58,22 @@ public: PP_EFB_COPY_CLOCKS }; - virtual void SetColorMask() = 0; - virtual void SetBlendMode(bool forceUpdate) = 0; - virtual void SetScissorRect(const EFBRectangle& rc) = 0; - virtual void SetGenerationMode() = 0; - virtual void SetDepthMode() = 0; - virtual void SetLogicOpMode() = 0; - virtual void SetDitherMode() = 0; - virtual void SetSamplerState(int stage, int texindex, bool custom_tex) = 0; - virtual void SetInterlacingMode() = 0; - virtual void SetViewport() = 0; + virtual void SetColorMask() {} + virtual void SetBlendMode(bool forceUpdate) {} + virtual void SetScissorRect(const EFBRectangle& rc) {} + virtual void SetGenerationMode() {} + virtual void SetDepthMode() {} + virtual void SetLogicOpMode() {} + virtual void SetDitherMode() {} + virtual void SetSamplerState(int stage, int texindex, bool custom_tex) {} + virtual void SetInterlacingMode() {} + virtual void SetViewport() {} - virtual void ApplyState(bool bUseDstAlpha) = 0; - virtual void RestoreState() = 0; + virtual void ApplyState(bool bUseDstAlpha) {} + virtual void RestoreState() {} + + virtual void ResetAPIState() {} + virtual void RestoreAPIState() {} // Ideal internal resolution - determined by display resolution (automatic scaling) and/or a multiple of the native EFB resolution static int GetTargetWidth() { return s_target_width; } @@ -117,10 +120,6 @@ public: virtual u16 BBoxRead(int index) = 0; virtual void BBoxWrite(int index, u16 value) = 0; - // What's the real difference between these? Too similar names. - virtual void ResetAPIState() = 0; - virtual void RestoreAPIState() = 0; - // Finish up the current frame, print some stats static void Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc,float Gamma = 1.0f); virtual void SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc, float Gamma = 1.0f) = 0; From fc00598785fc02393864dd3521056d70086ee4b7 Mon Sep 17 00:00:00 2001 From: degasus Date: Sat, 21 Nov 2015 10:32:07 +0100 Subject: [PATCH 2/5] NativeVertexFormat: Inline Initialize in contructor They were only called at once, so no need to seperate them. This also removes the only dereference of the NativeVertexFormat in VideoCommon, so backends may just return nullptr. --- .../VideoBackends/D3D/NativeVertexFormat.cpp | 12 +++---- Source/Core/VideoBackends/D3D/VertexManager.h | 2 +- .../VideoBackends/OGL/NativeVertexFormat.cpp | 31 ++++++++----------- Source/Core/VideoBackends/OGL/VertexManager.h | 5 ++- Source/Core/VideoCommon/NativeVertexFormat.h | 1 - .../Core/VideoCommon/VertexLoaderManager.cpp | 3 +- Source/Core/VideoCommon/VertexManagerBase.h | 2 +- 7 files changed, 24 insertions(+), 32 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp b/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp index 4b2951a689..be29d9fbfe 100644 --- a/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp +++ b/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp @@ -20,16 +20,15 @@ class D3DVertexFormat : public NativeVertexFormat ID3D11InputLayout* m_layout; public: - D3DVertexFormat() : m_num_elems(0), m_layout(nullptr) {} + D3DVertexFormat(const PortableVertexDeclaration& vtx_decl); ~D3DVertexFormat() { SAFE_RELEASE(m_layout); } - void Initialize(const PortableVertexDeclaration &_vtx_decl); void SetupVertexPointers(); }; -NativeVertexFormat* VertexManager::CreateNativeVertexFormat() +NativeVertexFormat* VertexManager::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) { - return new D3DVertexFormat(); + return new D3DVertexFormat(vtx_decl); } static const DXGI_FORMAT d3d_format_lookup[5*4*2] = @@ -57,9 +56,10 @@ DXGI_FORMAT VarToD3D(VarType t, int size, bool integer) return retval; } -void D3DVertexFormat::Initialize(const PortableVertexDeclaration &_vtx_decl) +D3DVertexFormat::D3DVertexFormat(const PortableVertexDeclaration& _vtx_decl) + : m_num_elems(0), m_layout(nullptr) { - vtx_decl = _vtx_decl; + this->vtx_decl = _vtx_decl; memset(m_elems, 0, sizeof(m_elems)); const AttributeFormat* format = &_vtx_decl.position; diff --git a/Source/Core/VideoBackends/D3D/VertexManager.h b/Source/Core/VideoBackends/D3D/VertexManager.h index d963f81266..2cc6b555d7 100644 --- a/Source/Core/VideoBackends/D3D/VertexManager.h +++ b/Source/Core/VideoBackends/D3D/VertexManager.h @@ -15,7 +15,7 @@ public: VertexManager(); ~VertexManager(); - NativeVertexFormat* CreateNativeVertexFormat() override; + NativeVertexFormat* CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) override; void CreateDeviceObjects() override; void DestroyDeviceObjects() override; diff --git a/Source/Core/VideoBackends/OGL/NativeVertexFormat.cpp b/Source/Core/VideoBackends/OGL/NativeVertexFormat.cpp index f7f3ab301a..004692999f 100644 --- a/Source/Core/VideoBackends/OGL/NativeVertexFormat.cpp +++ b/Source/Core/VideoBackends/OGL/NativeVertexFormat.cpp @@ -21,19 +21,9 @@ namespace OGL { -NativeVertexFormat* VertexManager::CreateNativeVertexFormat() +NativeVertexFormat* VertexManager::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) { - return new GLVertexFormat(); -} - -GLVertexFormat::GLVertexFormat() -{ - -} - -GLVertexFormat::~GLVertexFormat() -{ - glDeleteVertexArrays(1, &VAO); + return new GLVertexFormat(vtx_decl); } static inline GLuint VarToGL(VarType t) @@ -56,7 +46,7 @@ static void SetPointer(u32 attrib, u32 stride, const AttributeFormat &format) glVertexAttribPointer(attrib, format.components, VarToGL(format.type), true, stride, (u8*)nullptr + format.offset); } -void GLVertexFormat::Initialize(const PortableVertexDeclaration &_vtx_decl) +GLVertexFormat::GLVertexFormat(const PortableVertexDeclaration& _vtx_decl) { this->vtx_decl = _vtx_decl; u32 vertex_stride = _vtx_decl.stride; @@ -74,22 +64,27 @@ void GLVertexFormat::Initialize(const PortableVertexDeclaration &_vtx_decl) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vm->m_index_buffers); glBindBuffer(GL_ARRAY_BUFFER, vm->m_vertex_buffers); - SetPointer(SHADER_POSITION_ATTRIB, vertex_stride, vtx_decl.position); + SetPointer(SHADER_POSITION_ATTRIB, vertex_stride, _vtx_decl.position); for (int i = 0; i < 3; i++) - SetPointer(SHADER_NORM0_ATTRIB+i, vertex_stride, vtx_decl.normals[i]); + SetPointer(SHADER_NORM0_ATTRIB+i, vertex_stride, _vtx_decl.normals[i]); for (int i = 0; i < 2; i++) - SetPointer(SHADER_COLOR0_ATTRIB+i, vertex_stride, vtx_decl.colors[i]); + SetPointer(SHADER_COLOR0_ATTRIB+i, vertex_stride, _vtx_decl.colors[i]); for (int i = 0; i < 8; i++) - SetPointer(SHADER_TEXTURE0_ATTRIB+i, vertex_stride, vtx_decl.texcoords[i]); + SetPointer(SHADER_TEXTURE0_ATTRIB+i, vertex_stride, _vtx_decl.texcoords[i]); - SetPointer(SHADER_POSMTX_ATTRIB, vertex_stride, vtx_decl.posmtx); + SetPointer(SHADER_POSMTX_ATTRIB, vertex_stride, _vtx_decl.posmtx); vm->m_last_vao = VAO; } +GLVertexFormat::~GLVertexFormat() +{ + glDeleteVertexArrays(1, &VAO); +} + void GLVertexFormat::SetupVertexPointers() { } diff --git a/Source/Core/VideoBackends/OGL/VertexManager.h b/Source/Core/VideoBackends/OGL/VertexManager.h index 11d7e69e48..d674d5d792 100644 --- a/Source/Core/VideoBackends/OGL/VertexManager.h +++ b/Source/Core/VideoBackends/OGL/VertexManager.h @@ -15,10 +15,9 @@ namespace OGL class GLVertexFormat : public NativeVertexFormat { public: - GLVertexFormat(); + GLVertexFormat(const PortableVertexDeclaration& vtx_decl); ~GLVertexFormat(); - void Initialize(const PortableVertexDeclaration &_vtx_decl) override; void SetupVertexPointers() override; GLuint VAO; @@ -31,7 +30,7 @@ class VertexManager : public VertexManagerBase public: VertexManager(); ~VertexManager(); - NativeVertexFormat* CreateNativeVertexFormat() override; + NativeVertexFormat* CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) override; void CreateDeviceObjects() override; void DestroyDeviceObjects() override; diff --git a/Source/Core/VideoCommon/NativeVertexFormat.h b/Source/Core/VideoCommon/NativeVertexFormat.h index 0f48341deb..ff461a02fd 100644 --- a/Source/Core/VideoCommon/NativeVertexFormat.h +++ b/Source/Core/VideoCommon/NativeVertexFormat.h @@ -108,7 +108,6 @@ class NativeVertexFormat : NonCopyable public: virtual ~NativeVertexFormat() {} - virtual void Initialize(const PortableVertexDeclaration &vtx_decl) = 0; virtual void SetupVertexPointers() = 0; u32 GetVertexStride() const { return vtx_decl.stride; } diff --git a/Source/Core/VideoCommon/VertexLoaderManager.cpp b/Source/Core/VideoCommon/VertexLoaderManager.cpp index 50f50cb78e..25c4c6adb0 100644 --- a/Source/Core/VideoCommon/VertexLoaderManager.cpp +++ b/Source/Core/VideoCommon/VertexLoaderManager.cpp @@ -152,8 +152,7 @@ static VertexLoaderBase* RefreshLoader(int vtx_attr_group, bool preprocess = fal std::unique_ptr& native = s_native_vertex_map[format]; if (!native) { - native.reset(g_vertex_manager->CreateNativeVertexFormat()); - native->Initialize(format); + native.reset(g_vertex_manager->CreateNativeVertexFormat(format)); } loader->m_native_vertex_format = native.get(); } diff --git a/Source/Core/VideoCommon/VertexManagerBase.h b/Source/Core/VideoCommon/VertexManagerBase.h index 316bcd4123..26b02ae122 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.h +++ b/Source/Core/VideoCommon/VertexManagerBase.h @@ -50,7 +50,7 @@ public: static void Flush(); - virtual ::NativeVertexFormat* CreateNativeVertexFormat() = 0; + virtual NativeVertexFormat* CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) = 0; static void DoState(PointerWrap& p); From c9dc5fb3762dd98462061538cfa9789216b47477 Mon Sep 17 00:00:00 2001 From: degasus Date: Sat, 21 Nov 2015 10:41:05 +0100 Subject: [PATCH 3/5] TextureCache: Allow the backends to return nullptr for textures. --- Source/Core/VideoCommon/TextureCacheBase.cpp | 119 +++++++++++-------- 1 file changed, 67 insertions(+), 52 deletions(-) diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index f6d07a476b..c5330d6b86 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -265,20 +265,23 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::DoPartialTextureUpdates(Tex newconfig.height = h; newconfig.rendertarget = true; TCacheEntryBase* newentry = AllocateTexture(newconfig); - newentry->SetGeneralParameters(entry_to_update->addr, entry_to_update->size_in_bytes, entry_to_update->format); - newentry->SetDimensions(entry_to_update->native_width, entry_to_update->native_height, 1); - newentry->SetHashes(entry_to_update->base_hash, entry_to_update->hash); - newentry->frameCount = frameCount; - newentry->is_efb_copy = false; - srcrect.right = entry_to_update->config.width; - srcrect.bottom = entry_to_update->config.height; - dstrect.right = w; - dstrect.bottom = h; - newentry->CopyRectangleFromTexture(entry_to_update, srcrect, dstrect); - entry_to_update = newentry; - u64 key = iter_t->first; - iter_t = FreeTexture(iter_t); - textures_by_address.emplace(key, entry_to_update); + if (newentry) + { + newentry->SetGeneralParameters(entry_to_update->addr, entry_to_update->size_in_bytes, entry_to_update->format); + newentry->SetDimensions(entry_to_update->native_width, entry_to_update->native_height, 1); + newentry->SetHashes(entry_to_update->base_hash, entry_to_update->hash); + newentry->frameCount = frameCount; + newentry->is_efb_copy = false; + srcrect.right = entry_to_update->config.width; + srcrect.bottom = entry_to_update->config.height; + dstrect.right = w; + dstrect.bottom = h; + newentry->CopyRectangleFromTexture(entry_to_update, srcrect, dstrect); + entry_to_update = newentry; + u64 key = iter_t->first; + iter_t = FreeTexture(iter_t); + textures_by_address.emplace(key, entry_to_update); + } } } srcrect.right = entry->config.width; @@ -544,15 +547,18 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) config.layers = FramebufferManagerBase::GetEFBLayers(); TCacheEntryBase *decoded_entry = AllocateTexture(config); - decoded_entry->SetGeneralParameters(address, texture_size, full_format); - decoded_entry->SetDimensions(entry->native_width, entry->native_height, 1); - decoded_entry->SetHashes(base_hash, full_hash); - decoded_entry->frameCount = FRAMECOUNT_INVALID; - decoded_entry->is_efb_copy = false; + if (decoded_entry) + { + decoded_entry->SetGeneralParameters(address, texture_size, full_format); + decoded_entry->SetDimensions(entry->native_width, entry->native_height, 1); + decoded_entry->SetHashes(base_hash, full_hash); + decoded_entry->frameCount = FRAMECOUNT_INVALID; + decoded_entry->is_efb_copy = false; - g_texture_cache->ConvertTexture(decoded_entry, entry, &texMem[tlutaddr], (TlutFormat)tlutfmt); - textures_by_address.emplace((u64)address, decoded_entry); - return ReturnEntry(stage, decoded_entry); + g_texture_cache->ConvertTexture(decoded_entry, entry, &texMem[tlutaddr], (TlutFormat)tlutfmt); + textures_by_address.emplace((u64)address, decoded_entry); + return ReturnEntry(stage, decoded_entry); + } } // Search the texture cache for normal textures by hash @@ -611,6 +617,21 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) } } + // how many levels the allocated texture shall have + const u32 texLevels = hires_tex ? (u32)hires_tex->m_levels.size() : tex_levels; + + // create the entry/texture + TCacheEntryConfig config; + config.width = width; + config.height = height; + config.levels = texLevels; + + TCacheEntryBase* entry = AllocateTexture(config); + GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true); + + if (!entry) + return nullptr; + if (!hires_tex) { if (!(texformat == GX_TF_RGBA8 && from_tmem)) @@ -625,18 +646,6 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) } } - // how many levels the allocated texture shall have - const u32 texLevels = hires_tex ? (u32)hires_tex->m_levels.size() : tex_levels; - - // create the entry/texture - TCacheEntryConfig config; - config.width = width; - config.height = height; - config.levels = texLevels; - - TCacheEntryBase* entry = AllocateTexture(config); - GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true); - iter = textures_by_address.emplace((u64)address, entry); if (g_ActiveConfig.iSafeTextureCache_ColorSamples == 0 || std::max(texture_size, palette_size) <= (u32)g_ActiveConfig.iSafeTextureCache_ColorSamples * 8) @@ -1120,26 +1129,29 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo TCacheEntryBase* entry = AllocateTexture(config); - entry->SetGeneralParameters(dstAddr, 0, dstFormat); - entry->SetDimensions(tex_w, tex_h, 1); - - entry->frameCount = FRAMECOUNT_INVALID; - entry->SetEfbCopy(dstStride); - entry->is_custom_tex = false; - - entry->FromRenderTarget(dst, srcFormat, srcRect, scaleByHalf, cbufid, colmat); - - u64 hash = entry->CalculateHash(); - entry->SetHashes(hash, hash); - - if (g_ActiveConfig.bDumpEFBTarget) + if (entry) { - static int count = 0; - entry->Save(StringFromFormat("%sefb_frame_%i.png", File::GetUserPath(D_DUMPTEXTURES_IDX).c_str(), - count++), 0); - } + entry->SetGeneralParameters(dstAddr, 0, dstFormat); + entry->SetDimensions(tex_w, tex_h, 1); - textures_by_address.emplace((u64)dstAddr, entry); + entry->frameCount = FRAMECOUNT_INVALID; + entry->SetEfbCopy(dstStride); + entry->is_custom_tex = false; + + entry->FromRenderTarget(dst, srcFormat, srcRect, scaleByHalf, cbufid, colmat); + + u64 hash = entry->CalculateHash(); + entry->SetHashes(hash, hash); + + if (g_ActiveConfig.bDumpEFBTarget) + { + static int count = 0; + entry->Save(StringFromFormat("%sefb_frame_%i.png", File::GetUserPath(D_DUMPTEXTURES_IDX).c_str(), + count++), 0); + } + + textures_by_address.emplace((u64)dstAddr, entry); + } } } @@ -1155,6 +1167,9 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::AllocateTexture(const TCach else { entry = g_texture_cache->CreateTexture(config); + if (!entry) + return nullptr; + INCSTAT(stats.numTexturesCreated); } From 0f26cdd31bba90041f74a5a65973f52dcdf5db63 Mon Sep 17 00:00:00 2001 From: degasus Date: Sat, 21 Nov 2015 10:45:26 +0100 Subject: [PATCH 4/5] FramebufferManager: Allow CreateXFBSource to return nullptr --- Source/Core/VideoCommon/FramebufferManagerBase.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Core/VideoCommon/FramebufferManagerBase.cpp b/Source/Core/VideoCommon/FramebufferManagerBase.cpp index 7981dfce4d..48106844cd 100644 --- a/Source/Core/VideoCommon/FramebufferManagerBase.cpp +++ b/Source/Core/VideoCommon/FramebufferManagerBase.cpp @@ -62,6 +62,9 @@ const XFBSourceBase* const* FramebufferManagerBase::GetRealXFBSource(u32 xfbAddr if (!m_realXFBSource) m_realXFBSource = g_framebuffer_manager->CreateXFBSource(fbWidth, fbHeight, 1); + if (!m_realXFBSource) + return nullptr; + m_realXFBSource->srcAddr = xfbAddr; m_realXFBSource->srcWidth = MAX_XFB_WIDTH; @@ -160,6 +163,9 @@ void FramebufferManagerBase::CopyToVirtualXFB(u32 xfbAddr, u32 fbStride, u32 fbH if (!vxfb->xfbSource) { vxfb->xfbSource = g_framebuffer_manager->CreateXFBSource(target_width, target_height, m_EFBLayers); + if (!vxfb->xfbSource) + return; + vxfb->xfbSource->texWidth = target_width; vxfb->xfbSource->texHeight = target_height; } From 0b02dc4f814c4e842f460fd0814b8a7c5379b8c8 Mon Sep 17 00:00:00 2001 From: degasus Date: Sat, 21 Nov 2015 16:11:36 +0100 Subject: [PATCH 5/5] FramebufferManager: check for g_framebuffer_manager --- Source/Core/VideoCommon/FramebufferManagerBase.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Source/Core/VideoCommon/FramebufferManagerBase.cpp b/Source/Core/VideoCommon/FramebufferManagerBase.cpp index 48106844cd..b01c39a5f3 100644 --- a/Source/Core/VideoCommon/FramebufferManagerBase.cpp +++ b/Source/Core/VideoCommon/FramebufferManagerBase.cpp @@ -59,7 +59,7 @@ const XFBSourceBase* const* FramebufferManagerBase::GetRealXFBSource(u32 xfbAddr m_realXFBSource = nullptr; } - if (!m_realXFBSource) + if (!m_realXFBSource && g_framebuffer_manager) m_realXFBSource = g_framebuffer_manager->CreateXFBSource(fbWidth, fbHeight, 1); if (!m_realXFBSource) @@ -121,13 +121,21 @@ const XFBSourceBase* const* FramebufferManagerBase::GetVirtualXFBSource(u32 xfbA void FramebufferManagerBase::CopyToXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc, float Gamma) { if (g_ActiveConfig.bUseRealXFB) - g_framebuffer_manager->CopyToRealXFB(xfbAddr, fbStride, fbHeight, sourceRc, Gamma); + { + if (g_framebuffer_manager) + g_framebuffer_manager->CopyToRealXFB(xfbAddr, fbStride, fbHeight, sourceRc, Gamma); + } else + { CopyToVirtualXFB(xfbAddr, fbStride, fbHeight, sourceRc, Gamma); + } } void FramebufferManagerBase::CopyToVirtualXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc, float Gamma) { + if (!g_framebuffer_manager) + return; + VirtualXFBListType::iterator vxfb = FindVirtualXFB(xfbAddr, sourceRc.GetWidth(), fbHeight); if (m_virtualXFBList.end() == vxfb)