Skip to content

Commit

Permalink
Use playdate realloc for memory management
Browse files Browse the repository at this point in the history
Fixes samdze#59
  • Loading branch information
Nycto committed Mar 10, 2024
1 parent f664dda commit 37012d1
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 17 deletions.
2 changes: 2 additions & 0 deletions playdate_example/config.nims
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
include playdate/build/config

switch("d", "memtrace")
6 changes: 3 additions & 3 deletions src/playdate/api.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import macros
import std/importutils

import bindings/utils {.all.} as memory
import bindings/api
export api

Expand All @@ -17,9 +16,10 @@ macro initSDK*() =
proc eventHandler(playdateAPI: ptr PlaydateAPI, event: PDSystemEvent, arg: uint32): cint {.cdecl, exportc.} =
privateAccess(PlaydateSys)
if event == kEventInit:
when declared(setupRealloc):
setupRealloc(playdateAPI.system.realloc)
NimMain()
api.playdate = playdateAPI
memory.realloc = playdateAPI.system.realloc
handler(event, arg)
return 0

Expand Down Expand Up @@ -50,4 +50,4 @@ when not defined(simulator) and defined(release):
return 0

proc write(handle: cint, data: ptr cchar, size: cint): cint {.cdecl, exportc: "_write".} =
return -1
return -1
15 changes: 9 additions & 6 deletions src/playdate/bindings/graphics.nim
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ type LCDBitmapTablePtr {.importc: "LCDBitmapTable*", header: "pd_api.h".} = poin
type LCDFontPtr {.importc: "LCDFont*", header: "pd_api.h".} = pointer
type LCDFontObj = object
resource: LCDFontPtr
proc `=destroy`(this: var LCDFontObj) =
discard utils.realloc(this.resource, 0)

proc `=destroy`(this: var LCDFontObj) = deallocImpl(this.resource)

type LCDFont* = ref LCDFontObj

type LCDFontDataPtr {.importc: "LCDFontData*", header: "pd_api.h".} = object
Expand All @@ -83,15 +84,17 @@ type LCDFontData* = LCDFontDataPtr
type LCDFontPagePtr {.importc: "LCDFontPage*", header: "pd_api.h".} = pointer
type LCDFontPageObj = object
resource: LCDFontPagePtr
proc `=destroy`(this: var LCDFontPageObj) =
discard utils.realloc(this.resource, 0)

proc `=destroy`(this: var LCDFontPageObj) = deallocImpl(this.resource)

type LCDFontPage* = ref LCDFontPageObj

type LCDFontGlyphPtr {.importc: "LCDFontGlyph*", header: "pd_api.h".} = pointer
type LCDFontGlyphObj = object
resource: LCDFontGlyphPtr
proc `=destroy`(this: var LCDFontGlyphObj) =
discard utils.realloc(this.resource, 0)

proc `=destroy`(this: var LCDFontGlyphObj) = deallocImpl(this.resource)

type LCDFontGlyph* = ref LCDFontGlyphObj

type LCDVideoPlayerRaw {.importc: "LCDVideoPlayer", header: "pd_api.h".} = object
Expand Down
68 changes: 68 additions & 0 deletions src/playdate/bindings/malloc.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
##
## This file is a re-implementation of memalloc.nim in the Nim standard library. It is intended to be used by patching
## it in as a replacement in your configs.nim file, like this:
##
## ```nim
## patchFile("stdlib", "malloc", nimblePlaydatePath / "src/playdate/bindings/malloc")
## ```
##
## This file exists to ensure that Nim itself is using the memory allocators provided by the playdate SDK
##

{.push stackTrace: off.}

import system/ansi_c

type PDRealloc = proc (p: pointer; size: csize_t): pointer {.tags: [], raises: [], cdecl, gcsafe.}

var pdrealloc: PDRealloc

proc setupRealloc*(allocator: PDRealloc) =
when defined(memtrace):
cfprintf(cstderr, "Setting up playdate allocator")
pdrealloc = allocator

proc allocImpl(size: Natural): pointer =
when defined(memtrace):
cfprintf(cstderr, "Allocating %d\n", size)
result = pdrealloc(nil, size.csize_t)
when defined(memtrace):
cfprintf(cstderr, " At %p\n", result)

proc alloc0Impl(size: Natural): pointer =
result = allocImpl(size)
zeroMem(result, size)

proc reallocImpl(p: pointer, newSize: Natural): pointer =
when defined(memtrace):
cfprintf(cstderr, "Reallocating %p with size %d\n", p, newSize)
return pdrealloc(p, newSize.csize_t)

proc realloc0Impl(p: pointer, oldsize, newSize: Natural): pointer =
result = realloc(p, newSize.csize_t)
if newSize > oldSize:
zeroMem(cast[pointer](cast[uint](result) + uint(oldSize)), newSize - oldSize)

proc deallocImpl(p: pointer) =
when defined(memtrace):
cfprintf(cstderr, "Freeing %p\n", p)
discard pdrealloc(p, 0)

# The shared allocators map on the regular ones

proc allocSharedImpl(size: Natural): pointer {.used.} = allocImpl(size)

proc allocShared0Impl(size: Natural): pointer {.used.} = alloc0Impl(size)

proc reallocSharedImpl(p: pointer, newSize: Natural): pointer {.used.} = reallocImpl(p, newSize)

proc reallocShared0Impl(p: pointer, oldsize, newSize: Natural): pointer {.used.} = realloc0Impl(p, oldSize, newSize)

proc deallocSharedImpl(p: pointer) {.used.} = deallocImpl(p)

proc getOccupiedMem(): int {.used.} = discard
proc getFreeMem(): int {.used.} = discard
proc getTotalMem(): int {.used.} = discard
proc deallocOsPages() {.used.} = discard

{.pop.}
3 changes: 1 addition & 2 deletions src/playdate/bindings/system.nim
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ type PDMenuItemCallbackFunctionRaw {.importc: "PDMenuItemCallbackFunction", head
# System
sdktype:
type PlaydateSys* {.importc: "const struct playdate_sys", header: "pd_api.h".} = object
realloc {.importc: "realloc".}: proc (`ptr`: pointer; size: csize_t): pointer {.
cdecl, raises: [].}
realloc {.importc: "realloc".}: proc (`ptr`: pointer; size: csize_t): pointer {.cdecl, raises: [], tags: [], gcsafe.}
formatString {.importc: "formatString".}: proc (ret: cstringArray; fmt: cstring): cint {.
cdecl, varargs, raises: [].}
logToConsole {.importc: "logToConsole".}: proc (fmt: cstring) {.cdecl, varargs, raises: [].}
Expand Down
2 changes: 0 additions & 2 deletions src/playdate/bindings/utils.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import macros

var realloc*: proc(p: pointer, size: csize_t): pointer {.cdecl.}

func toNimSymbol(typeSymbol: string): string =
case typeSymbol:
of "cint":
Expand Down
10 changes: 8 additions & 2 deletions src/playdate/build/config.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ const headlessTesting = defined(simulator) and declared(test)
const nimbleTesting = not defined(simulator) and not defined(devide) and declared(test)
const testing = headlessTesting or nimbleTesting

# The path to the nimble playdate package
const nimblePlaydatePath = currentSourcePath / "../../../../"

if not testing:
switch("noMain", "on")
switch("backend", "c")
Expand Down Expand Up @@ -154,8 +157,11 @@ if nimbleTesting:
switch("passC", "-DTARGET_SIMULATOR=1")
switch("passC", "-Wstrict-prototypes")
else:
# Add extra files to compile last, so that
# Add extra files to compile last, so that
# they get compiled in the correct nimcache folder.
# Windows doesn't like having setup.c compiled.
if defined(device) or not defined(windows):
switch("compile", sdkPath() / "C_API" / "buildsupport" / "setup.c")
switch("compile", sdkPath() / "C_API" / "buildsupport" / "setup.c")

# Overrides the nim memory management code to ensure it uses the playdate allocator
patchFile("stdlib", "malloc", nimblePlaydatePath / "src/playdate/bindings/malloc")
3 changes: 1 addition & 2 deletions src/playdate/types.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import bindings/utils

type SDKArrayObj[T] = object
len: int
Expand All @@ -7,7 +6,7 @@ type SDKArray*[T] = ref SDKArrayObj[T]

proc `=destroy`*[T](this: var SDKArrayObj[T]) =
if this.data != nil:
discard utils.realloc(this.data, 0)
deallocImpl(this.data)

proc `[]`*[T](this: SDKArray[T]; i: Natural): lent T =
assert i < this.len
Expand Down

0 comments on commit 37012d1

Please sign in to comment.