diff --git a/direct/src/tkpanels/DirectSessionPanel.py b/direct/src/tkpanels/DirectSessionPanel.py index 39b18cfd7b..50cdf0330b 100644 --- a/direct/src/tkpanels/DirectSessionPanel.py +++ b/direct/src/tkpanels/DirectSessionPanel.py @@ -14,6 +14,7 @@ from direct.tkwidgets import Slider from direct.tkwidgets import VectorWidgets from direct.tkwidgets import SceneGraphExplorer from TaskManagerPanel import TaskManagerWidget +from direct.tkwidgets import MemoryExplorer """ Possible to add: @@ -183,17 +184,22 @@ class DirectSessionPanel(AppShell): # Create the notebook pages notebook = Pmw.NoteBook(notebookFrame) notebook.pack(fill = BOTH, expand = 1) - envPage = notebook.add('Environment') - lightsPage = notebook.add('Lights') - gridPage = notebook.add('Grid') - devicePage = notebook.add('Devices') - tasksPage = notebook.add('Tasks') - scenePage = notebook.add('Scene') + self.createEnvPage(notebook.add('Environment')) + self.createLightsPage(notebook.add('Lights')) + self.createGridPage(notebook.add('Grid')) + self.createDevicePage(notebook.add('Devices')) + self.createTasksPage(notebook.add('Tasks')) + self.createMemPage(notebook.add('Memory')) + + notebook.setnaturalsize() + + framePane.pack(expand = 1, fill = BOTH) + mainFrame.pack(fill = 'both', expand = 1) + # Put this here so it isn't called right away notebook['raisecommand'] = self.updateInfo - - ## Environment page ## - # Backgroud color + + def createEnvPage(self, envPage): bkgrdFrame = Frame(envPage, borderwidth = 2, relief = 'sunken') Label(bkgrdFrame, text = 'Background', @@ -273,7 +279,6 @@ class DirectSessionPanel(AppShell): frame.pack(side = LEFT, fill = X, expand = 0) fovFrame.pack(fill = X, expand = 1) - drFrame.pack(fill = BOTH, expand = 0) ## Render Style ## @@ -305,7 +310,7 @@ class DirectSessionPanel(AppShell): self.toggleWireframeButton.pack(fill = X, expand = 1) toggleFrame.pack(side = LEFT, fill = X, expand = 1) - ## Lights page ## + def createLightsPage(self, lightsPage): # Lights # lightFrame = Frame(lightsPage, borderwidth = 2, relief = 'sunken') self.lightsButton = Menubutton(lightFrame, text = 'Lights', @@ -470,7 +475,8 @@ class DirectSessionPanel(AppShell): lightFrame.pack(expand = 1, fill = BOTH) - ## GRID PAGE ## + + def createGridPage(self, gridPage): Label(gridPage, text = 'Grid', font=('MSSansSerif', 14, 'bold')).pack(expand = 0) self.enableGrid = BooleanVar() @@ -524,7 +530,7 @@ class DirectSessionPanel(AppShell): self.gridSnapAngle['command'] = base.direct.grid.setSnapAngle self.gridSnapAngle.pack(fill = X, expand = 0) - ## DEVICE PAGE ## + def createDevicePage(self, devicePage): Label(devicePage, text = 'DEVICES', font=('MSSansSerif', 14, 'bold')).pack(expand = 0) @@ -590,17 +596,19 @@ class DirectSessionPanel(AppShell): self.jbHprSF.pack(fill = X, expand = 0) self.bind(self.jbHprSF, 'Set joybox HPR speed multiplier') - ## TASKS PAGE ## + def createTasksPage(self, tasksPage): Label(tasksPage, text = 'TASKS', font=('MSSansSerif', 14, 'bold')).pack(expand = 0) self.taskMgrPanel = TaskManagerWidget(tasksPage, taskMgr) self.taskMgrPanel.taskListBox['listbox_height'] = 10 - notebook.setnaturalsize() - - framePane.pack(expand = 1, fill = BOTH) - mainFrame.pack(fill = 'both', expand = 1) - + def createMemPage(self, memPage): + self.MemExp = MemoryExplorer.MemoryExplorer( + memPage, nodePath = render, + scrolledCanvas_hull_width = 250, + scrolledCanvas_hull_height = 250) + self.MemExp.pack(fill = BOTH, expand = 1) + def toggleDirect(self): if self.directEnabled.get(): base.direct.enable() diff --git a/direct/src/tkwidgets/MemoryExplorer.py b/direct/src/tkwidgets/MemoryExplorer.py new file mode 100755 index 0000000000..e7a9283f5f --- /dev/null +++ b/direct/src/tkwidgets/MemoryExplorer.py @@ -0,0 +1,349 @@ +from direct.showbase.DirectObject import DirectObject +from direct.showbase.TkGlobal import * +from Tkinter import * +from Tree import * +import Pmw + +#-------------------------------------------------------------------------- +#-------------------------------------------------------------------------- +DEFAULT_BT_WIDTH = 50.0 + +#-------------------------------------------------------------------------- +#-------------------------------------------------------------------------- +class MemoryExplorer(Pmw.MegaWidget, DirectObject): + + #-------------------------------------------------------------------------- + # Init + #-------------------------------------------------------------------------- + def __init__(self, parent = None, nodePath = render, **kw): + optiondefs = (('menuItems', [], Pmw.INITOPT),) + self.defineoptions(kw, optiondefs) + Pmw.MegaWidget.__init__(self, parent) + + self.nodePath = nodePath + self.renderItem = None + self.render2dItem = None + + self.buttons = [] + self.labels = [] + self.rootItem = None + + self.btWidth = DEFAULT_BT_WIDTH + + self.createScrolledFrame() + self.createScale() + self.createRefreshBT() + + self.balloon = Pmw.Balloon(self.interior()) + + def createScrolledFrame(self): + self.frame = Pmw.ScrolledFrame(self.interior(), + labelpos = 'n', + label_text = 'ScrolledFrame', + usehullsize = 1, + hull_width = 200, + hull_height = 220,) + + self.frame.pack(padx = 3, pady = 3, fill = BOTH, expand = 1) + + def createScale(self): + self.scaleCtrl = Scale(self.interior(), + label = "Graph Scale", + from_= 0.0, + to = 20.0, + resolution = 0.1, + orient = HORIZONTAL, + command = self.onScaleUpdate) + + self.scaleCtrl.pack(side = LEFT, fill = BOTH, expand = 1) + self.scaleCtrl.set(0.0) + + def createRefreshBT(self): + self.refreshBT = Button(self.interior(), text = 'Refresh', command = self.refresh) + self.refreshBT.pack(side = LEFT, fill = BOTH, expand = 1) + + #-------------------------------------------------------------------------- + # Item Ctrls + #-------------------------------------------------------------------------- + def createDefaultCtrls(self): + if self.renderItem == None or self.render2dItem == None: + return + + totalBytes = self.renderItem.getVertexBytes()+self.render2dItem.getVertexBytes() + + self.addChildCtrl(self.renderItem, totalBytes) + self.addChildCtrl(self.render2dItem, totalBytes) + + self.setTitle("ALL", totalBytes) + + def setTitle(self, parent, bytes): + self.frame["label_text"] = "[%s] - %s bytes" % (parent, bytes) + + def resetCtrls(self): + for button in self.buttons: + self.balloon.unbind(button) + button.destroy() + self.buttons = [] + + for label in self.labels: + label.destroy() + self.labels = [] + + def getNewButton(self, width, ratio): + newBT = Button(self.frame.interior(), + anchor = W, + width = width) + + if ratio == 0.0: + newBT['bg'] = "grey" + newBT['text'] = "." + else: + newBT['bg'] = Pmw.Color.hue2name(0.0, 1.0-ratio) + newBT['text'] = "%0.2f%%" % (ratio*100.0) + + return newBT + + def addSelfCtrl(self, item, totalBytes): + self.addLabel("[self] : %s bytes" % item.getSelfVertexBytes()) + + bt = self.addButton(item.getSelfVertexBytes(), + totalBytes, + self.onSelfButtonLClick, + self.onSelfButtonRClick, + item) + + def addChildCtrl(self, item, totalBytes): + self.addLabel("%s [+%s] : %s bytes" % (item.getName(), + item.getNumChildren(), + item.getVertexBytes())) + + bt = self.addButton(item.getVertexBytes(), + totalBytes, + self.onChildButtonLClick, + self.onChildButtonRClick, + item) + + def addButton(self, vertexBytes, totalBytes, funcLClick, funcRClick, item): + width = self.getBTWidth(vertexBytes, totalBytes) + + if totalBytes == 0: + ratio = 0.0 + else: + ratio = vertexBytes/float(totalBytes) + + bt = self.getNewButton(width, ratio) + + def callbackL(event): + funcLClick(item) + + def callbackR(event): + funcRClick(item) + + bt.bind("", callbackL) + bt.bind("", callbackR) + + bt.pack(side = TOP, anchor = NW) + self.buttons.append(bt) + + self.balloon.bind(bt, item.getPathName()) + + return bt + + def addLabel(self, label): + label = Label(self.frame.interior(), text = label) + label.pack(side = TOP, anchor = NW, expand = 0) + self.labels.append(label) + + def getBTWidth(self, vertexBytes, totalBytes): + if totalBytes == 0: + return 1 + + width = int(self.btWidth * vertexBytes / totalBytes) + + if width == 0: + width = 1 + + return width + + #-------------------------------------------------------------------------- + # Callback + #-------------------------------------------------------------------------- + def onScaleUpdate(self, arg): + self.btWidth = DEFAULT_BT_WIDTH + DEFAULT_BT_WIDTH * float(arg) + + if self.rootItem: + self.updateBTWidth() + else: + self.updateDefaultBTWidth() + + def updateBTWidth(self): + self.buttons[0]['width'] = self.getBTWidth(self.rootItem.getSelfVertexBytes(), + self.rootItem.getVertexBytes()) + + btIndex = 1 + for item in self.rootItem.getChildrenAsList(): + self.buttons[btIndex]['width'] = self.getBTWidth(item.getVertexBytes(), + self.rootItem.getVertexBytes()) + btIndex += 1 + + def updateDefaultBTWidth(self): + if self.renderItem == None or self.render2dItem == None: + return + totalBytes = self.renderItem.getVertexBytes() + self.render2dItem.getVertexBytes() + self.buttons[0]['width'] = self.getBTWidth(self.renderItem.getVertexBytes(), totalBytes) + self.buttons[1]['width'] = self.getBTWidth(self.render2dItem.getVertexBytes(), totalBytes) + + def onSelfButtonLClick(self, item): + pass + + def onSelfButtonRClick(self, item): + parentItem = item.getParent() + self.resetCtrls() + self.addItemCtrls(parentItem) + + def onChildButtonLClick(self, item): + if item.getNumChildren() == 0: + return + + self.resetCtrls() + self.addItemCtrls(item) + + def onChildButtonRClick(self, item): + parentItem = item.getParent() + + if parentItem: + self.resetCtrls() + self.addItemCtrls(parentItem.getParent()) + + def addItemCtrls(self, item): + self.rootItem = item + if item == None: + self.createDefaultCtrls() + else: + self.addSelfCtrl(item, item.getVertexBytes()) + + for child in item.getChildrenAsList(): + self.addChildCtrl(child, item.getVertexBytes()) + + self.setTitle(item.getPathName(), item.getVertexBytes()) + + #-------------------------------------------------------------------------- + # List & Analyze + #-------------------------------------------------------------------------- + def makeList(self): + self.renderItem = MemoryExplorerItem(None, render) + self.buildList(self.renderItem) + + self.render2dItem = MemoryExplorerItem(None, render2d) + self.buildList(self.render2dItem) + + def buildList(self, parentItem): + for nodePath in parentItem.nodePath.getChildrenAsList(): + item = MemoryExplorerItem(parentItem, nodePath) + parentItem.addChild(item) + self.buildList(item) + + def analyze(self): + self.renderItem.analyze() + self.render2dItem.analyze() + + def refresh(self): + self.makeList() + self.analyze() + + self.resetCtrls() + self.createDefaultCtrls() + +#-------------------------------------------------------------------------- +#-------------------------------------------------------------------------- +class MemoryExplorerItem: + def __init__(self, parent, nodePath): + self.parent = parent + self.nodePath = nodePath + self.children = [] + + self.selfVertexBytes = 0 + self.childrenVertexBytes = 0 + + self.numFaces = 0 + self.textureBytes = 0 + + if parent: + self.pathName = parent.pathName + "/" + nodePath.getName() + else: + self.pathName = nodePath.getName() + + def getParent(self): + return self.parent + + def addChild(self, child): + self.children.append(child) + + def getNumChildren(self): + return len(self.children) + + def getChildrenAsList(self): + return self.children + + def getName(self): + return self.nodePath.getName() + + def getPathName(self): + return self.pathName + + def getVertexBytes(self): + return self.selfVertexBytes + self.childrenVertexBytes + + def getSelfVertexBytes(self): + return self.selfVertexBytes + + def analyze(self): + self.selfVertexBytes = 0 + self.childrenVertexBytes = 0 + + self.numFaces = 0 + self.textureBytes = 0 + + self.calcTextureBytes() + + if self.nodePath.node().isGeomNode(): + geomNode = self.nodePath.node() + + for i in range(geomNode.getNumGeoms()): + geom = geomNode.getGeom(i) + self.calcVertexBytes(geom) + self.calcNumFaces(geom) + + self.analyzeChildren() + + def calcVertexBytes(self, geom): + vData = geom.getVertexData() + for j in range(vData.getNumArrays()): + array = vData.getArray(j) + self.selfVertexBytes += array.getDataSizeBytes() + + def calcTextureBytes(self): + texCol = self.nodePath.findAllTextures() + for i in range(texCol.getNumTextures()): + tex = texCol.getTexture(i) + self.textureBytes += tex.estimateTextureMemory() + + # what about shared textures by multiple nodes ? + + def calcNumFaces(self, geom): + for k in range(geom.getNumPrimitives()): + primitive = geom.getPrimitive(k) + self.numFaces += primitive.getNumFaces() + + def analyzeChildren(self): + for child in self.children: + child.analyze() + self.childrenVertexBytes += child.getVertexBytes() + self.numFaces += child.numFaces + + def ls(self, indent = ""): + print(indent + self.nodePath.getName() + " " + str(self.vertexBytes) + " " + str(self.numFaces) + " " + str(self.textureBytes)) + indent = indent + " " + + for child in self.children: + child.ls(indent)