Compare commits

...

3 commits

Author SHA1 Message Date
JetSetIlly d7b8f19c21 added memory usage to FPS overlay 2024-05-03 18:38:20 +01:00
JetSetIlly 578e2a846a ARM package now receives environment instance
environment used to test the extent of the ARM.Plumb() function. this
function is called often from the caching package. some of the work done
by the Plumb() function is not needed for caching purposes and cases a
lot of memory churn
2024-05-03 18:04:04 +01:00
JetSetIlly 8e4838d582 removed prefs update ticker from ARM
it's not necessary for performance and worse, it causes memory churn due
to how the gui caching package works
2024-05-03 12:35:05 +01:00
10 changed files with 70 additions and 46 deletions

View file

@ -17,6 +17,7 @@ package sdlimgui
import (
"fmt"
"runtime"
"time"
"github.com/inkyblackness/imgui-go/v4"
@ -68,6 +69,9 @@ type playscrOverlay struct {
fps string
hz string
// memory stats are updated along with the fpsPulse
memStats runtime.MemStats
// top-left corner of the overlay includes emulation state. if the
// "fpsOverlay" is active then these will be drawn alongside the FPS
// information
@ -195,6 +199,7 @@ func (oly *playscrOverlay) drawTopLeft() {
fps, hz := oly.playscr.img.dbg.VCS().TV.GetActualFPS()
oly.fps = fmt.Sprintf("%03.2f fps", fps)
oly.hz = fmt.Sprintf("%03.2fhz", hz)
runtime.ReadMemStats(&oly.memStats)
default:
}
@ -234,6 +239,14 @@ func (oly *playscrOverlay) drawTopLeft() {
imgui.Text(string(fonts.Nudge))
}
if oly.playscr.img.prefs.memoryUsageInOverlay.Get().(bool) {
imguiSeparator()
imgui.Text(fmt.Sprintf("Alloc = %v MB\n", oly.memStats.Alloc/1048576))
imgui.Text(fmt.Sprintf(" TotalAlloc = %v MB\n", oly.memStats.TotalAlloc/1048576))
imgui.Text(fmt.Sprintf(" Sys = %v MB\n", oly.memStats.Sys/1048576))
imgui.Text(fmt.Sprintf(" NumGC = %v", oly.memStats.NumGC))
}
// create space in the window for any icons that we might want to draw.
// what's good about this is that it makes sure that the window is large
// enough from frame-to-frame. without this, there will be a visble

View file

@ -60,6 +60,7 @@ type preferences struct {
superchargerNotifications prefs.Bool
audioMuteNotification prefs.Bool
notificationVisibility prefs.Float
memoryUsageInOverlay prefs.Bool
// fonts
guiFontSize prefs.Int
@ -98,6 +99,7 @@ func newPreferences(img *SdlImgui) (*preferences, error) {
p.superchargerNotifications.Set(true)
p.audioMuteNotification.Set(true)
p.notificationVisibility.Set(0.75)
p.memoryUsageInOverlay.Set(false)
p.guiFontSize.Set(13)
p.terminalFontSize.Set(12)
p.codeFontSize.Set(15)
@ -166,6 +168,10 @@ func newPreferences(img *SdlImgui) (*preferences, error) {
if err != nil {
return nil, err
}
err = p.dsk.Add("sdlimgui.playmode.memoryUsageInOverlay", &p.memoryUsageInOverlay)
if err != nil {
return nil, err
}
// playmode audio mute options later

View file

@ -216,7 +216,7 @@ a television image that is sympathetic to the display kernel
of the ROM.`)
imgui.Spacing()
if imgui.CollapsingHeader("Notification Icons") {
if imgui.CollapsingHeader("Notification Overlay") {
controllerNotifications := win.img.prefs.controllerNotifcations.Get().(bool)
if imgui.Checkbox("Controller Changes", &controllerNotifications) {
win.img.prefs.controllerNotifcations.Set(controllerNotifications)
@ -241,6 +241,11 @@ of the ROM.`)
if imgui.SliderFloatV("Visibility", &visibility, 0.0, 100.0, "%.0f%%", imgui.SliderFlagsNone) {
win.img.prefs.notificationVisibility.Set(visibility / 100)
}
memoryUsageInOverlay := win.img.prefs.memoryUsageInOverlay.Get().(bool)
if imgui.Checkbox("Memory Usage in FPS Overlay", &memoryUsageInOverlay) {
win.img.prefs.memoryUsageInOverlay.Set(memoryUsageInOverlay)
}
}
imgui.Spacing()

View file

@ -106,7 +106,7 @@ func (cart *Ace) PlumbFromDifferentEmulation(env *environment.Environment) {
}
cart.arm = arm.NewARM(cart.env, cart.mem.model, cart.mem, cart)
cart.mem.Plumb(cart.arm)
cart.arm.Plumb(cart.armState, cart.mem, cart)
cart.arm.Plumb(cart.env, cart.armState, cart.mem, cart)
cart.armState = nil
cart.yieldHook = coprocessor.StubCartYieldHook{}
}
@ -118,7 +118,7 @@ func (cart *Ace) Plumb(env *environment.Environment) {
panic("cannot plumb this ELF instance because the ARM state is nil")
}
cart.mem.Plumb(cart.arm)
cart.arm.Plumb(cart.armState, cart.mem, cart)
cart.arm.Plumb(cart.env, cart.armState, cart.mem, cart)
cart.armState = nil
}

View file

@ -20,7 +20,6 @@ import (
"fmt"
"math"
"strings"
"time"
"github.com/jetsetilly/gopher2600/coprocessor"
"github.com/jetsetilly/gopher2600/coprocessor/developer/faults"
@ -190,12 +189,18 @@ type ARMState struct {
instruction32bitOpcodeHi uint16
}
// Snapshort makes a copy of the ARMState.
// Snapshot implements the mapper.CartMapper interface.
func (s *ARMState) Snapshot() *ARMState {
n := *s
return &n
}
// Plumb implements the mapper.CartMapper interface.
func (s *ARMState) Plumb(env *environment.Environment) {
s.mam.Plumb(env)
s.rng.Plumb(env)
}
// ARM implements the ARM7TDMI-S LPC2103 processor.
type ARM struct {
env *environment.Environment
@ -214,12 +219,7 @@ type ARM struct {
// state of the ARM. saveable and restorable
state *ARMState
// updating the preferences every time run() is executed can be slow
// (because the preferences need to be synchronised between tasks). the
// prefsPulse ticker slows the rate at which updatePrefs() is called
prefsPulse *time.Ticker
// read from ARM.prefs every prefsPulse tick
// updated on every call to run()
abortOnMemoryFault bool
misalignedAccessIsFault bool
@ -261,7 +261,8 @@ type ARM struct {
// interface to an option development package
dev coprocessor.CartCoProcDeveloper
// whether cycle count or not. set from ARM.prefs at the start of every arm.Run()
// immediateMode controls whether cycle count or not. value updated from
// updatePrefs()
//
// used to cut out code that is required only for cycle counting. See
// Icycle, Scycle and Ncycle fields which are called so frequently we
@ -294,12 +295,7 @@ func NewARM(env *environment.Environment, mmap architecture.Map, mem SharedMemor
hook: hook,
byteOrder: binary.LittleEndian,
executionCache: make(map[uint32][]decodeFunction),
// updated on every updatePrefs(). these are reasonable defaults
Clk: 70.0,
clklenFlash: 4.0,
state: &ARMState{},
state: &ARMState{},
}
// disassembly printed to stdout
@ -307,9 +303,6 @@ func NewARM(env *environment.Environment, mmap architecture.Map, mem SharedMemor
arm.disasm = &coprocessor.CartCoProcDisassemblerStdout{}
}
// slow prefs update by 100ms
arm.prefsPulse = time.NewTicker(time.Millisecond * 100)
switch arm.mmap.ARMArchitecture {
case architecture.ARM7TDMI:
arm.stepFunction = arm.stepARM7TDMI
@ -366,7 +359,7 @@ func (arm *ARM) SetDeveloper(dev coprocessor.CartCoProcDeveloper) {
arm.dev = dev
}
// Snapshort makes a copy of the ARM state.
// Snapshot implements the mapper.CartMapper interface.
func (arm *ARM) Snapshot() *ARMState {
return arm.state.Snapshot()
}
@ -377,25 +370,32 @@ func (arm *ARM) Snapshot() *ARMState {
// The ARMState argument can be nil as a special case. If it is nil then the
// existing state does not change. For some cartridge mappers this is acceptable
// and more convenient
func (arm *ARM) Plumb(state *ARMState, mem SharedMemory, hook CartridgeHook) {
//
// Plumb implements the mapper.CartMapper interface.
func (arm *ARM) Plumb(env *environment.Environment, state *ARMState, mem SharedMemory, hook CartridgeHook) {
arm.env = env
arm.mem = mem
arm.hook = hook
// always clear caches on a plumb event
arm.ClearCaches()
if state != nil {
arm.state = state
arm.state.Plumb(env)
}
// if we're plumbing in a new state then we *must* reevaluate the
// pointer the program memory
// any more plumbing work is superfluous unless we're dealing with the main
// emulation environment
if !arm.env.IsEmulation(environment.MainEmulation) {
return
}
// if we're plumbing in a new state then we *must* reevaluate the
// pointer the program memory
if state != nil {
arm.checkProgramMemory(true)
}
}
// ClearCaches should be used very rarely. It empties the instruction and
// disassembly caches.
func (arm *ARM) ClearCaches() {
// execution cache must be cleared because the old cache will be pointing to
// functions in another instance of ARM
arm.executionCache = make(map[uint32][]decodeFunction)
}
@ -436,8 +436,7 @@ func (arm *ARM) resetRegisters() {
}
// updatePrefs should be called periodically to ensure that the current
// preference values are being used in the ARM emulation. see also the
// prefsPulse ticker
// preference values are being used in the ARM emulation
func (arm *ARM) updatePrefs() {
// update clock value from preferences
arm.Clk = float32(arm.env.Prefs.ARM.Clock.Get().(float64))
@ -756,11 +755,7 @@ func (arm *ARM) checkProgramMemory(force bool) {
}
func (arm *ARM) run() (coprocessor.CoProcYield, float32) {
select {
case <-arm.prefsPulse.C:
arm.updatePrefs()
default:
}
arm.updatePrefs()
// number of iterations. only used when in immediate mode
var iterations int

View file

@ -59,7 +59,8 @@ func newMam(env *environment.Environment, mmap architecture.Map) mam {
}
}
func (m *mam) Reset() {
func (m *mam) Plumb(env *environment.Environment) {
m.env = env
}
func (m *mam) updatePrefs() {

View file

@ -57,6 +57,10 @@ func NewRNG(env *environment.Environment, mmap architecture.Map) RNG {
}
}
func (r *RNG) Plumb(env *environment.Environment) {
r.env = env
}
func (r *RNG) Reset() {
r.control = 0x0
}

View file

@ -170,7 +170,7 @@ func (cart *cdf) Plumb(env *environment.Environment) {
if cart.armState == nil {
panic("cannot plumb this ELF instance because the ARM state is nil")
}
cart.arm.Plumb(cart.armState, cart.state.static, cart)
cart.arm.Plumb(cart.env, cart.armState, cart.state.static, cart)
cart.armState = nil
}
@ -181,7 +181,7 @@ func (cart *cdf) PlumbFromDifferentEmulation(env *environment.Environment) {
panic("cannot plumb this ELF instance because the ARM state is nil")
}
cart.arm = arm.NewARM(cart.env, cart.version.mmap, cart.state.static, cart)
cart.arm.Plumb(cart.armState, cart.state.static, cart)
cart.arm.Plumb(cart.env, cart.armState, cart.state.static, cart)
cart.armState = nil
cart.yieldHook = &coprocessor.StubCartYieldHook{}
}

View file

@ -155,7 +155,7 @@ func (cart *dpcPlus) Plumb(env *environment.Environment) {
if cart.armState == nil {
panic("cannot plumb this ELF instance because the ARM state is nil")
}
cart.arm.Plumb(cart.armState, cart.state.static, cart)
cart.arm.Plumb(cart.env, cart.armState, cart.state.static, cart)
cart.armState = nil
}
@ -166,7 +166,7 @@ func (cart *dpcPlus) PlumbFromDifferentEmulation(env *environment.Environment) {
panic("cannot plumb this ELF instance because the ARM state is nil")
}
cart.arm = arm.NewARM(cart.env, cart.version.mmap, cart.state.static, cart)
cart.arm.Plumb(cart.armState, cart.state.static, cart)
cart.arm.Plumb(cart.env, cart.armState, cart.state.static, cart)
cart.armState = nil
cart.yieldHook = &coprocessor.StubCartYieldHook{}
}

View file

@ -192,7 +192,7 @@ func (cart *Elf) PlumbFromDifferentEmulation(env *environment.Environment) {
panic("cannot plumb this ELF instance because the ARM state is nil")
}
cart.arm = arm.NewARM(cart.env, cart.mem.model, cart.mem, cart)
cart.arm.Plumb(cart.armState, cart.mem, cart)
cart.arm.Plumb(cart.env, cart.armState, cart.mem, cart)
cart.armState = nil
cart.mem.Plumb(cart.arm)
cart.yieldHook = &coprocessor.StubCartYieldHook{}
@ -205,7 +205,7 @@ func (cart *Elf) Plumb(env *environment.Environment) {
panic("cannot plumb this ELF instance because the ARM state is nil")
}
cart.mem.Plumb(cart.arm)
cart.arm.Plumb(cart.armState, cart.mem, cart)
cart.arm.Plumb(cart.env, cart.armState, cart.mem, cart)
cart.armState = nil
}