Performance/Memory Improvements (#365)

* Performance improvements

* fix readbytes variable
This commit is contained in:
Tim Sarbin 2020-06-20 21:07:36 -04:00 committed by GitHub
parent 99b10b2599
commit b9f17f433f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 56 additions and 42 deletions

4
.gitignore vendored
View File

@ -10,4 +10,6 @@
**/*.pprof
*.swp
.*.swp
tags
tags
heap.out
heap.pdf

View File

@ -29,13 +29,13 @@ func LoadAnimationData(rawData []byte) {
for !streamReader.Eof() {
dataCount := int(streamReader.GetInt32())
for i := 0; i < dataCount; i++ {
cofNameBytes, _ := streamReader.ReadBytes(8)
cofNameBytes := streamReader.ReadBytes(8)
data := &AnimationDataRecord{
COFName: strings.ReplaceAll(string(cofNameBytes), string(0), ""),
FramesPerDirection: int(streamReader.GetInt32()),
AnimationSpeed: int(streamReader.GetInt32()),
}
data.Flags, _ = streamReader.ReadBytes(144)
data.Flags = streamReader.ReadBytes(144)
cofIndex := strings.ToLower(data.COFName)
if _, found := AnimationData[cofIndex]; !found {
AnimationData[cofIndex] = make([]*AnimationDataRecord, 0)

View File

@ -19,8 +19,8 @@ func LoadObjectTypes(objectTypeData []byte) {
count := streamReader.GetInt32()
ObjectTypes = make([]ObjectTypeRecord, count)
for i := range ObjectTypes {
nameBytes, _ := streamReader.ReadBytes(32)
tokenBytes, _ := streamReader.ReadBytes(20)
nameBytes := streamReader.ReadBytes(32)
tokenBytes := streamReader.ReadBytes(20)
ObjectTypes[i] = ObjectTypeRecord{
Name: strings.TrimSpace(strings.ReplaceAll(string(nameBytes), string(0), "")),
Token: strings.TrimSpace(strings.ReplaceAll(string(tokenBytes), string(0), "")),

View File

@ -70,7 +70,7 @@ func (v *BinkDecoder) GetNextFrame() {
func (v *BinkDecoder) loadHeaderInformation() {
v.streamReader.SetPosition(0)
headerBytes, _ := v.streamReader.ReadBytes(3)
headerBytes := v.streamReader.ReadBytes(3)
if string(headerBytes) != "BIK" {
log.Fatal("Invalid header for bink video")
}

View File

@ -37,19 +37,19 @@ func LoadCOF(fileData []byte) (*COF, error) {
layer.Selectable = streamReader.GetByte() != 0
layer.Transparent = streamReader.GetByte() != 0
layer.DrawEffect = d2enum.DrawEffect(streamReader.GetByte())
weaponClassStr, _ := streamReader.ReadBytes(4)
weaponClassStr := streamReader.ReadBytes(4)
layer.WeaponClass = d2enum.WeaponClassFromString(strings.TrimSpace(strings.ReplaceAll(string(weaponClassStr), string(0), "")))
result.CofLayers[i] = layer
result.CompositeLayers[layer.Type] = i
}
animationFrameBytes, _ := streamReader.ReadBytes(result.FramesPerDirection)
animationFrameBytes := streamReader.ReadBytes(result.FramesPerDirection)
result.AnimationFrames = make([]d2enum.AnimationFrame, result.FramesPerDirection)
for i := range animationFrameBytes {
result.AnimationFrames[i] = d2enum.AnimationFrame(animationFrameBytes[i])
}
priorityLen := result.FramesPerDirection * result.NumberOfDirections * result.NumberOfLayers
result.Priority = make([][][]d2enum.CompositeType, result.NumberOfDirections)
priorityBytes, _ := streamReader.ReadBytes(priorityLen)
priorityBytes := streamReader.ReadBytes(priorityLen)
priorityIndex := 0
for direction := 0; direction < result.NumberOfDirections; direction++ {
result.Priority[direction] = make([][]d2enum.CompositeType, result.FramesPerDirection)

View File

@ -74,7 +74,7 @@ func LoadDT1(fileData []byte) (*DT1, error) {
}
for blockIndex, block := range tile.Blocks {
br.SetPosition(uint64(tile.blockHeaderPointer + block.FileOffset))
encodedData, _ := br.ReadBytes(int(block.Length))
encodedData := br.ReadBytes(int(block.Length))
tile.Blocks[blockIndex].EncodedData = encodedData
}

View File

@ -103,12 +103,10 @@ func (v *StreamReader) ReadByte() (byte, error) {
}
// ReadBytes reads multiple bytes
func (v *StreamReader) ReadBytes(count int) ([]byte, error) {
result := make([]byte, count)
for i := 0; i < count; i++ {
result[i] = v.GetByte()
}
return result, nil
func (v *StreamReader) ReadBytes(count int) []byte {
result := v.data[v.position : v.position+uint64(count)]
v.position += uint64(count)
return result
}
func (v *StreamReader) SkipBytes(count int) {

View File

@ -39,9 +39,7 @@ func LoadDictionary(dictionaryData []byte) {
}
br := CreateStreamReader(dictionaryData)
// CRC
if _, err := br.ReadBytes(2); err != nil {
log.Fatal("Error reading CRC")
}
br.ReadBytes(2)
numberOfElements := br.GetUInt16()
hashTableSize := br.GetUInt32()
// Version (always 0)
@ -74,7 +72,7 @@ func LoadDictionary(dictionaryData []byte) {
continue
}
br.SetPosition(uint64(hashEntry.NameString))
nameVal, _ := br.ReadBytes(int(hashEntry.NameLength - 1))
nameVal := br.ReadBytes(int(hashEntry.NameLength - 1))
value := string(nameVal)
br.SetPosition(uint64(hashEntry.IndexString))
key := ""

View File

@ -63,5 +63,4 @@ func (m *MapEngine) GenerateAct1Overworld(cacheTiles bool) {
}
}
}

View File

@ -20,6 +20,8 @@ import (
"github.com/beefsack/go-astar"
)
var imageCacheRecords map[uint32]d2render.Surface
type PathTile struct {
Walkable bool
Up, Down, Left, Right, UpLeft, UpRight, DownLeft, DownRight *PathTile
@ -76,28 +78,31 @@ func (t *PathTile) PathEstimatedCost(to astar.Pather) float64 {
}
type MapRegion struct {
tileRect d2common.Rectangle
regionPath string
levelType d2datadict.LevelTypeRecord
levelPreset d2datadict.LevelPresetRecord
tiles []d2dt1.Tile
ds1 *d2ds1.DS1
palette *d2dat.DATPalette
startX float64
startY float64
imageCacheRecords map[uint32]d2render.Surface
seed int64
currentFrame int
lastFrameTime float64
walkableArea [][]PathTile
tileRect d2common.Rectangle
regionPath string
levelType d2datadict.LevelTypeRecord
levelPreset d2datadict.LevelPresetRecord
tiles []d2dt1.Tile
ds1 *d2ds1.DS1
palette *d2dat.DATPalette
startX float64
startY float64
seed int64
currentFrame int
lastFrameTime float64
walkableArea [][]PathTile
}
// Invalidates the global region image cache. Call this when you are changing regions
func InvalidateImageCache() {
imageCacheRecords = nil
}
func loadRegion(seed int64, tileOffsetX, tileOffsetY int, levelType d2enum.RegionIdType, levelPreset int, fileIndex int, cacheTiles bool) (*MapRegion, []MapEntity) {
region := &MapRegion{
levelType: d2datadict.LevelTypes[levelType],
levelPreset: d2datadict.LevelPresets[levelPreset],
imageCacheRecords: map[uint32]d2render.Surface{},
seed: seed,
levelType: d2datadict.LevelTypes[levelType],
levelPreset: d2datadict.LevelPresets[levelPreset],
seed: seed,
}
region.palette, _ = loadPaletteForAct(levelType)
@ -613,12 +618,15 @@ func (mr *MapRegion) generateTileCache() {
func (mr *MapRegion) getImageCacheRecord(style, sequence byte, tileType d2enum.TileType, randomIndex byte) d2render.Surface {
lookupIndex := uint32(style)<<24 | uint32(sequence)<<16 | uint32(tileType)<<8 | uint32(randomIndex)
return mr.imageCacheRecords[lookupIndex]
return imageCacheRecords[lookupIndex]
}
func (mr *MapRegion) setImageCacheRecord(style, sequence byte, tileType d2enum.TileType, randomIndex byte, image d2render.Surface) {
lookupIndex := uint32(style)<<24 | uint32(sequence)<<16 | uint32(tileType)<<8 | uint32(randomIndex)
mr.imageCacheRecords[lookupIndex] = image
if imageCacheRecords == nil {
imageCacheRecords = make(map[uint32]d2render.Surface)
}
imageCacheRecords[lookupIndex] = image
}
func (mr *MapRegion) generateFloorCache(tile *d2ds1.FloorShadowRecord, tileX, tileY int) {

View File

@ -105,6 +105,7 @@ func CreateMapEngineTest(currentRegion int, levelPreset int) *MapEngineTest {
}
func (met *MapEngineTest) LoadRegionByIndex(n int, levelPreset, fileIndex int) {
d2map.InvalidateImageCache()
for _, spec := range regions {
if spec.regionType == d2enum.RegionIdType(n) {
met.regionSpec = spec

View File

@ -44,7 +44,7 @@ func Create(openNetworkServer bool) {
}
mapEngine := d2map.CreateMapEngine(singletonServer.seed)
mapEngine.GenerateAct1Overworld(true)
mapEngine.GenerateAct1Overworld(false)
singletonServer.mapEngines = append(singletonServer.mapEngines, mapEngine)
singletonServer.scriptEngine.AddFunction("getMapEngines", func(call otto.FunctionCall) otto.Value {

View File

@ -9,7 +9,9 @@ import (
"image/png"
"log"
"os"
"os/exec"
"runtime"
"runtime/pprof"
"strconv"
"sync"
@ -120,6 +122,12 @@ func initialize() error {
}
d2term.BindLogger()
d2term.BindAction("dumpheap", "dumps the heap to heap.out", func() {
fileOut, _ := os.Create("heap.out")
pprof.WriteHeapProfile(fileOut)
fileOut.Close()
exec.Command("go", "tool", "pprof", "--pdf", "./OpenDiablo2", "./heap.out", ">", "./memprofile.pdf")
})
d2term.BindAction("fullscreen", "toggles fullscreen", func() {
fullscreen := !d2render.IsFullScreen()
d2render.SetFullScreen(fullscreen)