Skip to content

Commit

Permalink
Refactor: simplified the rendering allocation code
Browse files Browse the repository at this point in the history
  • Loading branch information
Martomate committed Feb 12, 2024
1 parent 2e6d2dc commit 3037bf8
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 35 deletions.
38 changes: 33 additions & 5 deletions game/src/main/scala/hexacraft/util/segments.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class SegmentSet extends mutable.Iterable[Segment] {
* or be part of a bigger existing segment `[c, d]` where `c <= a` and `d >= b`
*/
def remove(seg: Segment): Boolean = {
containedInSegments(seg) match {
segmentsContain.get(seg) match {
case Some(other) =>
val len1 = seg.start - other.start
val len2 = other.length - len1 - seg.length
Expand All @@ -66,8 +66,25 @@ class SegmentSet extends mutable.Iterable[Segment] {
}
}

private def containedInSegments(seg: Segment): Option[Segment] = {
segmentsContain.get(seg)
def cut(n: Int): (SegmentSet, SegmentSet) = {
val before = new SegmentSet
val after = new SegmentSet

var start = 0
for s <- this do {
if start + s.length <= n then {
before.add(s)
} else if start >= n then {
after.add(s)
} else {
val l = n - start
before.add(Segment(s.start, l))
after.add(Segment(s.start + l, s.length - l))
}
start += s.length
}

(before, after)
}

def totalLength: Int = _totalLength
Expand All @@ -77,6 +94,14 @@ class SegmentSet extends mutable.Iterable[Segment] {
def firstSegment(): Segment = segments.firstKey
def lastSegment(): Segment = segments.last
def segmentCount: Int = segments.size

override def clone(): SegmentSet = {
val res = new SegmentSet
for s <- this do {
res._add(s)
}
res
}
}

// TODO: make thread-safe, or rewrite it
Expand Down Expand Up @@ -197,8 +222,11 @@ class DenseKeyedSegmentStack[K] {
contentMap.get(key).exists(_.remove(segment))
}

def segments(key: K): Iterable[Segment] = {
contentMap.getOrElse(key, Iterable.empty)
def segments(key: K): SegmentSet = {
contentMap.get(key) match {
case Some(segments) => segments.clone()
case None => new SegmentSet
}
}

def lastSegment: Option[(K, Segment)] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ class RenderAspectHandler(bufferHandler: BufferHandler[_]) {
}

def clearChunkContent(coords: ChunkRelWorld): Unit = {
removeChunk(coords)
removeData(coords, memorySegments.segments(coords))
}

def unload(): Unit = {
bufferHandler.unload()
}

private def appendData(coords: ChunkRelWorld, data: ByteBuffer): Unit = {
Expand Down Expand Up @@ -55,27 +59,15 @@ class RenderAspectHandler(bufferHandler: BufferHandler[_]) {
// append the rest of the data at the end of the buffer
appendData(coords, data)
} else { // The new data takes less space than the old data
val leftOver: SegmentSet = new SegmentSet
val (before, after) = memorySegments.segments(coords).cut(newLen)

// overwrite the first segments that can be completely filled with the new data
for s <- memorySegments.segments(coords) do {
if data.remaining() >= s.length then {
bufferHandler.set(s, data)
} else {
leftOver.add(s)
}
}

// overwrite the rest of the new data into part of an existing segment
val splitSeg = leftOver.firstSegment()
if data.remaining() > 0 then {
val first = Segment(splitSeg.start, data.remaining())
bufferHandler.set(first, data)
leftOver.remove(first)
// overwrite the old data with the new data
for s <- before do {
bufferHandler.set(s, data)
}

// remove the space used by the old data but not needed by the new data (by moving data from the end of buffer)
removeData(coords, leftOver)
removeData(coords, after)
}
}

Expand Down Expand Up @@ -103,16 +95,4 @@ class RenderAspectHandler(bufferHandler: BufferHandler[_]) {
}
}
}

private def removeChunk(coords: ChunkRelWorld): Unit = {
val segments = new SegmentSet
for s <- memorySegments.segments(coords) do {
segments.add(s)
}
removeData(coords, segments)
}

def unload(): Unit = {
bufferHandler.unload()
}
}
33 changes: 33 additions & 0 deletions game/src/test/scala/hexacraft/util/SegmentSetTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,38 @@ class SegmentSetTest extends FunSuite {
assertEquals(set.iterator.toSeq, Seq(Segment(2, 4), Segment(12, 1), Segment(16, 2)))
}

test("cut can split segments") {
val set = make
set.add(Segment(0, 1))
set.add(Segment(2, 4))
set.add(Segment(12, 1))

val (before, after) = set.cut(3)
assertEquals(before.toSeq, Seq(Segment(0, 1), Segment(2, 2)))
assertEquals(after.toSeq, Seq(Segment(4, 2), Segment(12, 1)))
}

test("cut works at the beginning") {
val set = make
set.add(Segment(0, 1))
set.add(Segment(2, 4))
set.add(Segment(12, 1))

val (before, after) = set.cut(0)
assertEquals(before.toSeq, Seq())
assertEquals(after.toSeq, Seq(Segment(0, 1), Segment(2, 4), Segment(12, 1)))
}

test("cut works at the end") {
val set = make
set.add(Segment(0, 1))
set.add(Segment(2, 4))
set.add(Segment(12, 1))

val (before, after) = set.cut(6)
assertEquals(before.toSeq, Seq(Segment(0, 1), Segment(2, 4), Segment(12, 1)))
assertEquals(after.toSeq, Seq())
}

def make: SegmentSet = new SegmentSet
}

0 comments on commit 3037bf8

Please sign in to comment.