void GUIWidget::_updateLayout() { bs_frame_mark(); // Determine dirty contents and layouts FrameStack<GUIElementBase*> todo; todo.push(mPanel); while (!todo.empty()) { GUIElementBase* currentElem = todo.top(); todo.pop(); if (currentElem->_isDirty()) { GUIElementBase* updateParent = currentElem->_getUpdateParent(); assert(updateParent != nullptr || currentElem == mPanel); if (updateParent != nullptr) _updateLayout(updateParent); else // Must be root panel _updateLayout(mPanel); } else { UINT32 numChildren = currentElem->_getNumChildren(); for (UINT32 i = 0; i < numChildren; i++) todo.push(currentElem->_getChild(i)); } } bs_frame_clear(); }
void GUIInputTool::updateText(const GUIElement* element, const TEXT_SPRITE_DESC& textDesc) { mElement = element; mTextDesc = textDesc; mNumChars = UTF8::count(mTextDesc.text); mLineDescs.clear(); bs_frame_mark(); { const U32String utf32text = UTF8::toUTF32(mTextDesc.text); TextData<FrameAlloc> textData(utf32text, mTextDesc.font, mTextDesc.fontSize, mTextDesc.width, mTextDesc.height, mTextDesc.wordWrap, mTextDesc.wordBreak); UINT32 numLines = textData.getNumLines(); UINT32 numPages = textData.getNumPages(); mNumQuads = 0; for (UINT32 i = 0; i < numPages; i++) mNumQuads += textData.getNumQuadsForPage(i); if (mQuads != nullptr) bs_delete(mQuads); mQuads = bs_newN<Vector2>(mNumQuads * 4); TextSprite::genTextQuads(textData, mTextDesc.width, mTextDesc.height, mTextDesc.horzAlign, mTextDesc.vertAlign, mTextDesc.anchor, mQuads, nullptr, nullptr, mNumQuads); // Store cached line data UINT32 curCharIdx = 0; UINT32 curLineIdx = 0; Vector2I* alignmentOffsets = bs_frame_new<Vector2I>(numLines); TextSprite::getAlignmentOffsets(textData, mTextDesc.width, mTextDesc.height, mTextDesc.horzAlign, mTextDesc.vertAlign, alignmentOffsets); for (UINT32 i = 0; i < numLines; i++) { const TextDataBase::TextLine& line = textData.getLine(i); // Line has a newline char only if it wasn't created by word wrap and it isn't the last line bool hasNewline = line.hasNewlineChar() && (curLineIdx != (numLines - 1)); UINT32 startChar = curCharIdx; UINT32 endChar = curCharIdx + line.getNumChars() + (hasNewline ? 1 : 0); UINT32 lineHeight = line.getYOffset(); INT32 lineYStart = alignmentOffsets[curLineIdx].y; GUIInputLineDesc lineDesc(startChar, endChar, lineHeight, lineYStart, hasNewline); mLineDescs.push_back(lineDesc); curCharIdx = lineDesc.getEndChar(); curLineIdx++; } bs_frame_delete(alignmentOffsets); } bs_frame_clear(); }
Vector2I GUIHelper::calcOptimalContentsSize(const String& text, const GUIElementStyle& style, const GUIDimensions& dimensions) { UINT32 wordWrapWidth = 0; if(style.wordWrap) wordWrapWidth = dimensions.maxWidth; UINT32 contentWidth = style.margins.left + style.margins.right + style.contentOffset.left + style.contentOffset.right; UINT32 contentHeight = style.margins.top + style.margins.bottom + style.contentOffset.top + style.contentOffset.bottom; if(style.font != nullptr && !text.empty()) { bs_frame_mark(); const U32String utf32text = UTF8::toUTF32(text); TextData<FrameAlloc> textData(utf32text, style.font, style.fontSize, wordWrapWidth, 0, style.wordWrap); contentWidth += textData.getWidth(); contentHeight += textData.getNumLines() * textData.getLineHeight(); bs_frame_clear(); } return Vector2I(contentWidth, contentHeight); }
void GUIWidget::_updateLayout(GUIElementBase* elem) { GUIElementBase* parent = elem->_getParent(); bool isPanelOptimized = parent != nullptr && parent->_getType() == GUIElementBase::Type::Panel; GUIElementBase* updateParent = nullptr; if (isPanelOptimized) updateParent = parent; else updateParent = elem; // For GUIPanel we can do a an optimization and update only the element in question instead // of all the children if (isPanelOptimized) { GUIPanel* panel = static_cast<GUIPanel*>(updateParent); GUIElementBase* dirtyElement = elem; dirtyElement->_updateOptimalLayoutSizes(); LayoutSizeRange elementSizeRange = panel->_getElementSizeRange(dirtyElement); Rect2I elementArea = panel->_getElementArea(panel->_getLayoutData().area, dirtyElement, elementSizeRange); GUILayoutData childLayoutData = panel->_getLayoutData(); panel->_updateDepthRange(childLayoutData); childLayoutData.area = elementArea; panel->_updateChildLayout(dirtyElement, childLayoutData); } else { GUILayoutData childLayoutData = updateParent->_getLayoutData(); updateParent->_updateLayout(childLayoutData); } // Mark dirty contents bs_frame_mark(); { FrameStack<GUIElementBase*> todo; todo.push(elem); while (!todo.empty()) { GUIElementBase* currentElem = todo.top(); todo.pop(); if (currentElem->_getType() == GUIElementBase::Type::Element) mDirtyContents.insert(static_cast<GUIElement*>(currentElem)); currentElem->_markAsClean(); UINT32 numChildren = currentElem->_getNumChildren(); for (UINT32 i = 0; i < numChildren; i++) todo.push(currentElem->_getChild(i)); } } bs_frame_clear(); }
void TextSprite::update(const TEXT_SPRITE_DESC& desc, UINT64 groupId) { bs_frame_mark(); { TextData<FrameAlloc> textData(desc.text, desc.font, desc.fontSize, desc.width, desc.height, desc.wordWrap, desc.wordBreak); UINT32 numPages = textData.getNumPages(); // Free all previous memory for (auto& cachedElem : mCachedRenderElements) { if (cachedElem.vertices != nullptr) mAlloc.free(cachedElem.vertices); if (cachedElem.uvs != nullptr) mAlloc.free(cachedElem.uvs); if (cachedElem.indexes != nullptr) mAlloc.free(cachedElem.indexes); } mAlloc.clear(); // Resize cached mesh array to needed size if (mCachedRenderElements.size() != numPages) mCachedRenderElements.resize(numPages); // Actually generate a mesh UINT32 texPage = 0; for (auto& cachedElem : mCachedRenderElements) { UINT32 newNumQuads = textData.getNumQuadsForPage(texPage); cachedElem.vertices = (Vector2*)mAlloc.alloc(sizeof(Vector2) * newNumQuads * 4); cachedElem.uvs = (Vector2*)mAlloc.alloc(sizeof(Vector2) * newNumQuads * 4); cachedElem.indexes = (UINT32*)mAlloc.alloc(sizeof(UINT32) * newNumQuads * 6); cachedElem.numQuads = newNumQuads; const HTexture& tex = textData.getTextureForPage(texPage); SpriteMaterialInfo& matInfo = cachedElem.matInfo; matInfo.groupId = groupId; matInfo.texture = tex; matInfo.tint = desc.color; matInfo.type = SpriteMaterial::Text; texPage++; } // Calc alignment and anchor offsets and set final line positions for (UINT32 j = 0; j < numPages; j++) { SpriteRenderElement& renderElem = mCachedRenderElements[j]; genTextQuads(j, textData, desc.width, desc.height, desc.horzAlign, desc.vertAlign, desc.anchor, renderElem.vertices, renderElem.uvs, renderElem.indexes, renderElem.numQuads); } } bs_frame_clear(); updateBounds(); }
Vector2I GUIHelper::calcTextSize(const String& text, const HFont& font, UINT32 fontSize) { Vector2I size; if (font != nullptr) { bs_frame_mark(); const U32String utf32text = UTF8::toUTF32(text); TextData<FrameAlloc> textData(utf32text, font, fontSize, 0, 0, false); size.x = textData.getWidth(); size.y = textData.getNumLines() * textData.getLineHeight(); bs_frame_clear(); } return size; }
UINT8* OggVorbisEncoder::PCMToOggVorbis(UINT8* samples, const AudioDataInfo& info, UINT32& size) { struct EncodedBlock { UINT8* data; UINT32 size; }; Vector<EncodedBlock> blocks; UINT32 totalEncodedSize = 0; auto writeCallback = [&](UINT8* buffer, UINT32 size) { EncodedBlock newBlock; newBlock.data = bs_frame_alloc(size); newBlock.size = size; memcpy(newBlock.data, buffer, size); blocks.push_back(newBlock); totalEncodedSize += size; }; bs_frame_mark(); OggVorbisEncoder writer; writer.open(writeCallback, info.sampleRate, info.bitDepth, info.numChannels); writer.write(samples, info.numSamples); writer.close(); UINT8* outSampleBuffer = (UINT8*)bs_alloc(totalEncodedSize); UINT32 offset = 0; for (auto& block : blocks) { memcpy(outSampleBuffer + offset, block.data, block.size); offset += block.size; bs_frame_free(block.data); } bs_frame_clear(); size = totalEncodedSize; return outSampleBuffer; }
void TriangleClipper2D::convertToMesh(const std::function<void(Vector2*, Vector2*, UINT32)>& writeCallback) { bs_frame_mark(); { FrameVector<FrameVector<UINT32>> allFaces; getOrderedFaces(allFaces); // Note: Consider using Delaunay triangulation to avoid skinny triangles UINT32 numWritten = 0; assert(BUFFER_SIZE % 3 == 0); for (auto& face : allFaces) { for (UINT32 i = 0; i < (UINT32)face.size() - 2; i++) { const Vector3& v0 = mesh.verts[face[0]].point; const Vector3& v1 = mesh.verts[face[i + 1]].point; const Vector3& v2 = mesh.verts[face[i + 2]].point; vertexBuffer[numWritten] = Vector2(v0.x, v0.y); uvBuffer[numWritten] = mesh.verts[face[0]].uv; numWritten++; vertexBuffer[numWritten] = Vector2(v1.x, v1.y); uvBuffer[numWritten] = mesh.verts[face[i + 1]].uv; numWritten++; vertexBuffer[numWritten] = Vector2(v2.x, v2.y); uvBuffer[numWritten] = mesh.verts[face[i + 2]].uv; numWritten++; // Only need to check this here since we guarantee the buffer is in multiples of three if (numWritten >= BUFFER_SIZE) { writeCallback(vertexBuffer, uvBuffer, numWritten); numWritten = 0; } } } if (numWritten > 0) writeCallback(vertexBuffer, uvBuffer, numWritten); } bs_frame_clear(); }
bool Resource::areDependenciesLoaded() const { bs_frame_mark(); bool areLoaded = true; { FrameVector<HResource> dependencies; getResourceDependencies(dependencies); for (auto& dependency : dependencies) { if (dependency != nullptr && !dependency.isLoaded()) { areLoaded = false; break; } } } bs_frame_clear(); return areLoaded; }
Win32Window::~Win32Window() { if (m->hWnd && !m->isExternal) { // Handle modal windows bs_frame_mark(); { FrameVector<HWND> windowsToEnable; { Lock lock(sWindowsMutex); // Hidden dependency: All windows must be re-enabled before a window is destroyed, otherwise the incorrect // window in the z order will be activated. bool reenableWindows = false; if (!sModalWindowStack.empty()) { // Start from back because the most common case is closing the top-most modal window for (auto iter = sModalWindowStack.rbegin(); iter != sModalWindowStack.rend(); ++iter) { if (*iter == this) { auto iterFwd = std::next(iter).base(); // erase doesn't accept reverse iter, so convert sModalWindowStack.erase(iterFwd); break; } } if (!sModalWindowStack.empty()) // Enable next modal window { Win32Window* curModalWindow = sModalWindowStack.back(); windowsToEnable.push_back(curModalWindow->m->hWnd); } else reenableWindows = true; // No more modal windows, re-enable any remaining window } if (reenableWindows) { for (auto& window : sAllWindows) windowsToEnable.push_back(window->m->hWnd); } } for(auto& entry : windowsToEnable) EnableWindow(entry, TRUE); } bs_frame_clear(); DestroyWindow(m->hWnd); } { Lock lock(sWindowsMutex); auto iterFind = std::find(sAllWindows.begin(), sAllWindows.end(), this); sAllWindows.erase(iterFind); } bs_delete(m); }
Win32Window::Win32Window(const WINDOW_DESC& desc) { m = bs_new<Pimpl>(); m->isModal = desc.modal; m->isHidden = desc.hidden; HMONITOR hMonitor = desc.monitor; if (!desc.external) { m->style = WS_CLIPCHILDREN; INT32 left = desc.left; INT32 top = desc.top; // If we didn't specified the adapter index, or if we didn't find it if (hMonitor == nullptr) { POINT windowAnchorPoint; // Fill in anchor point. windowAnchorPoint.x = left; windowAnchorPoint.y = top; // Get the nearest monitor to this window. hMonitor = MonitorFromPoint(windowAnchorPoint, MONITOR_DEFAULTTOPRIMARY); } // Get the target monitor info MONITORINFO monitorInfo; memset(&monitorInfo, 0, sizeof(MONITORINFO)); monitorInfo.cbSize = sizeof(MONITORINFO); GetMonitorInfo(hMonitor, &monitorInfo); UINT32 width = desc.width; UINT32 height = desc.height; // No specified top left -> Center the window in the middle of the monitor if (left == -1 || top == -1) { int screenw = monitorInfo.rcWork.right - monitorInfo.rcWork.left; int screenh = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; // clamp window dimensions to screen size int outerw = (int(width) < screenw) ? int(width) : screenw; int outerh = (int(height) < screenh) ? int(height) : screenh; if (left == -1) left = monitorInfo.rcWork.left + (screenw - outerw) / 2; else if (hMonitor != nullptr) left += monitorInfo.rcWork.left; if (top == -1) top = monitorInfo.rcWork.top + (screenh - outerh) / 2; else if (hMonitor != nullptr) top += monitorInfo.rcWork.top; } else if (hMonitor != nullptr) { left += monitorInfo.rcWork.left; top += monitorInfo.rcWork.top; } if (!desc.fullscreen) { if (desc.parent) { if (desc.toolWindow) m->styleEx = WS_EX_TOOLWINDOW; else m->style |= WS_CHILD; } else { if (desc.toolWindow) m->styleEx = WS_EX_TOOLWINDOW; } if (!desc.parent || desc.toolWindow) { if(desc.showTitleBar) { if(desc.showBorder || desc.allowResize) m->style |= WS_OVERLAPPEDWINDOW; else m->style |= WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; } else { if(desc.showBorder || desc.allowResize) m->style |= WS_POPUP | WS_BORDER; else m->style |= WS_POPUP; } } if (!desc.outerDimensions) { // Calculate window dimensions required to get the requested client area RECT rect; SetRect(&rect, 0, 0, width, height); AdjustWindowRect(&rect, m->style, false); width = rect.right - rect.left; height = rect.bottom - rect.top; // Clamp width and height to the desktop dimensions int screenw = GetSystemMetrics(SM_CXSCREEN); int screenh = GetSystemMetrics(SM_CYSCREEN); if ((int)width > screenw) width = screenw; if ((int)height > screenh) height = screenh; if (left < 0) left = (screenw - width) / 2; if (top < 0) top = (screenh - height) / 2; } if (desc.backgroundPixels != nullptr) m->styleEx |= WS_EX_LAYERED; } else { m->style |= WS_POPUP; top = 0; left = 0; } UINT classStyle = 0; if (desc.enableDoubleClick) classStyle |= CS_DBLCLKS; // Register the window class WNDCLASS wc = { classStyle, desc.wndProc, 0, 0, desc.module, LoadIcon(nullptr, IDI_APPLICATION), LoadCursor(nullptr, IDC_ARROW), (HBRUSH)GetStockObject(BLACK_BRUSH), 0, "Win32Wnd" }; RegisterClass(&wc); // Create main window m->hWnd = CreateWindowEx(m->styleEx, "Win32Wnd", desc.title.c_str(), m->style, left, top, width, height, desc.parent, nullptr, desc.module, desc.creationParams); m->isExternal = false; } else { m->hWnd = desc.external; m->isExternal = true; } RECT rect; GetWindowRect(m->hWnd, &rect); m->top = rect.top; m->left = rect.left; GetClientRect(m->hWnd, &rect); m->width = rect.right; m->height = rect.bottom; // Set background, if any if (desc.backgroundPixels != nullptr) { HBITMAP backgroundBitmap = Win32PlatformUtility::createBitmap( desc.backgroundPixels, desc.backgroundWidth, desc.backgroundHeight, true); HDC hdcScreen = GetDC(nullptr); HDC hdcMem = CreateCompatibleDC(hdcScreen); HBITMAP hOldBitmap = (HBITMAP)SelectObject(hdcMem, backgroundBitmap); BLENDFUNCTION blend = { 0 }; blend.BlendOp = AC_SRC_OVER; blend.SourceConstantAlpha = 255; blend.AlphaFormat = AC_SRC_ALPHA; POINT origin; origin.x = m->left; origin.y = m->top; SIZE size; size.cx = m->width; size.cy = m->height; POINT zero = { 0 }; UpdateLayeredWindow(m->hWnd, hdcScreen, &origin, &size, hdcMem, &zero, RGB(0, 0, 0), &blend, desc.alphaBlending ? ULW_ALPHA : ULW_OPAQUE); SelectObject(hdcMem, hOldBitmap); DeleteDC(hdcMem); ReleaseDC(nullptr, hdcScreen); } // Handle modal windows bs_frame_mark(); { FrameVector<HWND> windowsToDisable; FrameVector<HWND> windowsToBringToFront; { Lock lock(sWindowsMutex); if (m->isModal) { if (!sModalWindowStack.empty()) { Win32Window* curModalWindow = sModalWindowStack.back(); windowsToDisable.push_back(curModalWindow->m->hWnd); } else { for (auto& window : sAllWindows) windowsToDisable.push_back(window->m->hWnd); } sModalWindowStack.push_back(this); } else { // A non-modal window was opened while another modal one is open, // immediately deactivate it and make sure the modal windows stay on top. if (!sModalWindowStack.empty()) { windowsToDisable.push_back(m->hWnd); for (auto window : sModalWindowStack) windowsToBringToFront.push_back(window->m->hWnd); } } sAllWindows.push_back(this); } for(auto& entry : windowsToDisable) EnableWindow(entry, FALSE); for (auto& entry : windowsToBringToFront) BringWindowToTop(entry); } if(desc.hidden) setHidden(true); bs_frame_clear(); }
void CoreObjectManager::syncDownload(FrameAlloc* allocator) { Lock lock(mObjectsMutex); mCoreSyncData.push_back(CoreStoredSyncData()); CoreStoredSyncData& syncData = mCoreSyncData.back(); syncData.alloc = allocator; // Add all objects dependant on the dirty objects bs_frame_mark(); { FrameSet<CoreObject*> dirtyDependants; for (auto& objectData : mDirtyObjects) { auto iterFind = mDependants.find(objectData.first); if (iterFind != mDependants.end()) { const Vector<CoreObject*>& dependants = iterFind->second; for (auto& dependant : dependants) { if (!dependant->isCoreDirty()) { dependant->mCoreDirtyFlags |= 0xFFFFFFFF; // To ensure the loop below doesn't skip it dirtyDependants.insert(dependant); } } } } for (auto& dirtyDependant : dirtyDependants) { UINT64 id = dirtyDependant->getInternalID(); mDirtyObjects[id] = { dirtyDependant, -1 }; } } bs_frame_clear(); // Order in which objects are recursed in matters, ones with lower ID will have been created before // ones with higher ones and should be updated first. for (auto& objectData : mDirtyObjects) { std::function<void(CoreObject*)> syncObject = [&](CoreObject* curObj) { if (!curObj->isCoreDirty()) return; // We already processed it as some other object's dependency // Sync dependencies before dependants // Note: I don't check for recursion. Possible infinite loop if two objects // are dependent on one another. UINT64 id = curObj->getInternalID(); auto iterFind = mDependencies.find(id); if (iterFind != mDependencies.end()) { const Vector<CoreObject*>& dependencies = iterFind->second; for (auto& dependency : dependencies) syncObject(dependency); } SPtr<CoreObjectCore> objectCore = curObj->getCore(); if (objectCore == nullptr) { curObj->markCoreClean(); return; } CoreSyncData objSyncData = curObj->syncToCore(allocator); curObj->markCoreClean(); syncData.entries.push_back(CoreStoredSyncObjData(objectCore, curObj->getInternalID(), objSyncData)); }; CoreObject* object = objectData.second.object; if (object != nullptr) syncObject(object); else { // Object was destroyed but we still need to sync its modifications before it was destroyed if (objectData.second.syncDataId != -1) syncData.entries.push_back(mDestroyedSyncData[objectData.second.syncDataId]); } } mDirtyObjects.clear(); mDestroyedSyncData.clear(); }
void CoreObjectManager::updateDependencies(CoreObject* object, Vector<CoreObject*>* dependencies) { UINT64 id = object->getInternalID(); bs_frame_mark(); { FrameVector<CoreObject*> toRemove; FrameVector<CoreObject*> toAdd; Lock lock(mObjectsMutex); // Add dependencies and clear old dependencies from dependants { if (dependencies != nullptr) std::sort(dependencies->begin(), dependencies->end()); auto iterFind = mDependencies.find(id); if (iterFind != mDependencies.end()) { const Vector<CoreObject*>& oldDependencies = iterFind->second; if (dependencies != nullptr) { std::set_difference(dependencies->begin(), dependencies->end(), dependencies->begin(), dependencies->end(), toRemove.begin()); std::set_difference(oldDependencies.begin(), oldDependencies.end(), oldDependencies.begin(), oldDependencies.end(), toAdd.begin()); } else { for (auto& dependency : oldDependencies) toRemove.push_back(dependency); } for (auto& dependency : toRemove) { UINT64 dependencyId = dependency->getInternalID(); auto iterFind2 = mDependants.find(dependencyId); if (iterFind2 != mDependants.end()) { Vector<CoreObject*>& dependants = iterFind2->second; auto findIter3 = std::find(dependants.begin(), dependants.end(), object); dependants.erase(findIter3); if (dependants.size() == 0) mDependants.erase(iterFind2); } } } else { if (dependencies != nullptr) { for (auto& dependency : *dependencies) toAdd.push_back(dependency); } } if (dependencies != nullptr) mDependencies[id] = *dependencies; } // Register dependants { for (auto& dependency : toAdd) { UINT64 dependencyId = dependency->getInternalID(); Vector<CoreObject*>& dependants = mDependants[dependencyId]; dependants.push_back(object); } } } bs_frame_clear(); }
void VulkanImage::getBarriers(const VkImageSubresourceRange& range, Vector<VkImageMemoryBarrier>& barriers) { UINT32 numSubresources = range.levelCount * range.layerCount; // Nothing to do if (numSubresources == 0) return; UINT32 mip = range.baseMipLevel; UINT32 face = range.baseArrayLayer; UINT32 lastMip = range.baseMipLevel + range.levelCount - 1; UINT32 lastFace = range.baseArrayLayer + range.layerCount - 1; VkImageMemoryBarrier defaultBarrier; defaultBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; defaultBarrier.pNext = nullptr; defaultBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; defaultBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; defaultBarrier.image = getHandle(); defaultBarrier.subresourceRange.aspectMask = range.aspectMask; defaultBarrier.subresourceRange.layerCount = 1; defaultBarrier.subresourceRange.levelCount = 1; defaultBarrier.subresourceRange.baseArrayLayer = 0; defaultBarrier.subresourceRange.baseMipLevel = 0; auto addNewBarrier = [&](VulkanImageSubresource* subresource, UINT32 face, UINT32 mip) { barriers.push_back(defaultBarrier); VkImageMemoryBarrier* barrier = &barriers.back(); barrier->subresourceRange.baseArrayLayer = face; barrier->subresourceRange.baseMipLevel = mip; barrier->srcAccessMask = getAccessFlags(subresource->getLayout()); barrier->oldLayout = subresource->getLayout(); return barrier; }; bs_frame_mark(); { FrameVector<bool> processed(numSubresources, false); // Add first subresource VulkanImageSubresource* subresource = getSubresource(face, mip); addNewBarrier(subresource, face, mip); numSubresources--; processed[0] = true; while (numSubresources > 0) { // Try to expand the barrier as much as possible VkImageMemoryBarrier* barrier = &barriers.back(); while (true) { // Expand by one in the X direction bool expandedFace = true; if (face < lastFace) { for (UINT32 i = 0; i < barrier->subresourceRange.levelCount; i++) { UINT32 curMip = barrier->subresourceRange.baseMipLevel + i; VulkanImageSubresource* subresource = getSubresource(face + 1, curMip); if (barrier->oldLayout != subresource->getLayout()) { expandedFace = false; break; } } if (expandedFace) { barrier->subresourceRange.layerCount++; numSubresources -= barrier->subresourceRange.levelCount; face++; for (UINT32 i = 0; i < barrier->subresourceRange.levelCount; i++) { UINT32 curMip = (barrier->subresourceRange.baseMipLevel + i) - range.baseMipLevel; UINT32 idx = curMip * range.layerCount + (face - range.baseArrayLayer); processed[idx] = true; } } } else expandedFace = false; // Expand by one in the Y direction bool expandedMip = true; if (mip < lastMip) { for (UINT32 i = 0; i < barrier->subresourceRange.layerCount; i++) { UINT32 curFace = barrier->subresourceRange.baseArrayLayer + i; VulkanImageSubresource* subresource = getSubresource(curFace, mip + 1); if (barrier->oldLayout != subresource->getLayout()) { expandedMip = false; break; } } if (expandedMip) { barrier->subresourceRange.levelCount++; numSubresources -= barrier->subresourceRange.layerCount; mip++; for (UINT32 i = 0; i < barrier->subresourceRange.layerCount; i++) { UINT32 curFace = (barrier->subresourceRange.baseArrayLayer + i) - range.baseArrayLayer; UINT32 idx = (mip - range.baseMipLevel) * range.layerCount + curFace; processed[idx] = true; } } } else expandedMip = false; // If we can't grow no more, we're done with this square if (!expandedMip && !expandedFace) break; } // Look for a new starting point (sub-resource we haven't processed yet) for (UINT32 i = 0; i < range.levelCount; i++) { bool found = false; for (UINT32 j = 0; j < range.layerCount; j++) { UINT32 idx = i * range.layerCount + j; if (!processed[idx]) { mip = range.baseMipLevel + i; face = range.baseArrayLayer + j; found = true; processed[idx] = true; break; } } if (found) { VulkanImageSubresource* subresource = getSubresource(face, mip); addNewBarrier(subresource, face, mip); numSubresources--; break; } } } } bs_frame_clear(); }