Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Elide memtrace when disabled #97

Merged
merged 7 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/actions/macos-build-setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: macos build setup
inputs:
nim-version:
required: true
runs:
using: "composite"
steps:

- name: Git safe directory
shell: bash
run: git config --global --add safe.directory "$(pwd)"

- uses: iffy/install-nim@v5
with:
version: ${{ inputs.nim-version }}
- run: nimble --accept refresh
shell: bash

- run: nimble install
shell: bash

- name: Locally publish playdate nimble package
shell: bash
if: ${{ startsWith(inputs.nim-version, '1.') }}
run: nimble develop

- name: Install Playdate SDK
id: playdate
uses: pd-rs/[email protected]
with:
version: latest # possible values: version `x.x.x` or `latest` by default

- name: print playdate sdk info
shell: bash
run: |
echo "SDK path env: $PLAYDATE_SDK_PATH"
echo "SDK root out: ${{ steps.playdate.outputs.root }}"
echo "SDK version: ${{ steps.playdate.outputs.version }}"
pdc --version # because SDK/bin already in PATH
17 changes: 15 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,26 @@ name: Build
on: [push, pull_request]
jobs:

compile-example-project-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/macos-build-setup
with:
nim-version: 2.0.8
- uses: ./.github/actions/project-setup
with:
working-directory: ./playdate_example
- run: nimble simulator
working-directory: ./playdate_example

example-project:
runs-on: ubuntu-latest
container: nimlang/choosenim
strategy:
matrix:
target: [ device, simulator ]
nim-version: [ 2.0.8 ]
runs-on: ubuntu-latest
container: nimlang/choosenim
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/build-setup
Expand Down
4 changes: 2 additions & 2 deletions playdate_example/src/playdate_example.nim
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ proc handler(event: PDSystemEvent, keycode: uint) {.raises: [].} =
font = try: playdate.graphics.newFont(FONT_PATH) except: nil
playdate.graphics.setFont(font)

playdateNimBitmap = try: playdate.graphics.newBitmap(PLAYDATE_NIM_IMAGE_PATH) except: return
nimLogoBitmap = try: playdate.graphics.newBitmap(NIM_IMAGE_PATH) except: return
playdateNimBitmap = try: playdate.graphics.newBitmap(PLAYDATE_NIM_IMAGE_PATH) except: nil
nimLogoBitmap = try: playdate.graphics.newBitmap(NIM_IMAGE_PATH) except: nil

sprite = playdate.sprite.newSprite()
sprite.add()
Expand Down
8 changes: 4 additions & 4 deletions src/playdate/api.nim
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{.push raises: [].}

import macros
import std/importutils
import std/importutils, util/initreqs

import bindings/api
import bindings/[api, initreqs]
export api

import graphics, system, file, sprite, display, sound, scoreboards, lua, json, utils, types, nineslice
Expand All @@ -15,9 +15,9 @@ macro initSDK*() =

proc eventHandler(playdateAPI: ptr PlaydateAPI, event: PDSystemEvent, arg: uint32): cint {.cdecl, exportc.} =
privateAccess(PlaydateSys)
privateAccess(PlaydateFile)
if event == kEventInit:
when declared(setupRealloc):
setupRealloc(playdateAPI.system.realloc)
initPrereqs(playdateAPI.system.realloc, playdateAPI.system.logToConsole)
NimMain()
api.playdate = playdateAPI
handler(event, arg)
Expand Down
11 changes: 7 additions & 4 deletions src/playdate/bindings/graphics.nim
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{.push raises: [].}

import utils, types
import utils, types, ../util/initreqs

type LCDRect* {.importc: "LCDRect", header: "pd_api.h".} = object
left* {.importc.}: int # int32?
Expand Down Expand Up @@ -74,7 +74,8 @@ type LCDFontPtr {.importc: "LCDFont*", header: "pd_api.h".} = pointer
type LCDFontObj = object
resource: LCDFontPtr

proc `=destroy`(this: var LCDFontObj) = deallocImpl(this.resource)
proc `=destroy`(this: var LCDFontObj) =
discard pdrealloc(this.resource, 0)

type LCDFont* = ref LCDFontObj

Expand All @@ -85,15 +86,17 @@ type LCDFontPagePtr {.importc: "LCDFontPage*", header: "pd_api.h".} = pointer
type LCDFontPageObj = object
resource: LCDFontPagePtr

proc `=destroy`(this: var LCDFontPageObj) = deallocImpl(this.resource)
proc `=destroy`(this: var LCDFontPageObj) =
discard pdrealloc(this.resource, 0)

type LCDFontPage* = ref LCDFontPageObj

type LCDFontGlyphPtr {.importc: "LCDFontGlyph*", header: "pd_api.h".} = pointer
type LCDFontGlyphObj = object
resource: LCDFontGlyphPtr

proc `=destroy`(this: var LCDFontGlyphObj) = deallocImpl(this.resource)
proc `=destroy`(this: var LCDFontGlyphObj) =
discard pdrealloc(this.resource, 0)

type LCDFontGlyph* = ref LCDFontGlyphObj

Expand Down
10 changes: 10 additions & 0 deletions src/playdate/bindings/initreqs.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
##
## Contains direct references to playdate apis that are required to be usable before the
## full API is itself available.
##

var pdrealloc*: proc (p: pointer; size: csize_t): pointer {.tags: [], raises: [], cdecl, gcsafe.}

proc initPrereqs*(realloc: auto) =
## Sets pointers to functions that are needed early in the initialization process
pdrealloc = realloc
63 changes: 29 additions & 34 deletions src/playdate/bindings/malloc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -14,62 +14,57 @@

{.push stackTrace: off.}

# Forward declaration for memory profiling support
when defined(memProfiler):
proc nimProfile(requestedSize: int)
import ../util/initreqs

import memtrace
import system/ansi_c
when defined(memProfiler):

type PDRealloc = proc (p: pointer; size: csize_t): pointer {.tags: [], raises: [], cdecl, gcsafe.}
# Forward declaration for memory profiling support
proc nimProfile(requestedSize: int)

proc nativeAlloc(p: pointer; size: csize_t): pointer {.tags: [], raises: [], cdecl, gcsafe.} =
if p == nil:
return c_malloc(size)
elif size == 0:
c_free(p)
return nil
else:
return c_realloc(p, size)
template rawAlloc(size): untyped =
# Integrage with: https://nim-lang.org/docs/estp.html
try:
nimProfile(size.int)
except:
discard
pdrealloc(nil, size)

var pdrealloc: PDRealloc
template rawRealloc(p, size): untyped = pdrealloc(p, size)
template rawDealloc(p) = discard pdrealloc(p, 0)

var trace: MemTrace
elif defined(memtrace):
import ../util/memtrace
var trace: MemTrace

template rawAlloc(size): untyped = traceAlloc(trace, pdrealloc, size)
template rawRealloc(p, size): untyped = traceRealloc(trace, pdrealloc, p, size)
template rawDealloc(p) = traceDealloc(trace, pdrealloc, p)

proc setupRealloc*(allocator: PDRealloc) =
when defined(memtrace):
cfprintf(cstderr, "Setting up playdate allocator")
when defined(nativeAlloc):
pdrealloc = nativeAlloc
else:
pdrealloc = allocator
else:
template rawAlloc(size): untyped = pdrealloc(nil, size)
template rawRealloc(p, size): untyped = pdrealloc(p, size)
template rawDealloc(p) = discard pdrealloc(p, 0)

proc allocImpl(size: Natural): pointer =
# Integrage with: https://nim-lang.org/docs/estp.html
when defined(memProfiler):
{.cast(tags: []).}:
try:
nimProfile(size.int)
except:
discard

return trace.alloc(pdrealloc, size.csize_t)
{.cast(tags: []).}:
return rawAlloc(size.csize_t)

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

proc reallocImpl(p: pointer, newSize: Natural): pointer =
return trace.realloc(pdrealloc, p, newSize)
{.cast(tags: []).}:
return rawRealloc(p, newSize.csize_t)

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

proc deallocImpl(p: pointer) =
trace.dealloc(pdrealloc, p)
{.cast(tags: []).}:
rawDealloc(p)

# The shared allocators map on the regular ones

Expand Down
4 changes: 2 additions & 2 deletions src/playdate/bindings/system.nim
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ 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: [], tags: [], gcsafe.}
realloc {.importc: "realloc".}: PDRealloc
formatString {.importc: "formatString".}: proc (ret: cstringArray; fmt: cstring): cint {.
cdecl, varargs, raises: [].}
logToConsole {.importc: "logToConsole".}: proc (fmt: cstring) {.cdecl, varargs, raises: [].}
logToConsole {.importc: "logToConsole".}: PDLog
error {.importc: "error".}: proc (fmt: cstring) {.cdecl, varargs, raises: [].}
getLanguage {.importsdk.}: proc (): PDLanguage
getCurrentTimeMilliseconds {.importsdk.}: proc (): cuint
Expand Down
14 changes: 10 additions & 4 deletions src/playdate/bindings/types.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
type ConstChar* {.importc: "const char*".} = cstring
type ConstCharPtr* {.importc: "const char**".} = cstring
type Char* {.importc: "char*".} = cstring
type
ConstChar* {.importc: "const char*".} = cstring
ConstCharPtr* {.importc: "const char**".} = cstring
Char* {.importc: "char*".} = cstring
LCDBitmapPtr* {.importc: "LCDBitmap*", header: "pd_api.h".} = pointer

type LCDBitmapPtr* {.importc: "LCDBitmap*", header: "pd_api.h".} = pointer
PDLog* = proc (fmt: ConstChar) {.cdecl, varargs, raises: [].}
## The type signature for playdate.system.logToConsole

PDRealloc* = proc (p: pointer; size: csize_t): pointer {.tags: [], raises: [], cdecl, gcsafe.}
## The type signature for playdate.system.realloc
19 changes: 11 additions & 8 deletions src/playdate/graphics.nim
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,15 @@ proc `=destroy`(this: var LCDBitmapObj) =

proc `=copy`(a: var LCDBitmapObj, b: LCDBitmapObj) {.error.}

proc bitmapPtr(point: LCDBitmapPtr): auto =
converter bitmapPtr*(point: LCDBitmapPtr): auto =
LCDBitmap(managed: false, res: point)

proc bitmapRef(point: LCDBitmapPtr): auto =
LCDBitmap(managed: true, obj: LCDBitmapObjRef(res: point))

proc `==`*(bitmap: LCDBitmap, point: LCDBitmapPtr): bool =
not bitmap.managed and bitmap.res == point

proc resource*(bitmap: LCDBitmap): LCDBitmapPtr =
if bitmap.managed:
return if bitmap.obj != nil: bitmap.obj.res else: nil
Expand All @@ -53,7 +56,7 @@ type LCDVideoPlayerObj = object of RootObj
proc `=destroy`(this: var LCDVideoPlayerObj) =
privateAccess(PlaydateVideo)
playdate.graphics.video.freePlayer(this.resource)
this.context.reset()
this.context = nil
type LCDVideoPlayer* = ref LCDVideoPlayerObj

proc newVideoPlayer*(this: ptr PlaydateVideo, path: string): LCDVideoPlayer {.raises: [IOError]} =
Expand All @@ -72,7 +75,7 @@ proc setContext*(this: LCDVideoPlayer, context: LCDBitmap) {.raises: [CatchableE
proc useScreenContext*(this: LCDVideoPlayer) =
privateAccess(PlaydateVideo)
playdate.graphics.video.useScreenContext(this.resource)
this.context.reset()
this.context = nil

proc renderFrame*(this: LCDVideoPlayer, index: int) {.raises: [CatchableError]} =
privateAccess(PlaydateVideo)
Expand All @@ -90,7 +93,7 @@ proc getContext*(this: LCDVideoPlayer): LCDBitmap =
privateAccess(PlaydateVideo)
let bitmapPtr = playdate.graphics.video.getContext(this.resource)
playdate.system.logToConsole(fmt"video context: {bitmapPtr.repr}")
if this.context.isNil or this.context.resource != bitmapPtr:
if this.context == nil or this.context.resource != bitmapPtr:
this.context = bitmapPtr(bitmapPtr)
return this.context

Expand All @@ -107,7 +110,7 @@ proc getFont*(this: ptr PlaydateGraphics): LCDFont =

proc pushContext*(this: ptr PlaydateGraphics, target: LCDBitmap) =
privateAccess(PlaydateGraphics)
this.pushContext(target.resource)
this.pushContext(if target != nil: target.resource else: nil)

proc draw*(this: LCDBitmap, x: int, y: int, flip: LCDBitmapFlip) =
privateAccess(PlaydateGraphics)
Expand Down Expand Up @@ -175,7 +178,7 @@ template read(bitmap: AnyBitmapData, x, y: int): untyped =
proc getDataObj*(this: LCDBitmap): BitmapDataObj =
## Fetch the underlying bitmap data for an image.
privateAccess(PlaydateGraphics)
assert(not this.isNil)
assert(this != nil)
assert(this.resource != nil)
playdate.graphics.getBitmapData(
this.resource,
Expand All @@ -193,7 +196,7 @@ proc getData*(this: LCDBitmap): BitmapData =

proc getSize*(this: LCDBitmap): tuple[width: int, height: int] =
privateAccess(PlaydateGraphics)
assert(not this.isNil)
assert(this != nil)
assert(this.resource != nil)
var width, height: cint
playdate.graphics.getBitmapData(this.resource, addr(width), addr(height), nil,
Expand Down Expand Up @@ -440,7 +443,7 @@ proc set*(this: var LCDBitmap, x, y: int, color: LCDSolidColor = kColorBlack) =

proc setStencilImage*(this: ptr PlaydateGraphics, bitmap: LCDBitmap, tile: bool = false) =
privateAccess(PlaydateGraphics)
if bitmap.isNil:
if bitmap == nil:
this.setStencilImage(nil, if tile: 1 else: 0)
else:
this.setStencilImage(bitmap.resource, if tile: 1 else: 0)
Expand Down
4 changes: 2 additions & 2 deletions src/playdate/sprite.nim
Original file line number Diff line number Diff line change
Expand Up @@ -404,12 +404,12 @@ proc setStencilPattern*(this: LCDSprite, pattern: array[8, uint8]) =
proc clearStencil*(this: LCDSprite) =
privateAccess(PlaydateSprite)
playdate.sprite.clearStencil(this.resource)
this.stencil.reset()
this.stencil = nil

proc setStencilImage*(this: LCDSprite, stencil: LCDBitmap, tile: bool) =
privateAccess(PlaydateSprite)
privateAccess(LCDBitmap)
playdate.sprite.setStencilImage(this.resource, stencil.resource, if tile: 1 else: 0)
playdate.sprite.setStencilImage(this.resource, if stencil != nil: stencil.resource else: nil, if tile: 1 else: 0)
this.stencil = stencil

proc setCenter*(this: LCDSprite, x: float32, y: float32) =
Expand Down
3 changes: 2 additions & 1 deletion src/playdate/types.nim
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import util/initreqs

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

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

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