From dcc96e913312fa7bd118a8db7efc081f5050f6d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Blanco=20Celdr=C3=A1n?= Date: Mon, 18 May 2020 18:54:39 +0200 Subject: [PATCH 01/18] Added VEGA AI name (Doom 2016) --- src/main/resources/assets/opencomputers/robot.names | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/assets/opencomputers/robot.names b/src/main/resources/assets/opencomputers/robot.names index 9b0250b1d..0e5ab13c0 100644 --- a/src/main/resources/assets/opencomputers/robot.names +++ b/src/main/resources/assets/opencomputers/robot.names @@ -120,6 +120,7 @@ Rinzler # Tron Twiki # Buck Rodgers Uniblab # The Jetsons Unimate # First programmable robot. +VEGA # Doom 2016 Vertigo # Perry Rhodan Vexatos # Contributor V.I.K.I. # Virtual Interactive Kinetic Intelligence - I, Robot From 687bf569a0d44aa64743169dd23daeb28f08cc57 Mon Sep 17 00:00:00 2001 From: payonel Date: Mon, 18 May 2020 23:36:14 -0700 Subject: [PATCH 02/18] more meaningful bitblt budget cost scaling --- .../opencomputers/loot/openos/lib/core/lua_shell.lua | 5 +---- .../li/cil/oc/server/component/GraphicsCard.scala | 10 +++++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/lua_shell.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/lua_shell.lua index 12cf51f94..ab07bd6a3 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/lua_shell.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/lua_shell.lua @@ -116,15 +116,12 @@ while term.isAvailable() do else local ok, why = pcall(function() for i = 2, result.n do - io.write(require("serialization").serialize(result[i], true) .. "\t") + io.write(require("serialization").serialize(result[i], true), i < result.n and "\t" or "\n") end end) if not ok then io.stderr:write("crashed serializing result: ", tostring(why)) end - if term.getCursor() > 1 then - io.write("\n") - end end else io.stderr:write(tostring(reason) .. "\n") diff --git a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala index e9ba824ad..eea6bc4c8 100644 --- a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala @@ -70,8 +70,12 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI final val setCosts = Array(1.0 / 64, 1.0 / 128, 1.0 / 256) final val copyCosts = Array(1.0 / 16, 1.0 / 32, 1.0 / 64) final val fillCosts = Array(1.0 / 32, 1.0 / 64, 1.0 / 128) - final val bitbltCosts = Array(32, 16, 8) - final val totalVRAM: Int = (maxResolution._1 * maxResolution._2) * Settings.get.vramSizes(0 max tier min Settings.get.vramSizes.length) + // These are dirty page bitblt budget costs + // a single bitblt can send a screen of data, which is n*set calls where set is writing an entire line + // So for each tier, we multiple the set cost with the number of lines the screen may have + // Additionally, we multiply by 4 for the packet size which is generally 4x larger than a set call + final val bitbltCosts = Array(setCosts(0) * 16 * 4, setCosts(1) * 25 * 4, setCosts(1) * 50 * 4) + final val totalVRAM: Int = (maxResolution._1 * maxResolution._2) * Settings.get.vramSizes(0 max tier min Settings.get.vramSizes.length) * 1000 // ----------------------------------------------------------------------- // @@ -208,7 +212,7 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI // rasterizing to the screen has the same cost as copy (in fact, screen-to-screen blt _is_ a copy dst match { case _: GpuTextBuffer => 0 - case _ => Settings.get.gpuCopyCost / (maxResolution._1 * maxResolution._2) + case _ => Settings.get.gpuCopyCost } } From 694a12af38fc7aaab9fb74efeac8e4ea6837ef7d Mon Sep 17 00:00:00 2001 From: payonel Date: Mon, 18 May 2020 23:41:08 -0700 Subject: [PATCH 03/18] left in debug values on accident --- src/main/scala/li/cil/oc/server/component/GraphicsCard.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala index eea6bc4c8..ee68ace9f 100644 --- a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala @@ -75,7 +75,7 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI // So for each tier, we multiple the set cost with the number of lines the screen may have // Additionally, we multiply by 4 for the packet size which is generally 4x larger than a set call final val bitbltCosts = Array(setCosts(0) * 16 * 4, setCosts(1) * 25 * 4, setCosts(1) * 50 * 4) - final val totalVRAM: Int = (maxResolution._1 * maxResolution._2) * Settings.get.vramSizes(0 max tier min Settings.get.vramSizes.length) * 1000 + final val totalVRAM: Int = (maxResolution._1 * maxResolution._2) * Settings.get.vramSizes(0 max tier min Settings.get.vramSizes.length) // ----------------------------------------------------------------------- // @@ -212,7 +212,7 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI // rasterizing to the screen has the same cost as copy (in fact, screen-to-screen blt _is_ a copy dst match { case _: GpuTextBuffer => 0 - case _ => Settings.get.gpuCopyCost + case _ => Settings.get.gpuCopyCost / 10 } } From 61780aa36ee0a13bdb29e1c0f5df6271f73a2824 Mon Sep 17 00:00:00 2001 From: payonel Date: Tue, 19 May 2020 02:04:11 -0700 Subject: [PATCH 04/18] small adjustment to bitblt perf also, temporary changes to minimum callback data for perf testing --- src/main/scala/li/cil/oc/server/component/GraphicsCard.scala | 5 ++--- src/main/scala/li/cil/oc/server/machine/Machine.scala | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala index ee68ace9f..8aab4bb70 100644 --- a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala @@ -73,8 +73,7 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI // These are dirty page bitblt budget costs // a single bitblt can send a screen of data, which is n*set calls where set is writing an entire line // So for each tier, we multiple the set cost with the number of lines the screen may have - // Additionally, we multiply by 4 for the packet size which is generally 4x larger than a set call - final val bitbltCosts = Array(setCosts(0) * 16 * 4, setCosts(1) * 25 * 4, setCosts(1) * 50 * 4) + final val bitbltCosts = Array(setCosts(0) * 16 * 1.5, setCosts(1) * 25 * 1.5, setCosts(1) * 50 * 1.5) final val totalVRAM: Int = (maxResolution._1 * maxResolution._2) * Settings.get.vramSizes(0 max tier min Settings.get.vramSizes.length) // ----------------------------------------------------------------------- // @@ -212,7 +211,7 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI // rasterizing to the screen has the same cost as copy (in fact, screen-to-screen blt _is_ a copy dst match { case _: GpuTextBuffer => 0 - case _ => Settings.get.gpuCopyCost / 10 + case _ => Settings.get.gpuCopyCost / 15 } } diff --git a/src/main/scala/li/cil/oc/server/machine/Machine.scala b/src/main/scala/li/cil/oc/server/machine/Machine.scala index 62ea1e770..3bc3c81bb 100644 --- a/src/main/scala/li/cil/oc/server/machine/Machine.scala +++ b/src/main/scala/li/cil/oc/server/machine/Machine.scala @@ -280,7 +280,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach override def consumeCallBudget(callCost: Double): Unit = { if (architecture.isInitialized && !inSynchronizedCall) { - val clampedCost = math.max(0.001, callCost) + val clampedCost = math.max(0.0, callCost) if (clampedCost > callBudget) { throw new LimitReachedException() } From 4857395e6a69d26aac6b5bc7a9c556741276fdbe Mon Sep 17 00:00:00 2001 From: payonel Date: Tue, 19 May 2020 02:04:52 -0700 Subject: [PATCH 05/18] reverting clamp for callback cost (as it was before) --- src/main/scala/li/cil/oc/server/machine/Machine.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/server/machine/Machine.scala b/src/main/scala/li/cil/oc/server/machine/Machine.scala index 3bc3c81bb..62ea1e770 100644 --- a/src/main/scala/li/cil/oc/server/machine/Machine.scala +++ b/src/main/scala/li/cil/oc/server/machine/Machine.scala @@ -280,7 +280,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach override def consumeCallBudget(callCost: Double): Unit = { if (architecture.isInitialized && !inSynchronizedCall) { - val clampedCost = math.max(0.0, callCost) + val clampedCost = math.max(0.001, callCost) if (clampedCost > callBudget) { throw new LimitReachedException() } From 09505e6cff410fb1bfd822eebb2b0f61f52024b3 Mon Sep 17 00:00:00 2001 From: payonel Date: Thu, 21 May 2020 00:36:09 -0700 Subject: [PATCH 06/18] openos improvements 1. if /home is readonly, a helpful message is displayed tell the user to run install 2. remove -i from `cp` alias because a bunch of people complain about it 3. `install` now does not clobber /etc/rc.cfg nor /home/.shrc --- .../assets/opencomputers/loot/openos/.prop | 2 +- .../opencomputers/loot/openos/bin/cp.lua | 2 +- .../opencomputers/loot/openos/bin/install.lua | 26 ++--- .../opencomputers/loot/openos/bin/mv.lua | 2 +- .../assets/opencomputers/loot/openos/etc/motd | 4 + .../opencomputers/loot/openos/etc/profile.lua | 1 - .../loot/openos/lib/core/install_basics.lua | 103 +++++++++++------- .../loot/openos/lib/tools/transfer.lua | 11 +- 8 files changed, 91 insertions(+), 60 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/openos/.prop b/src/main/resources/assets/opencomputers/loot/openos/.prop index 5cf0ed07b..3425482c1 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/.prop +++ b/src/main/resources/assets/opencomputers/loot/openos/.prop @@ -1 +1 @@ -{label = "OpenOS", reboot=true, setlabel=true, setboot=true} +{label = "OpenOS", reboot=true, setlabel=true, setboot=true, noclobber={"etc/rc.cfg","home/.shrc"}} diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/cp.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/cp.lua index d173e219a..3341709a6 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/cp.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/cp.lua @@ -30,7 +30,7 @@ options = P = options.P, v = options.v, x = options.x, - skip = options.skip, + skip = {options.skip}, } return transfer.batch(args, options) diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/install.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/install.lua index f8410e98b..d3a39b476 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/install.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/install.lua @@ -1,6 +1,4 @@ local computer = require("computer") -local shell = require("shell") - local options do @@ -12,21 +10,23 @@ do options = basic(...) end -if not options then return end +if not options then + return +end if computer.freeMemory() < 50000 then print("Low memory, collecting garbage") - for i=1,20 do os.sleep(0) end + for i = 1, 20 do + os.sleep(0) + end end -local cp, reason = loadfile(shell.resolve("cp", "lua"), "bt", _G) -assert(cp, reason) - -local ok, ec = pcall(cp, table.unpack(options.cp_args)) -assert(ok, ec) - -if ec ~= nil and ec ~= 0 then - return ec +local transfer = require("tools/transfer") +for _, inst in ipairs(options.cp_args) do + local ec = transfer.batch(table.unpack(inst)) + if ec ~= nil and ec ~= 0 then + return ec + end end print("Installation complete!") @@ -44,7 +44,7 @@ end if options.reboot then io.write("Reboot now? [Y/n] ") - if ((io.read() or "n").."y"):match("^%s*[Yy]") then + if ((io.read() or "n") .. "y"):match("^%s*[Yy]") then print("\nRebooting now!\n") computer.shutdown(true) end diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/mv.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/mv.lua index 9567d6f64..d5f0b4796 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/mv.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/mv.lua @@ -24,7 +24,7 @@ options = i = options.i, v = options.v, n = options.n, -- no clobber - skip = options.skip, + skip = {options.skip}, P = true, -- move operations always preserve r = true, -- move is allowed to move entire dirs x = true, -- cannot move mount points diff --git a/src/main/resources/assets/opencomputers/loot/openos/etc/motd b/src/main/resources/assets/opencomputers/loot/openos/etc/motd index 0c73a8250..2d07a9b1b 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/etc/motd +++ b/src/main/resources/assets/opencomputers/loot/openos/etc/motd @@ -32,3 +32,7 @@ for _,line in ipairs(lines) do io.write(borders[2][1], " ", line, (" "):rep(maxLine - #line + 1), borders[2][3], " \n") end io.write(borders[3][1] .. string.rep(borders[3][2], maxLine + 2) .. borders[3][3] .. "\n") + +if require("filesystem").get("home").isReadOnly() then + io.write("\27[33mNote: Your home directory is readonly. Run `install` and reboot.\27[m\n") +end diff --git a/src/main/resources/assets/opencomputers/loot/openos/etc/profile.lua b/src/main/resources/assets/opencomputers/loot/openos/etc/profile.lua index 52e054734..6c54cb4d1 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/etc/profile.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/etc/profile.lua @@ -20,7 +20,6 @@ shell.setAlias("cls", "clear") shell.setAlias("rs", "redstone") shell.setAlias("view", "edit -r") shell.setAlias("help", "man") -shell.setAlias("cp", "cp -i") shell.setAlias("l", "ls -lhp") shell.setAlias("..", "cd ..") shell.setAlias("df", "df -h") diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/install_basics.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/install_basics.lua index f139eeb3b..41b72d1cf 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/install_basics.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/install_basics.lua @@ -27,7 +27,7 @@ local utils local rootfs = fs.get("/") if not rootfs then - io.stderr:write("no root filesystem, aborting\n"); + io.stderr:write("no root filesystem, aborting\n") os.exit(1) end @@ -86,11 +86,13 @@ for dev, path in pairs(devices) do io.stderr:write("Cannot install to " .. options.to .. ", it is read only\n") os.exit(1) end - elseif specified or - not (source_filter and address:find(source_filter, 1, true) == 1) and -- specified for source - not target_filter and - address ~= tmpAddress then - table.insert(targets, {dev=dev, path=install_path, specified=specified}) + elseif + specified or + not (source_filter and address:find(source_filter, 1, true) == 1) and -- specified for source + not target_filter and + address ~= tmpAddress + then + table.insert(targets, {dev = dev, path = install_path, specified = specified}) end end @@ -105,11 +107,11 @@ for dev, path in pairs(devices) do local install_path = dev == source_filter_dev and options.from or path local specified = source_filter and address:find(source_filter, 1, true) == 1 - if fs.list(install_path)() - and (specified or - not source_filter and - address ~= tmpAddress and - not (address == rootfs.address and not rootfs.isReadOnly())) then + if + fs.list(install_path)() and + (specified or + not source_filter and address ~= tmpAddress and not (address == rootfs.address and not rootfs.isReadOnly())) + then local prop = {} local prop_path = path .. "/.prop" local prop_file = fs.open(prop_path) @@ -125,7 +127,7 @@ for dev, path in pairs(devices) do end if not prop.ignore then if not label or label:lower() == (prop.label or dev.getLabel() or ""):lower() then - table.insert(sources, {dev=dev, path=install_path, prop=prop, specified=specified}) + table.insert(sources, {dev = dev, path = install_path, prop = prop, specified = specified}) end end end @@ -137,23 +139,24 @@ if #sources ~= 1 then utils = loadfile(utils_path, "bt", _G) source = utils("select", "sources", options, sources) end -if not source then return end +if not source then + return +end -options = -{ - from = source.path .. '/', - fromDir = fs.canonical(options.fromDir or source.prop.fromDir or ""), - root = fs.canonical(options.root or options.toDir or source.prop.root or ""), - update = options.update or options.u, - label = source.prop.label or label, +options = { + from = source.path .. "/", + fromDir = fs.canonical(options.fromDir or source.prop.fromDir or ""), + root = fs.canonical(options.root or options.toDir or source.prop.root or ""), + update = options.update or options.u, + label = source.prop.label or label, setlabel = not (options.nosetlabel or options.nolabelset) and source.prop.setlabel, - setboot = not (options.nosetboot or options.noboot) and source.prop.setboot, - reboot = not options.noreboot and source.prop.reboot, + setboot = not (options.nosetboot or options.noboot) and source.prop.setboot, + reboot = not options.noreboot and source.prop.reboot } local source_display = options.label or source.dev.getLabel() or source.path -- Remove the source from the target options -for index,entry in ipairs(targets) do +for index, entry in ipairs(targets) do if entry.dev == source.dev then table.remove(targets, index) target = targets[1] @@ -162,47 +165,67 @@ end -- Ask the user to select a target if #targets ~= 1 then - if #sources == 1 then - io.write(source_display, " selected for install\n") - end + if #sources == 1 then + io.write(source_display, " selected for install\n") + end - utils = utils or loadfile(utils_path, "bt", _G) + utils = utils or loadfile(utils_path, "bt", _G) target = utils("select", "targets", options, targets) end -if not target then return end +if not target then + return +end -options.to = target.path .. '/' +options.to = target.path .. "/" -local cp_args = -{ - "-vrx" .. (options.update and "ui" or ""), - "--skip=.prop", - fs.concat(options.from, options.fromDir) .. "/.", - fs.concat(options.to , options.root) +local function resolveFrom(path) + return fs.concat(options.from, options.fromDir) .. "/" .. path +end + +local fullTargetPath = fs.concat(options.to, options.root) +local transfer_args = { + { + {resolveFrom("."), fullTargetPath}, + { + cmd = "cp", + r = true, v = true, x = true, u = options.update, i = options.update, + skip = {resolveFrom(".prop")}, + } + } } +if source.prop.noclobber and #source.prop.noclobber > 0 then + local noclobber_opts = {cmd = "cp", v = true, n = true} + for _, noclobber in ipairs(source.prop.noclobber or {}) do + local noclobberFrom = resolveFrom(noclobber) + local noclobberTo = fs.concat(fullTargetPath, noclobber) + table.insert(transfer_args[1][2].skip, noclobberFrom) + table.insert(transfer_args, {{noclobberFrom, noclobberTo}, noclobber_opts}) + end +end + local special_target = "" if #targets > 1 or target_filter or source_filter then - special_target = " to " .. cp_args[4] + special_target = " to " .. transfer_args[1].args[2] end io.write("Install " .. source_display .. special_target .. "? [Y/n] ") -if not ((io.read() or "n").."y"):match("^%s*[Yy]") then +if not ((io.read() or "n") .. "y"):match("^%s*[Yy]") then io.write("Installation cancelled\n") os.exit() end local installer_path = options.from .. "/.install" if fs.exists(installer_path) then - local installer, reason = loadfile(installer_path, "bt", setmetatable({install=options}, {__index = _G})) + local installer, reason = loadfile(installer_path, "bt", setmetatable({install = options}, {__index = _G})) if not installer then - io.stderr:write("installer failed to load: " .. tostring(reason) .. '\n') + io.stderr:write("installer failed to load: " .. tostring(reason) .. "\n") os.exit(1) end os.exit(installer()) end -options.cp_args = cp_args +options.cp_args = transfer_args options.target = target return options diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/transfer.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/transfer.lua index 3e3f4b725..ec847252f 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/transfer.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/transfer.lua @@ -93,7 +93,7 @@ function lib.recurse(fromPath, toPath, options, origin, top) local toPathFull = shell.resolve(toPath) local mv = options.cmd == "mv" local verbose = options.v and (not mv or top) - if select(2, fromPathFull:find(options.skip)) == #fromPathFull then + if options.skip[fromPathFull] then status(verbose, string.format("skipping %s", fromPath)) return true end @@ -221,8 +221,13 @@ function lib.batch(args, options) -- standardized options options.i = options.i and not options.f options.P = options.P or options.r - options.skip = text.escapeMagic(options.skip or "") - + + local skips = options.skip or {} + options.skip = {} + for _, skip_item in ipairs(skips) do + options.skip[shell.resolve(skip_item)] = true + end + local origin = {} for dev,path in fs.mounts() do origin[path] = dev From a4fbcd975bc3d82f41e59ff16548523b3e32524c Mon Sep 17 00:00:00 2001 From: payonel Date: Thu, 21 May 2020 00:44:43 -0700 Subject: [PATCH 07/18] significant change, and carefully tested - read/writes to vram is now free, no budget cost This change required removing our minimum budget cost (the cost for a component invoke) which was .001 (budget limits are ~1) This affects a number of cheap component api calls, so that they no longer have a .001 minimum call budget. From tesing it appears that tight loops caling these api don't experience a different behavior, they still fail with "too long without yielding" just the same. Additionally, these api are safe to call without forcing a budget cost. This should have a small quality of life improvement in many places, being able to call some api ever so slightly faster. also, added better config options for vram settings --- src/main/resources/application.conf | 33 ++++++++++----- src/main/scala/li/cil/oc/Settings.scala | 35 ++++++++++------ .../oc/server/component/GraphicsCard.scala | 41 +++++++++++-------- .../li/cil/oc/server/machine/Machine.scala | 2 +- 4 files changed, 69 insertions(+), 42 deletions(-) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 6a0337422..768405939 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -244,16 +244,6 @@ opencomputers { 1024 ] - # Video ram can be allocated on a gpu. The amount of vram you can allocate - # is equal to the width*height of the max resolution of the gpu multiplied - # by the "vramSize" for that tier. For example, a T2 gpu can have 80*25*2 of - # text buffer space allocated - vramSizes: [ - 1, - 2, - 3 - ] - # This setting allows you to fine-tune how RAM sizes are scaled internally # on 64 Bit machines (i.e. when the Minecraft server runs in a 64 Bit VM). # Why is this even necessary? Because objects consume more memory in a 64 @@ -1596,4 +1586,27 @@ opencomputers { # whitelist and the blacklist, the blacklist will win. dimWhitelist: [] } + + # Graphics Card Component Settings + gpu { + # Video ram can be allocated on a gpu. The amount of vram you can allocate + # is equal to the width*height of the max resolution of the gpu multiplied + # by the "vramSize" for that tier. For example, a T2 gpu can have 80*25*2 of + # text buffer space allocated + vramSizes: [ + 1, + 2, + 3 + ] + + # This setting assigns the budget call cost to invoke bitblt to write vram + # to a screen. Video ram can bitblit to a screen which can cause real life + # network laod the defaults settings put bitblit network impact close to gpu.set + # Increase these values to throttle bitblt more. + bitbltCosts: [ + 0.375, # (1/64) * 16 * 1.5 + 0.29296875, # (1/128) * 25 * 1.5 + 0.29296875, # (1/256) * 50 * 1.5 + ] + } } diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index 51742fbbd..6d1e643b6 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -93,12 +93,6 @@ class Settings(val config: Config) { OpenComputers.log.warn("Bad number of RAM sizes, ignoring.") Array(192, 256, 384, 512, 768, 1024) } - val vramSizes = Array(config.getIntList("computer.lua.vramSizes"): _*) match { - case Array(tier1, tier2, tier3) => Array(tier1: Int, tier2: Int, tier3: Int) - case _ => - OpenComputers.log.warn("Bad number of VRAM sizes, ignoring.") - Array(1, 2, 3) - } val ramScaleFor64Bit = config.getDouble("computer.lua.ramScaleFor64Bit") max 1 val maxTotalRam = config.getInt("computer.lua.maxTotalRam") max 0 @@ -473,24 +467,39 @@ class Settings(val config: Config) { // >= 1.7.4 val maxSignalQueueSize: Int = (if (config.hasPath("computer.maxSignalQueueSize")) config.getInt("computer.maxSignalQueueSize") else 256) min 256 + + // >= 1.7.6 + val vramSizes: Array[Double] = Array(config.getDoubleList("gpu.vramSizes"): _*) match { + case Array(tier1, tier2, tier3) => Array(tier1: Double, tier2: Double, tier3: Double) + case _ => + OpenComputers.log.warn("Bad number of VRAM sizes (expected 3), ignoring.") + Array(1, 2, 3) + } + + val bitbltCosts: Array[Double] = Array(config.getDoubleList("gpu.bitbltCosts"): _*) match { + case Array(tier1, tier2, tier3) => Array(tier1: Double, tier2: Double, tier3: Double) + case _ => + OpenComputers.log.warn("Bad number of bitblit costs (expected 3), ignoring.") + Array((1/64.0) * 16 * 1.5, (1/128.0) * 25 * 1.5, (1/256.0) * 50 * 1.5) + } } object Settings { val resourceDomain = "opencomputers" val namespace = "oc:" val savePath = "opencomputers/" - val scriptPath = "/assets/" + resourceDomain + "/lua/" - val screenResolutionsByTier = Array((50, 16), (80, 25), (160, 50)) - val screenDepthsByTier = Array(api.internal.TextBuffer.ColorDepth.OneBit, api.internal.TextBuffer.ColorDepth.FourBit, api.internal.TextBuffer.ColorDepth.EightBit) - val deviceComplexityByTier = Array(12, 24, 32, 9001) + val scriptPath: String = "/assets/" + resourceDomain + "/lua/" + val screenResolutionsByTier: Array[(Int, Int)] = Array((50, 16), (80, 25), (160, 50)) + val screenDepthsByTier: Array[api.internal.TextBuffer.ColorDepth] = Array(api.internal.TextBuffer.ColorDepth.OneBit, api.internal.TextBuffer.ColorDepth.FourBit, api.internal.TextBuffer.ColorDepth.EightBit) + val deviceComplexityByTier: Array[Int] = Array(12, 24, 32, 9001) var rTreeDebugRenderer = false - var blockRenderId = -1 + var blockRenderId: Int = -1 - def basicScreenPixels = screenResolutionsByTier(0)._1 * screenResolutionsByTier(0)._2 + def basicScreenPixels: Int = screenResolutionsByTier(0)._1 * screenResolutionsByTier(0)._2 private var settings: Settings = _ - def get = settings + def get: Settings = settings def load(file: File) = { import scala.compat.Platform.EOL diff --git a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala index 8aab4bb70..b9b135617 100644 --- a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala @@ -73,8 +73,8 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI // These are dirty page bitblt budget costs // a single bitblt can send a screen of data, which is n*set calls where set is writing an entire line // So for each tier, we multiple the set cost with the number of lines the screen may have - final val bitbltCosts = Array(setCosts(0) * 16 * 1.5, setCosts(1) * 25 * 1.5, setCosts(1) * 50 * 1.5) - final val totalVRAM: Int = (maxResolution._1 * maxResolution._2) * Settings.get.vramSizes(0 max tier min Settings.get.vramSizes.length) + final val bitbltCost: Double = Settings.get.bitbltCosts(0 max tier min 2) + final val totalVRAM: Double = (maxResolution._1 * maxResolution._2) * Settings.get.vramSizes(0 max tier min 2) // ----------------------------------------------------------------------- // @@ -98,12 +98,12 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI // ----------------------------------------------------------------------- // - private def consumeViewportPower(buffer: api.internal.TextBuffer, context: Context, budgetCost: Double, units: Int, factor: Double): Boolean = { - buffer match { - case _: component.GpuTextBuffer => true - case _ => + private def resolveInvokeCosts(idx: Int, context: Context, budgetCost: Double, units: Int, factor: Double): Boolean = { + idx match { + case RESERVED_SCREEN_INDEX => context.consumeCallBudget(budgetCost) consumePower(units, factor) + case _ => true } } @@ -200,7 +200,7 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI case _: GpuTextBuffer => 0.0 // no cost to write to ram case _ => // screen target will need the new buffer // small buffers are cheap, so increase with size of buffer source - bitbltCosts(tier) * (src.getWidth * src.getHeight) / (maxResolution._1 * maxResolution._2) + bitbltCost * (src.getWidth * src.getHeight) / (maxResolution._1 * maxResolution._2) } case _ => 0.0 // from screen or from clean buffer is free } @@ -231,7 +231,7 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI val budgetCost: Double = determineBitbltBudgetCost(dst, src) val energyCost: Double = determineBitbltEnergyCost(dst) - if (consumeViewportPower(dst, context, budgetCost, w * h, energyCost)) { + if (resolveInvokeCosts(dstIdx, context, budgetCost, w * h, energyCost)) { if (dstIdx == srcIdx) { val tx = col - fromCol val ty = row - fromRow @@ -282,7 +282,7 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI if (bufferIndex == RESERVED_SCREEN_INDEX) { screen(s => result(s.node.address)) } else { - result(Unit, "the current text buffer is video ram") + result(Unit, "the current device is video ram") } } @@ -292,8 +292,10 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI @Callback(direct = true, doc = """function(value:number[, palette:boolean]):number, number or nil -- Sets the background color to the specified value. Optionally takes an explicit palette index. Returns the old value and if it was from the palette its palette index.""") def setBackground(context: Context, args: Arguments): Array[AnyRef] = { - context.consumeCallBudget(setBackgroundCosts(tier)) val color = args.checkInteger(0) + if (bufferIndex == RESERVED_SCREEN_INDEX) { + context.consumeCallBudget(setBackgroundCosts(tier)) + } screen(s => { val oldValue = s.getBackgroundColor val (oldColor, oldIndex) = @@ -314,8 +316,10 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI @Callback(direct = true, doc = """function(value:number[, palette:boolean]):number, number or nil -- Sets the foreground color to the specified value. Optionally takes an explicit palette index. Returns the old value and if it was from the palette its palette index.""") def setForeground(context: Context, args: Arguments): Array[AnyRef] = { - context.consumeCallBudget(setForegroundCosts(tier)) val color = args.checkInteger(0) + if (bufferIndex == RESERVED_SCREEN_INDEX) { + context.consumeCallBudget(setForegroundCosts(tier)) + } screen(s => { val oldValue = s.getForegroundColor val (oldColor, oldIndex) = @@ -340,10 +344,12 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI @Callback(direct = true, doc = """function(index:number, color:number):number -- Set the palette color at the specified palette index. Returns the previous value.""") def setPaletteColor(context: Context, args: Arguments): Array[AnyRef] = { - context.consumeCallBudget(setPaletteColorCosts(tier)) val index = args.checkInteger(0) val color = args.checkInteger(1) - context.pause(0.1) + if (bufferIndex == RESERVED_SCREEN_INDEX) { + context.consumeCallBudget(setPaletteColorCosts(tier)) + context.pause(0.1) + } screen(s => try { val oldColor = s.getPaletteColor(index) s.setPaletteColor(index, color) @@ -457,11 +463,10 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI val vertical = args.optBoolean(3, false) screen(s => { - if (consumeViewportPower(s, context, setCosts(tier), value.length, Settings.get.gpuSetCost)) { + if (resolveInvokeCosts(bufferIndex, context, setCosts(tier), value.length, Settings.get.gpuSetCost)) { s.set(x, y, value, vertical) result(true) - } - else result(Unit, "not enough energy") + } else result(Unit, "not enough energy") }) } @@ -474,7 +479,7 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI val tx = args.checkInteger(4) val ty = args.checkInteger(5) screen(s => { - if (consumeViewportPower(s, context, copyCosts(tier), w * h, Settings.get.gpuCopyCost)) { + if (resolveInvokeCosts(bufferIndex, context, copyCosts(tier), w * h, Settings.get.gpuCopyCost)) { s.copy(x, y, w, h, tx, ty) result(true) } @@ -492,7 +497,7 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI if (value.length == 1) screen(s => { val c = value.charAt(0) val cost = if (c == ' ') Settings.get.gpuClearCost else Settings.get.gpuFillCost - if (consumeViewportPower(s, context, fillCosts(tier), w * h, cost)) { + if (resolveInvokeCosts(bufferIndex, context, fillCosts(tier), w * h, cost)) { s.fill(x, y, w, h, value.charAt(0)) result(true) } diff --git a/src/main/scala/li/cil/oc/server/machine/Machine.scala b/src/main/scala/li/cil/oc/server/machine/Machine.scala index 62ea1e770..3bc3c81bb 100644 --- a/src/main/scala/li/cil/oc/server/machine/Machine.scala +++ b/src/main/scala/li/cil/oc/server/machine/Machine.scala @@ -280,7 +280,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach override def consumeCallBudget(callCost: Double): Unit = { if (architecture.isInitialized && !inSynchronizedCall) { - val clampedCost = math.max(0.001, callCost) + val clampedCost = math.max(0.0, callCost) if (clampedCost > callBudget) { throw new LimitReachedException() } From c8fce521d78088a51ab388ab2260b7d9a6eb02b0 Mon Sep 17 00:00:00 2001 From: payonel Date: Thu, 21 May 2020 02:41:22 -0700 Subject: [PATCH 08/18] gpu vram api return change - freeAllBuffers returns the number of buffers freed increase timeouts on bitblt after further server load and network load testing --- src/main/resources/application.conf | 6 +++--- .../oc/common/component/traits/VideoRamAware.scala | 14 ++++++++------ .../li/cil/oc/server/component/GraphicsCard.scala | 4 ++-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 768405939..1a2add46a 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -1604,9 +1604,9 @@ opencomputers { # network laod the defaults settings put bitblit network impact close to gpu.set # Increase these values to throttle bitblt more. bitbltCosts: [ - 0.375, # (1/64) * 16 * 1.5 - 0.29296875, # (1/128) * 25 * 1.5 - 0.29296875, # (1/256) * 50 * 1.5 + 2, # (1/64) * 16 * 8 + 1.5625, # (1/128) * 25 * 8 + 1.5625, # (1/256) * 50 * 8 ] } } diff --git a/src/main/scala/li/cil/oc/common/component/traits/VideoRamAware.scala b/src/main/scala/li/cil/oc/common/component/traits/VideoRamAware.scala index 55b30b2fe..6a1fe0764 100644 --- a/src/main/scala/li/cil/oc/common/component/traits/VideoRamAware.scala +++ b/src/main/scala/li/cil/oc/common/component/traits/VideoRamAware.scala @@ -1,6 +1,7 @@ package li.cil.oc.common.component.traits import li.cil.oc.common.component +import li.cil.oc.common.component.GpuTextBuffer import net.minecraft.nbt.NBTTagCompound trait VideoRamAware { @@ -25,19 +26,20 @@ trait VideoRamAware { preexists } - def removeBuffers(ids: Array[Int]): Boolean = { - var allRemoved: Boolean = true + def removeBuffers(ids: Array[Int]): Int = { + var count = 0 if (ids.nonEmpty) { onBufferRamDestroy(ids) for (id <- ids) { - if (internalBuffers.remove(id).isEmpty) - allRemoved = false + if (internalBuffers.remove(id).nonEmpty) { + count += 1 + } } } - allRemoved + count } - def removeAllBuffers(): Boolean = removeBuffers(bufferIndexes()) + def removeAllBuffers(): Int = removeBuffers(bufferIndexes()) def loadBuffer(id: Int, nbt: NBTTagCompound): Unit = { val src = new li.cil.oc.util.TextBuffer(width = 1, height = 1, li.cil.oc.util.PackedColor.SingleBitFormat) diff --git a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala index b9b135617..67e819046 100644 --- a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala @@ -169,11 +169,11 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI @Callback(direct = true, doc = """function(index: number): boolean -- Closes buffer at `index`. Returns true if a buffer closed. If the current buffer is closed, index moves to 0""") def freeBuffer(context: Context, args: Arguments): Array[AnyRef] = { val index: Int = args.optInteger(0, bufferIndex) - if (removeBuffers(Array(index))) result(true) + if (removeBuffers(Array(index)) == 1) result(true) else result(Unit, "no buffer at index") } - @Callback(direct = true, doc = """function(): number -- Closes all buffers and returns true on success. If the active buffer is closed, index moves to 0""") + @Callback(direct = true, doc = """function(): number -- Closes all buffers and returns the count. If the active buffer is closed, index moves to 0""") def freeAllBuffers(context: Context, args: Arguments): Array[AnyRef] = result(removeAllBuffers()) @Callback(direct = true, doc = """function(): number -- returns the total memory size of the gpu vram. This does not include the screen.""") From 4e6d1f930bdbfb635a33e8acdc53b7ce7031e888 Mon Sep 17 00:00:00 2001 From: payonel Date: Thu, 21 May 2020 23:47:10 -0700 Subject: [PATCH 09/18] added budget costs for bitblt that allow a dynamic indirect callback behavior, while being direct by default this also increases the budget cost for bitblt for dirty pages. this new calibration is more server friendly --- src/main/resources/application.conf | 15 +++-------- .../oc/server/component/GraphicsCard.scala | 25 ++++++++++++++++--- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 1a2add46a..300782102 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -1593,20 +1593,13 @@ opencomputers { # is equal to the width*height of the max resolution of the gpu multiplied # by the "vramSize" for that tier. For example, a T2 gpu can have 80*25*2 of # text buffer space allocated - vramSizes: [ - 1, - 2, - 3 - ] + vramSizes: [ 1, 2, 3 ] # This setting assigns the budget call cost to invoke bitblt to write vram # to a screen. Video ram can bitblit to a screen which can cause real life # network laod the defaults settings put bitblit network impact close to gpu.set - # Increase these values to throttle bitblt more. - bitbltCosts: [ - 2, # (1/64) * 16 * 8 - 1.5625, # (1/128) * 25 * 8 - 1.5625, # (1/256) * 50 * 8 - ] + # Increase these values to throttle bitblt more. These values appear inverted + # but the cost of bitblt is based on the max resolution of the device + bitbltCosts: [ 1, 2, 8 ] } } diff --git a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala index 67e819046..a2ade576c 100644 --- a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala @@ -7,9 +7,7 @@ import li.cil.oc.api.Network import li.cil.oc.api.driver.DeviceInfo import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute import li.cil.oc.api.driver.DeviceInfo.DeviceClass -import li.cil.oc.api.machine.Arguments -import li.cil.oc.api.machine.Callback -import li.cil.oc.api.machine.Context +import li.cil.oc.api.machine.{Arguments, Callback, Context, LimitReachedException} import li.cil.oc.api.network._ import li.cil.oc.api.prefab import li.cil.oc.util.PackedColor @@ -76,6 +74,8 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI final val bitbltCost: Double = Settings.get.bitbltCosts(0 max tier min 2) final val totalVRAM: Double = (maxResolution._1 * maxResolution._2) * Settings.get.vramSizes(0 max tier min 2) + var budgetExhausted: Boolean = false // for especially expensive calls, bitblt + // ----------------------------------------------------------------------- // private final lazy val deviceInfo = Map( @@ -228,8 +228,25 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI val fromCol = args.optInteger(6, 1) val fromRow = args.optInteger(7, 1) - val budgetCost: Double = determineBitbltBudgetCost(dst, src) + var budgetCost: Double = determineBitbltBudgetCost(dst, src) val energyCost: Double = determineBitbltEnergyCost(dst) + val tierCredit: Double = ((tier + 1) * .5) + val overBudget: Double = budgetCost - tierCredit + + if (overBudget > 0) { + if (budgetExhausted) { // we've thrown once before + if (overBudget > tierCredit) { // we need even more pause than just a single tierCredit + val pauseNeeded = overBudget - tierCredit + val seconds: Double = (pauseNeeded / tierCredit) / 20 + context.pause(seconds) + } + budgetCost = 0 // remove the rest of the budget cost at this point + } else { + budgetExhausted = true + throw new LimitReachedException() + } + } + budgetExhausted = false if (resolveInvokeCosts(dstIdx, context, budgetCost, w * h, energyCost)) { if (dstIdx == srcIdx) { From a879a181f271ac464f71005f92ced7e97cb57d93 Mon Sep 17 00:00:00 2001 From: payonel Date: Fri, 22 May 2020 01:54:05 -0700 Subject: [PATCH 10/18] update settings.scala to match defaults in application.conf --- src/main/scala/li/cil/oc/Settings.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index 6d1e643b6..0a266ce65 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -480,7 +480,7 @@ class Settings(val config: Config) { case Array(tier1, tier2, tier3) => Array(tier1: Double, tier2: Double, tier3: Double) case _ => OpenComputers.log.warn("Bad number of bitblit costs (expected 3), ignoring.") - Array((1/64.0) * 16 * 1.5, (1/128.0) * 25 * 1.5, (1/256.0) * 50 * 1.5) + Array(1, 2, 8) } } From 13d19c1bb8ab6007dfc780a2b169da602888c9cd Mon Sep 17 00:00:00 2001 From: payonel Date: Fri, 22 May 2020 10:12:05 -0700 Subject: [PATCH 11/18] insure minimal bitblt cost when writing to screen --- .../scala/li/cil/oc/server/component/GraphicsCard.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala index a2ade576c..1e7c0c18a 100644 --- a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala @@ -31,7 +31,7 @@ import scala.util.matching.Regex // the save file - a Bad Thing (TM). class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceInfo with component.traits.VideoRamAware { - override val node = Network.newNode(this, Visibility.Neighbors). + override val node: Connector = Network.newNode(this, Visibility.Neighbors). withComponent("gpu"). withConnector(). create() @@ -196,13 +196,14 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI // large dirty buffers need throttling so their budget cost is more // clean buffers have no budget cost. src match { - case page: GpuTextBuffer if page.dirty => dst match { + case page: GpuTextBuffer => dst match { case _: GpuTextBuffer => 0.0 // no cost to write to ram - case _ => // screen target will need the new buffer + case _ if page.dirty => // screen target will need the new buffer // small buffers are cheap, so increase with size of buffer source bitbltCost * (src.getWidth * src.getHeight) / (maxResolution._1 * maxResolution._2) + case _ => .001 // bitblt a clean page to screen has a minimal cost } - case _ => 0.0 // from screen or from clean buffer is free + case _ => 0.0 // from screen is free } } From 9a61dd94f61c373b708152c27b458cfc089fbeaf Mon Sep 17 00:00:00 2001 From: payonel Date: Fri, 22 May 2020 23:23:53 -0700 Subject: [PATCH 12/18] add tooltip to trading upgrade. diable rack "relay mode" by default when crafting a new rack add better descriptive text to debug card connectToBlock --- .../assets/opencomputers/lang/en_US.lang | 1 + .../scala/li/cil/oc/common/init/Items.scala | 2 + .../cil/oc/common/item/UpgradeTrading.scala | 4 +- .../li/cil/oc/common/tileentity/Rack.scala | 2 +- .../cil/oc/server/component/DebugCard.scala | 2 +- .../li/cil/oc/server/component/Trade.scala | 37 +++++++++++-------- 6 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang index c96fa6c86..849ba858f 100644 --- a/src/main/resources/assets/opencomputers/lang/en_US.lang +++ b/src/main/resources/assets/opencomputers/lang/en_US.lang @@ -388,6 +388,7 @@ oc:tooltip.UpgradeSolarGenerator=Can be used to generate energy from sunlight on oc:tooltip.UpgradeTank=This upgrade provides a tank for fluid storage for robots and drones. Without one of these, they will not be able to store fluids internally. oc:tooltip.UpgradeTankController=This upgrade allows robots and drones more control in how they interacts with external tanks, and allows them to transfer fluids into and out of fluid tank items in their inventory. oc:tooltip.UpgradeTractorBeam=Equips a device with extremely advanced technology, nicknamed the "Item Magnet". Allows the device to pick up items anywhere within 3 blocks of its location. +oc:tooltip.UpgradeTrading=Allows robots and drones to trade with villagers. oc:tooltip.Waypoint=Provides a point of reference to devices with a navigation upgrade. oc:tooltip.WirelessNetworkCard=Allows wireless sending of network messages in addition to normal ones. You can adjust the §fsignal strength§7 to control how far messages are sent. Higher signal strength results in higher energy consumption. oc:tooltip.WorldSensorCard=Allows reading out information about the world, such as its gravity and whether it has a breathable atmosphere. Use results at own risk. The manufacturer takes no responsibility for bodily or material harm caused by decisions made upon the cards' outputs. We have lawyers. And money. Don't even try. diff --git a/src/main/scala/li/cil/oc/common/init/Items.scala b/src/main/scala/li/cil/oc/common/init/Items.scala index 4960b8bf6..2acdc2e1b 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -247,6 +247,8 @@ object Items extends ItemAPI { safeGetStack(Constants.ItemName.TankControllerUpgrade), safeGetStack(Constants.ItemName.CraftingUpgrade), safeGetStack(Constants.ItemName.HoverUpgradeTier2), + safeGetStack(Constants.ItemName.TradingUpgrade), + safeGetStack(Constants.ItemName.ExperienceUpgrade), safeGetStack(Constants.ItemName.GraphicsCardTier3), safeGetStack(Constants.ItemName.RedstoneCardTier2), diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeTrading.scala b/src/main/scala/li/cil/oc/common/item/UpgradeTrading.scala index 1cb2b27ab..30806741d 100644 --- a/src/main/scala/li/cil/oc/common/item/UpgradeTrading.scala +++ b/src/main/scala/li/cil/oc/common/item/UpgradeTrading.scala @@ -1,3 +1,5 @@ package li.cil.oc.common.item -class UpgradeTrading(val parent: Delegator) extends traits.Delegate with traits.ItemTier \ No newline at end of file +class UpgradeTrading(val parent: Delegator) extends traits.Delegate with traits.ItemTier { + override protected def tooltipName: Option[String] = Option(super.unlocalizedName) +} diff --git a/src/main/scala/li/cil/oc/common/tileentity/Rack.scala b/src/main/scala/li/cil/oc/common/tileentity/Rack.scala index 7658d23a3..e9a16327d 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Rack.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Rack.scala @@ -39,7 +39,7 @@ import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalancer with traits.ComponentInventory with traits.Rotatable with traits.BundledRedstoneAware with traits.AbstractBusAware with Analyzable with internal.Rack with traits.StateAware { - var isRelayEnabled = true + var isRelayEnabled = false val lastData = new Array[NBTTagCompound](getSizeInventory) val hasChanged = Array.fill(getSizeInventory)(true) diff --git a/src/main/scala/li/cil/oc/server/component/DebugCard.scala b/src/main/scala/li/cil/oc/server/component/DebugCard.scala index fb1a1e97b..e35d867d1 100644 --- a/src/main/scala/li/cil/oc/server/component/DebugCard.scala +++ b/src/main/scala/li/cil/oc/server/component/DebugCard.scala @@ -201,7 +201,7 @@ class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment with De } } - @Callback(doc = """function(x:number, y:number, z:number):boolean -- Connect the debug card to the block at the specified coordinates.""") + @Callback(doc = """function(x:number, y:number, z:number):boolean -- Add a component block at the specified coordinates to the computer network.""") def connectToBlock(context: Context, args: Arguments): Array[AnyRef] = { checkAccess() val x = args.checkInteger(0) diff --git a/src/main/scala/li/cil/oc/server/component/Trade.scala b/src/main/scala/li/cil/oc/server/component/Trade.scala index 6b07ffd3d..1c0b8a579 100644 --- a/src/main/scala/li/cil/oc/server/component/Trade.scala +++ b/src/main/scala/li/cil/oc/server/component/Trade.scala @@ -102,27 +102,32 @@ class Trade(val info: TradeInfo) extends AbstractValue { def completeTrade(inventory: IInventory, recipe: MerchantRecipe, exact: Boolean) : Boolean = { // Now we'll check if we have enough items to perform the trade, caching first - val firstInputStack = recipe.getItemToBuy - val secondInputStack = if (recipe.hasSecondItemToBuy) Option(recipe.getSecondItemToBuy) else None + info.merchant.get match { + case Some(merchant) => { + val firstInputStack = recipe.getItemToBuy + val secondInputStack = if (recipe.hasSecondItemToBuy) Option(recipe.getSecondItemToBuy) else None - def containsAccumulativeItemStack(stack: ItemStack) = - InventoryUtils.extractFromInventory(stack, inventory, ForgeDirection.UNKNOWN, simulate = true, exact = exact).stackSize == 0 + def containsAccumulativeItemStack(stack: ItemStack) = + InventoryUtils.extractFromInventory(stack, inventory, ForgeDirection.UNKNOWN, simulate = true, exact = exact).stackSize == 0 - // Check if we have enough to perform the trade. - if (!containsAccumulativeItemStack(firstInputStack) || !secondInputStack.forall(containsAccumulativeItemStack)) - return false + // Check if we have enough to perform the trade. + if (!containsAccumulativeItemStack(firstInputStack) || !secondInputStack.forall(containsAccumulativeItemStack)) + return false - // Now we need to check if we have enough inventory space to accept the item we get for the trade. - val outputStack = recipe.getItemToSell.copy() + // Now we need to check if we have enough inventory space to accept the item we get for the trade. + val outputStack = recipe.getItemToSell.copy() - // We established that out inventory allows to perform the trade, now actually do the trade. - InventoryUtils.extractFromInventory(firstInputStack, inventory, ForgeDirection.UNKNOWN, exact = exact) - secondInputStack.map(InventoryUtils.extractFromInventory(_, inventory, ForgeDirection.UNKNOWN, exact = exact)) - InventoryUtils.insertIntoInventory(outputStack, inventory, None, outputStack.stackSize) + // We established that out inventory allows to perform the trade, now actually do the trade. + InventoryUtils.extractFromInventory(firstInputStack, inventory, ForgeDirection.UNKNOWN, exact = exact) + secondInputStack.map(InventoryUtils.extractFromInventory(_, inventory, ForgeDirection.UNKNOWN, exact = exact)) + InventoryUtils.insertIntoInventory(outputStack, inventory, None, outputStack.stackSize) - // Tell the merchant we used the recipe, so MC can disable it and/or enable more recipes. - info.merchant.get.orNull.useRecipe(recipe) - true + // Tell the merchant we used the recipe, so MC can disable it and/or enable more recipes. + merchant.useRecipe(recipe) + true + } + case _ => false + } } } From ec2e7d4aaf970d89212958e5f40fa24d373e4360 Mon Sep 17 00:00:00 2001 From: payonel Date: Sat, 23 May 2020 01:29:04 -0700 Subject: [PATCH 13/18] some tooltips and a fix for possible install code path (nil ref) --- src/main/resources/assets/opencomputers/lang/en_US.lang | 1 + .../opencomputers/loot/openos/lib/core/install_basics.lua | 2 +- src/main/scala/li/cil/oc/Localization.scala | 2 ++ src/main/scala/li/cil/oc/client/gui/Case.scala | 2 +- src/main/scala/li/cil/oc/client/gui/Rack.scala | 8 ++++++++ src/main/scala/li/cil/oc/server/component/DebugCard.scala | 2 +- 6 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang index 849ba858f..30da048b9 100644 --- a/src/main/resources/assets/opencomputers/lang/en_US.lang +++ b/src/main/resources/assets/opencomputers/lang/en_US.lang @@ -230,6 +230,7 @@ oc:gui.Rack.None=None oc:gui.Rack.Right=Right oc:gui.Rack.Enabled=Enabled oc:gui.Rack.Disabled=Disabled +oc:gui.Rack.RelayModeTooltip=Relay Mode oc:gui.Rack.Top=Top oc:gui.Switch.PacketsPerCycle=Packets / cycle oc:gui.Switch.QueueSize=Queue size diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/install_basics.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/install_basics.lua index 41b72d1cf..7fcb945b9 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/install_basics.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/install_basics.lua @@ -206,7 +206,7 @@ end local special_target = "" if #targets > 1 or target_filter or source_filter then - special_target = " to " .. transfer_args[1].args[2] + special_target = " to " .. transfer_args[1][1][2] end io.write("Install " .. source_display .. special_target .. "? [Y/n] ") diff --git a/src/main/scala/li/cil/oc/Localization.scala b/src/main/scala/li/cil/oc/Localization.scala index 90321bf5d..82197eadf 100644 --- a/src/main/scala/li/cil/oc/Localization.scala +++ b/src/main/scala/li/cil/oc/Localization.scala @@ -142,6 +142,8 @@ object Localization { def RelayEnabled = localizeImmediately("gui.Rack.Enabled") def RelayDisabled = localizeImmediately("gui.Rack.Disabled") + + def RelayModeTooltip: String = localizeImmediately("gui.Rack.RelayModeTooltip") } object Switch { diff --git a/src/main/scala/li/cil/oc/client/gui/Case.scala b/src/main/scala/li/cil/oc/client/gui/Case.scala index 244697cbb..0b5c25ca4 100644 --- a/src/main/scala/li/cil/oc/client/gui/Case.scala +++ b/src/main/scala/li/cil/oc/client/gui/Case.scala @@ -48,4 +48,4 @@ class Case(playerInventory: InventoryPlayer, val computer: tileentity.Case) exte mc.renderEngine.bindTexture(Textures.guiComputer) drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) } -} \ No newline at end of file +} diff --git a/src/main/scala/li/cil/oc/client/gui/Rack.scala b/src/main/scala/li/cil/oc/client/gui/Rack.scala index 9c6fc9b4d..ea60d4114 100644 --- a/src/main/scala/li/cil/oc/client/gui/Rack.scala +++ b/src/main/scala/li/cil/oc/client/gui/Rack.scala @@ -11,6 +11,8 @@ import net.minecraft.entity.player.InventoryPlayer import net.minecraftforge.common.util.ForgeDirection import org.lwjgl.opengl.GL11 +import scala.collection.convert.WrapAsJava.asJavaCollection + class Rack(playerInventory: InventoryPlayer, val rack: tileentity.Rack) extends DynamicGuiContainer(new container.Rack(playerInventory, rack)) { ySize = 210 @@ -248,6 +250,12 @@ class Rack(playerInventory: InventoryPlayer, val rack: tileentity.Rack) extends x, y, 0x404040) } + if (relayButton.func_146115_a) { + val tooltip = new java.util.ArrayList[String] + tooltip.addAll(asJavaCollection(Localization.Rack.RelayModeTooltip.lines.toIterable)) + copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRendererObj) + } + GL11.glPopAttrib() } diff --git a/src/main/scala/li/cil/oc/server/component/DebugCard.scala b/src/main/scala/li/cil/oc/server/component/DebugCard.scala index e35d867d1..4ab26c9e7 100644 --- a/src/main/scala/li/cil/oc/server/component/DebugCard.scala +++ b/src/main/scala/li/cil/oc/server/component/DebugCard.scala @@ -622,7 +622,7 @@ object DebugCard { } val count = args.checkInteger(1) val damage = args.checkInteger(2) - val tagJson = args.checkString(3) + val tagJson = args.optString(3, "") val tag = if (Strings.isNullOrEmpty(tagJson)) null else JsonToNBT.func_150315_a(tagJson).asInstanceOf[NBTTagCompound] val position = BlockPosition(args.checkDouble(4), args.checkDouble(5), args.checkDouble(6), world) val side = args.checkSideAny(7) From 6ff0d7865e14e789915a874409cbf430c1fbe658 Mon Sep 17 00:00:00 2001 From: payonel Date: Sat, 23 May 2020 17:39:35 -0700 Subject: [PATCH 14/18] fix support for multiple gpu vram access to a screen --- .../li/cil/oc/client/PacketHandler.scala | 21 ++--- .../oc/common/component/GpuTextBuffer.scala | 91 ++++++++++++------- .../cil/oc/common/component/TextBuffer.scala | 54 +++++------ .../component/traits/TextBufferProxy.scala | 2 +- ...deoRamAware.scala => VideoRamDevice.scala} | 28 +++--- .../component/traits/VideoRamRasterizer.scala | 82 +++++++++++++++++ .../scala/li/cil/oc/server/PacketSender.scala | 14 +-- .../oc/server/component/GraphicsCard.scala | 34 +++---- 8 files changed, 208 insertions(+), 118 deletions(-) rename src/main/scala/li/cil/oc/common/component/traits/{VideoRamAware.scala => VideoRamDevice.scala} (59%) create mode 100644 src/main/scala/li/cil/oc/common/component/traits/VideoRamRasterizer.scala diff --git a/src/main/scala/li/cil/oc/client/PacketHandler.scala b/src/main/scala/li/cil/oc/client/PacketHandler.scala index 1e8f70e13..fd40c0dde 100644 --- a/src/main/scala/li/cil/oc/client/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/client/PacketHandler.scala @@ -705,13 +705,11 @@ object PacketHandler extends CommonPacketHandler { } def onTextBufferRamInit(p: PacketParser, buffer: api.internal.TextBuffer): Unit = { + val owner = p.readUTF() val id = p.readInt() val nbt = p.readNBT() - buffer match { - case screen: component.traits.VideoRamAware => screen.loadBuffer(id, nbt) - case _ => // ignore - } + component.ClientGpuTextBufferHandler.loadBuffer(buffer, owner, id, nbt) } def onTextBufferBitBlt(p: PacketParser, buffer: api.internal.TextBuffer): Unit = { @@ -719,24 +717,19 @@ object PacketHandler extends CommonPacketHandler { val row = p.readInt() val w = p.readInt() val h = p.readInt() + val owner = p.readUTF() val id = p.readInt() val fromCol = p.readInt() val fromRow = p.readInt() - component.GpuTextBuffer.bitblt(buffer, col, row, w, h, id, fromCol, fromRow) + component.ClientGpuTextBufferHandler.bitblt(buffer, col, row, w, h, owner, id, fromCol, fromRow) } def onTextBufferRamDestroy(p: PacketParser, buffer: api.internal.TextBuffer): Unit = { - val length = p.readInt() - val ids = new Array[Int](length) - for (i <- 0 until length) { - ids(i) = p.readInt() - } + val owner = p.readUTF() + val id = p.readInt() - buffer match { - case screen: component.traits.VideoRamAware => screen.removeBuffers(ids) - case _ => // ignore, not compatible with bitblts - } + component.ClientGpuTextBufferHandler.removeBuffer(buffer, owner, id) } def onTextBufferMultiRawSetText(p: PacketParser, buffer: api.internal.TextBuffer) { diff --git a/src/main/scala/li/cil/oc/common/component/GpuTextBuffer.scala b/src/main/scala/li/cil/oc/common/component/GpuTextBuffer.scala index 8c7d8d414..2d9f8f4df 100644 --- a/src/main/scala/li/cil/oc/common/component/GpuTextBuffer.scala +++ b/src/main/scala/li/cil/oc/common/component/GpuTextBuffer.scala @@ -1,14 +1,24 @@ package li.cil.oc.common.component -import li.cil.oc.api.network.{ManagedEnvironment, Message, Node} +import java.io.InvalidObjectException +import java.security.InvalidParameterException + +import li.cil.oc.api.network.{Environment, Message, Node} import net.minecraft.entity.player.EntityPlayer import net.minecraft.nbt.NBTTagCompound import li.cil.oc.api.internal.TextBuffer.ColorDepth import li.cil.oc.api -import li.cil.oc.common.component.traits.TextBufferProxy -import li.cil.oc.util.PackedColor +import li.cil.oc.common.component.traits.{TextBufferProxy, VideoRamDevice, VideoRamRasterizer} + +class GpuTextBuffer(val owner: String, val id: Int, val data: li.cil.oc.util.TextBuffer) extends traits.TextBufferProxy { + + // the gpu ram does not join nor is searchable to the network + // this field is required because the api TextBuffer is an Environment + override def node(): Node = { + throw new InvalidObjectException("GpuTextBuffers do not have nodes") + } + -class GpuTextBuffer(val id: Int, val data: li.cil.oc.util.TextBuffer) extends ManagedEnvironment with traits.TextBufferProxy { override def getMaximumWidth: Int = data.width override def getMaximumHeight: Int = data.height override def getViewportWidth: Int = data.height @@ -17,10 +27,19 @@ class GpuTextBuffer(val id: Int, val data: li.cil.oc.util.TextBuffer) extends Ma var dirty: Boolean = true override def onBufferSet(col: Int, row: Int, s: String, vertical: Boolean): Unit = dirty = true override def onBufferColorChange(): Unit = dirty = true - override def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, id: Int, fromCol: Int, fromRow: Int): Unit = dirty = true override def onBufferCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int): Unit = dirty = true override def onBufferFill(col: Int, row: Int, w: Int, h: Int, c: Char): Unit = dirty = true - override def onBufferRamInit(id: Int, ram: TextBufferProxy): Unit = dirty = false + + override def load(nbt: NBTTagCompound): Unit = { + // the data is initially dirty because other devices don't know about it yet + data.load(nbt) + dirty = true + } + + override def save(nbt: NBTTagCompound): Unit = { + data.save(nbt) + dirty = false + } override def setEnergyCostPerTick(value: Double): Unit = {} override def getEnergyCostPerTick: Double = 0 @@ -47,29 +66,42 @@ class GpuTextBuffer(val id: Int, val data: li.cil.oc.util.TextBuffer) extends Ma override def mouseScroll(x: Double, y: Double, delta: Int, player: EntityPlayer): Unit = {} override def canUpdate: Boolean = false override def update(): Unit = {} - override def node: li.cil.oc.api.network.Node = null override def onConnect(node: Node): Unit = {} override def onDisconnect(node: Node): Unit = {} override def onMessage(message: Message): Unit = {} - override def load(nbt: NBTTagCompound): Unit = {} - override def save(nbt: NBTTagCompound): Unit = {} } -object GpuTextBuffer { - def wrap(id: Int, data: li.cil.oc.util.TextBuffer): GpuTextBuffer = new GpuTextBuffer(id, data) - - def bitblt(dst: api.internal.TextBuffer, col: Int, row: Int, w: Int, h: Int, srcId: Int, fromCol: Int, fromRow: Int): Unit = { +object ClientGpuTextBufferHandler { + def bitblt(dst: api.internal.TextBuffer, col: Int, row: Int, w: Int, h: Int, owner: String, srcId: Int, fromCol: Int, fromRow: Int): Unit = { dst match { - case screen: traits.TextBufferProxy => screen.getBuffer(srcId) match { + case videoDevice: VideoRamRasterizer => videoDevice.getBuffer(owner, srcId) match { case Some(buffer: GpuTextBuffer) => { - bitblt(dst, col, row, w, h, buffer, fromCol, fromRow) + GpuTextBuffer.bitblt(dst, col, row, w, h, buffer, fromCol, fromRow) } case _ => // ignore - got a bitblt for a missing buffer } - case _ => // ignore - weird packet handler called this, should only happen for screens that know about thsi + case _ => // ignore - weird packet handler called this, should only happen for video ram aware devices } } + def removeBuffer(buffer: api.internal.TextBuffer, owner: String, id: Int): Boolean = { + buffer match { + case screen: VideoRamRasterizer => screen.removeBuffer(owner, id) + case _ => false // ignore, not compatible with bitblts + } + } + + def loadBuffer(buffer: api.internal.TextBuffer, owner: String, id: Int, nbt: NBTTagCompound): Boolean = { + buffer match { + case screen: VideoRamRasterizer => screen.loadBuffer(owner, id, nbt) + case _ => false // ignore, not compatible with bitblts + } + } +} + +object GpuTextBuffer { + def wrap(owner: String, id: Int, data: li.cil.oc.util.TextBuffer): GpuTextBuffer = new GpuTextBuffer(owner, id, data) + def bitblt(dst: api.internal.TextBuffer, col: Int, row: Int, w: Int, h: Int, src: api.internal.TextBuffer, fromCol: Int, fromRow: Int): Unit = { val x = col - 1 val y = row - 1 @@ -118,37 +150,28 @@ object GpuTextBuffer { } dst match { - case dstRam: GpuTextBuffer => src match { - case srcRam: GpuTextBuffer => write_vram_to_vram(dstRam, adjustedDstX, adjustedDstY, adjustedWidth, adjustedHeight, srcRam, adjustedSourceX, adjustedSourceY) - case srcScreen: traits.TextBufferProxy => write_screen_to_vram(dstRam, adjustedDstX, adjustedDstY, adjustedWidth, adjustedHeight, srcScreen, adjustedSourceX, adjustedSourceY) - case _ => throw new UnsupportedOperationException("Source buffer does not support bitblt operations") + case dstScreen: TextBuffer => src match { + case srcGpu: GpuTextBuffer => write_vram_to_screen(dstScreen, adjustedDstX, adjustedDstY, adjustedWidth, adjustedHeight, srcGpu, adjustedSourceX, adjustedSourceY) + case _ => throw new UnsupportedOperationException("Source buffer does not support bitblt operations to a screen") } - case dstScreen: traits.TextBufferProxy => src match { - case srcRam: GpuTextBuffer => write_vram_to_screen(dstScreen, adjustedDstX, adjustedDstY, adjustedWidth, adjustedHeight, srcRam, adjustedSourceX, adjustedSourceY) - case _: traits.TextBufferProxy => throw new UnsupportedOperationException("Screen to screen bitblt not supported") + case dstGpu: GpuTextBuffer => src match { + case srcProxy: TextBufferProxy => write_to_vram(dstGpu, adjustedDstX, adjustedDstY, adjustedWidth, adjustedHeight, srcProxy, adjustedSourceX, adjustedSourceY) case _ => throw new UnsupportedOperationException("Source buffer does not support bitblt operations") } case _ => throw new UnsupportedOperationException("Destination buffer does not support bitblt operations") } } - def write_vram_to_vram(dstRam: GpuTextBuffer, x: Int, y: Int, w: Int, h: Int, srcRam: GpuTextBuffer, fx: Int, fy: Int): Boolean = { - dstRam.data.rawcopy(x + 1, y + 1, w, h, srcRam.data, fx + 1, fx + 1) - } - - def write_vram_to_screen(dstScreen: traits.TextBufferProxy, x: Int, y: Int, w: Int, h: Int, srcRam: GpuTextBuffer, fx: Int, fy: Int): Boolean = { + def write_vram_to_screen(dstScreen: TextBuffer, x: Int, y: Int, w: Int, h: Int, srcRam: GpuTextBuffer, fx: Int, fy: Int): Boolean = { if (dstScreen.data.rawcopy(x + 1, y + 1, w, h, srcRam.data, fx + 1, fy + 1)) { // rawcopy returns true only if data was modified dstScreen.addBuffer(srcRam) - dstScreen.onBufferBitBlt(x + 1, y + 1, w, h, srcRam.id, fx + 1, fy + 1) + dstScreen.onBufferBitBlt(x + 1, y + 1, w, h, srcRam, fx + 1, fy + 1) true } else false } - def write_screen_to_vram(dstRam: GpuTextBuffer, x: Int, y: Int, w: Int, h: Int, srcScreen: traits.TextBufferProxy, fx: Int, fy: Int): Boolean = { - val format: PackedColor.ColorFormat = PackedColor.Depth.format(srcScreen.getColorDepth) - val tempGpu = GpuTextBuffer.wrap(id = -1, new li.cil.oc.util.TextBuffer(w, h, format)) - tempGpu.data.rawcopy(col = 1, row = 1, w, h, srcScreen.data, fx + 1, fy + 1) - write_vram_to_vram(dstRam, x, y, w, h, tempGpu, fx = 0, fy = 0) + def write_to_vram(dstRam: GpuTextBuffer, x: Int, y: Int, w: Int, h: Int, src: TextBufferProxy, fx: Int, fy: Int): Boolean = { + dstRam.data.rawcopy(x + 1, y + 1, w, h, src.data, fx + 1, fy + 1) } } diff --git a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala index 5383ddf53..6194876ce 100644 --- a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala +++ b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala @@ -22,7 +22,7 @@ import li.cil.oc.client.renderer.font.TextBufferRenderData import li.cil.oc.client.{ComponentTracker => ClientComponentTracker} import li.cil.oc.client.{PacketSender => ClientPacketSender} import li.cil.oc.common._ -import li.cil.oc.common.component.traits.TextBufferProxy +import li.cil.oc.common.component.traits.VideoRamRasterizer import li.cil.oc.server.component.Keyboard import li.cil.oc.server.{ComponentTracker => ServerComponentTracker} import li.cil.oc.server.{PacketSender => ServerPacketSender} @@ -40,7 +40,7 @@ import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ import scala.collection.mutable -class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment with traits.TextBufferProxy with DeviceInfo { +class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment with traits.TextBufferProxy with VideoRamRasterizer with DeviceInfo { override val node = api.Network.newNode(this, Visibility.Network). withComponent("screen"). withConnector(). @@ -323,16 +323,16 @@ class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment wi proxy.onBufferSet(col, row, s, vertical) } - override def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, id: Int, fromCol: Int, fromRow: Int): Unit = { - proxy.onBufferBitBlt(col, row, w, h, id, fromCol, fromRow) + override def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, ram: component.GpuTextBuffer, fromCol: Int, fromRow: Int): Unit = { + proxy.onBufferBitBlt(col, row, w, h, ram, fromCol, fromRow) } - override def onBufferRamInit(id: Int, ram: TextBufferProxy): Unit = { - proxy.onBufferRamInit(id, ram) + override def onBufferRamInit(ram: component.GpuTextBuffer): Unit = { + proxy.onBufferRamInit(ram) } - override def onBufferRamDestroy(ids: Array[Int]): Unit = { - proxy.onBufferRamDestroy(ids) + override def onBufferRamDestroy(ram: component.GpuTextBuffer): Unit = { + proxy.onBufferRamDestroy(ram) } override def rawSetText(col: Int, row: Int, text: Array[Array[Char]]): Unit = { @@ -556,15 +556,15 @@ object TextBuffer { owner.relativeLitArea = -1 } - def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, id: Int, fromCol: Int, fromRow: Int): Unit = { + def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, ram: component.GpuTextBuffer, fromCol: Int, fromRow: Int): Unit = { owner.relativeLitArea = -1 } - def onBufferRamInit(id: Int, ram: TextBufferProxy): Unit = { + def onBufferRamInit(ram: component.GpuTextBuffer): Unit = { owner.relativeLitArea = -1 } - def onBufferRamDestroy(ids: Array[Int]): Unit = { + def onBufferRamDestroy(ram: component.GpuTextBuffer): Unit = { owner.relativeLitArea = -1 } @@ -651,17 +651,17 @@ object TextBuffer { markDirty() } - override def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, id: Int, fromCol: Int, fromRow: Int): Unit = { - super.onBufferBitBlt(col, row, w, h, id, fromCol, fromRow) + override def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, ram: component.GpuTextBuffer, fromCol: Int, fromRow: Int): Unit = { + super.onBufferBitBlt(col, row, w, h, ram, fromCol, fromRow) markDirty() } - override def onBufferRamInit(id: Int, buffer: TextBufferProxy): Unit = { - super.onBufferRamInit(id, buffer) + override def onBufferRamInit(ram: component.GpuTextBuffer): Unit = { + super.onBufferRamInit(ram) } - override def onBufferRamDestroy(ids: Array[Int]): Unit = { - super.onBufferRamDestroy(ids) + override def onBufferRamDestroy(ram: component.GpuTextBuffer): Unit = { + super.onBufferRamDestroy(ram) } override def keyDown(character: Char, code: Int, player: EntityPlayer) { @@ -766,24 +766,24 @@ object TextBuffer { owner.synchronized(ServerPacketSender.appendTextBufferSet(owner.pendingCommands, col, row, s, vertical)) } - override def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, id: Int, fromCol: Int, fromRow: Int): Unit = { - super.onBufferBitBlt(col, row, w, h, id, fromCol, fromRow) + override def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, ram: component.GpuTextBuffer, fromCol: Int, fromRow: Int): Unit = { + super.onBufferBitBlt(col, row, w, h, ram, fromCol, fromRow) owner.host.markChanged() - owner.synchronized(ServerPacketSender.appendTextBufferBitBlt(owner.pendingCommands, col, row, w, h, id, fromCol, fromRow)) + owner.synchronized(ServerPacketSender.appendTextBufferBitBlt(owner.pendingCommands, col, row, w, h, ram.owner, ram.id, fromCol, fromRow)) } - override def onBufferRamInit(id: Int, buffer: TextBufferProxy): Unit = { - super.onBufferRamInit(id, buffer) + override def onBufferRamInit(ram: component.GpuTextBuffer): Unit = { + super.onBufferRamInit(ram) owner.host.markChanged() val nbt = new NBTTagCompound() - buffer.data.save(nbt) - owner.synchronized(ServerPacketSender.appendTextBufferRamInit(owner.pendingCommands, id, nbt)) + ram.save(nbt) + owner.synchronized(ServerPacketSender.appendTextBufferRamInit(owner.pendingCommands, ram.owner, ram.id, nbt)) } - override def onBufferRamDestroy(ids: Array[Int]): Unit = { - super.onBufferRamDestroy(ids) + override def onBufferRamDestroy(ram: component.GpuTextBuffer): Unit = { + super.onBufferRamDestroy(ram) owner.host.markChanged() - owner.synchronized(ServerPacketSender.appendTextBufferRamDestroy(owner.pendingCommands, ids)) + owner.synchronized(ServerPacketSender.appendTextBufferRamDestroy(owner.pendingCommands, ram.owner, ram.id)) } override def onBufferRawSetText(col: Int, row: Int, text: Array[Array[Char]]) { diff --git a/src/main/scala/li/cil/oc/common/component/traits/TextBufferProxy.scala b/src/main/scala/li/cil/oc/common/component/traits/TextBufferProxy.scala index 082670998..ae55093ff 100644 --- a/src/main/scala/li/cil/oc/common/component/traits/TextBufferProxy.scala +++ b/src/main/scala/li/cil/oc/common/component/traits/TextBufferProxy.scala @@ -5,7 +5,7 @@ import li.cil.oc.api import li.cil.oc.api.internal.TextBuffer import li.cil.oc.util.PackedColor -trait TextBufferProxy extends api.internal.TextBuffer with VideoRamAware { +trait TextBufferProxy extends api.internal.TextBuffer { def data: util.TextBuffer override def getWidth: Int = data.width diff --git a/src/main/scala/li/cil/oc/common/component/traits/VideoRamAware.scala b/src/main/scala/li/cil/oc/common/component/traits/VideoRamDevice.scala similarity index 59% rename from src/main/scala/li/cil/oc/common/component/traits/VideoRamAware.scala rename to src/main/scala/li/cil/oc/common/component/traits/VideoRamDevice.scala index 6a1fe0764..7bd2b4115 100644 --- a/src/main/scala/li/cil/oc/common/component/traits/VideoRamAware.scala +++ b/src/main/scala/li/cil/oc/common/component/traits/VideoRamDevice.scala @@ -1,37 +1,33 @@ package li.cil.oc.common.component.traits import li.cil.oc.common.component -import li.cil.oc.common.component.GpuTextBuffer import net.minecraft.nbt.NBTTagCompound +import scala.collection.mutable -trait VideoRamAware { - private val internalBuffers = new scala.collection.mutable.HashMap[Int, component.GpuTextBuffer] +trait VideoRamDevice { + private val internalBuffers = new mutable.HashMap[Int, component.GpuTextBuffer] val RESERVED_SCREEN_INDEX: Int = 0 - def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, id: Int, fromCol: Int, fromRow: Int): Unit = {} - def onBufferRamInit(id: Int, ram: TextBufferProxy): Unit = {} - def onBufferRamDestroy(ids: Array[Int]): Unit = {} + def isEmpty: Boolean = internalBuffers.isEmpty + + def onBufferRamDestroy(id: Int): Unit = {} def bufferIndexes(): Array[Int] = internalBuffers.collect { case (index: Int, _: Any) => index }.toArray - def addBuffer(buffer: component.GpuTextBuffer): Boolean = { - val preexists = internalBuffers.contains(buffer.id) - internalBuffers += buffer.id -> buffer - if (!preexists || buffer.dirty) { - buffer.onBufferRamInit(buffer.id, buffer) - onBufferRamInit(buffer.id, buffer) - } + def addBuffer(ram: component.GpuTextBuffer): Boolean = { + val preexists = internalBuffers.contains(ram.id) + internalBuffers += ram.id -> ram preexists } def removeBuffers(ids: Array[Int]): Int = { var count = 0 if (ids.nonEmpty) { - onBufferRamDestroy(ids) for (id <- ids) { if (internalBuffers.remove(id).nonEmpty) { + onBufferRamDestroy(id) count += 1 } } @@ -41,10 +37,10 @@ trait VideoRamAware { def removeAllBuffers(): Int = removeBuffers(bufferIndexes()) - def loadBuffer(id: Int, nbt: NBTTagCompound): Unit = { + def loadBuffer(address: String, id: Int, nbt: NBTTagCompound): Unit = { val src = new li.cil.oc.util.TextBuffer(width = 1, height = 1, li.cil.oc.util.PackedColor.SingleBitFormat) src.load(nbt) - addBuffer(component.GpuTextBuffer.wrap(id, src)) + addBuffer(component.GpuTextBuffer.wrap(address, id, src)) } def getBuffer(id: Int): Option[component.GpuTextBuffer] = { diff --git a/src/main/scala/li/cil/oc/common/component/traits/VideoRamRasterizer.scala b/src/main/scala/li/cil/oc/common/component/traits/VideoRamRasterizer.scala new file mode 100644 index 000000000..1aa3910cc --- /dev/null +++ b/src/main/scala/li/cil/oc/common/component/traits/VideoRamRasterizer.scala @@ -0,0 +1,82 @@ +package li.cil.oc.common.component.traits + +import li.cil.oc.common.component +import li.cil.oc.common.component.GpuTextBuffer +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.village.VillageDoorInfo + +import scala.collection.mutable + +trait VideoRamRasterizer { + class VirtualRamDevice(val owner: String) extends VideoRamDevice {} + private val internalBuffers = new mutable.HashMap[String, VideoRamDevice] + + def onBufferRamInit(ram: component.GpuTextBuffer): Unit + def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, ram: component.GpuTextBuffer, fromCol: Int, fromRow: Int): Unit + def onBufferRamDestroy(ram: component.GpuTextBuffer): Unit + + def addBuffer(ram: GpuTextBuffer): Boolean = { + var gpu = internalBuffers.get(ram.owner) + if (gpu.isEmpty) { + gpu = Option(new VirtualRamDevice(ram.owner)) + internalBuffers += ram.owner -> gpu.get + } + val preexists: Boolean = gpu.get.addBuffer(ram) + if (!preexists || ram.dirty) { + onBufferRamInit(ram) + } + preexists + } + + def removeBuffer(owner: String, id: Int): Boolean = { + internalBuffers.get(owner) match { + case Some(gpu: VideoRamDevice) => { + gpu.getBuffer(id) match { + case Some(ram: component.GpuTextBuffer) => { + onBufferRamDestroy(ram) + gpu.removeBuffers(Array(id)) == 1 + } + case _ => false + } + } + case _ => false + } + } + + def removeAllBuffers(owner: String): Int = { + var count = 0 + internalBuffers.get(owner) match { + case Some(gpu: VideoRamDevice) => { + val ids = gpu.bufferIndexes() + for (id <- ids) { + if (removeBuffer(owner, id)) { + count += 1 + } + } + } + case _ => Unit + } + count + } + + def removeAllBuffers(): Int = { + var count = 0 + for ((owner: String, _: Any) <- internalBuffers) { + count += removeAllBuffers(owner) + } + count + } + + def loadBuffer(owner: String, id: Int, nbt: NBTTagCompound): Boolean = { + val src = new li.cil.oc.util.TextBuffer(width = 1, height = 1, li.cil.oc.util.PackedColor.SingleBitFormat) + src.load(nbt) + addBuffer(component.GpuTextBuffer.wrap(owner, id, src)) + } + + def getBuffer(owner: String, id: Int): Option[component.GpuTextBuffer] = { + internalBuffers.get(owner) match { + case Some(gpu: VideoRamDevice) => gpu.getBuffer(id) + case _ => None + } + } +} diff --git a/src/main/scala/li/cil/oc/server/PacketSender.scala b/src/main/scala/li/cil/oc/server/PacketSender.scala index 8036e115e..eaead252f 100644 --- a/src/main/scala/li/cil/oc/server/PacketSender.scala +++ b/src/main/scala/li/cil/oc/server/PacketSender.scala @@ -665,31 +665,31 @@ object PacketSender { pb.writeBoolean(vertical) } - def appendTextBufferBitBlt(pb: PacketBuilder, col: Int, row: Int, w: Int, h: Int, id: Int, fromCol: Int, fromRow: Int): Unit = { + def appendTextBufferBitBlt(pb: PacketBuilder, col: Int, row: Int, w: Int, h: Int, owner: String, id: Int, fromCol: Int, fromRow: Int): Unit = { pb.writePacketType(PacketType.TextBufferBitBlt) pb.writeInt(col) pb.writeInt(row) pb.writeInt(w) pb.writeInt(h) + pb.writeUTF(owner) pb.writeInt(id) pb.writeInt(fromCol) pb.writeInt(fromRow) } - def appendTextBufferRamInit(pb: PacketBuilder, id: Int, nbt: NBTTagCompound): Unit = { + def appendTextBufferRamInit(pb: PacketBuilder, address: String, id: Int, nbt: NBTTagCompound): Unit = { pb.writePacketType(PacketType.TextBufferRamInit) + pb.writeUTF(address) pb.writeInt(id) pb.writeNBT(nbt) } - def appendTextBufferRamDestroy(pb: PacketBuilder, ids: Array[Int]): Unit = { + def appendTextBufferRamDestroy(pb: PacketBuilder, owner: String, id: Int): Unit = { pb.writePacketType(PacketType.TextBufferRamDestroy) - pb.writeInt(ids.length) - for (idx <- ids) { - pb.writeInt(idx) - } + pb.writeUTF(owner) + pb.writeInt(id) } def appendTextBufferRawSetText(pb: PacketBuilder, col: Int, row: Int, text: Array[Array[Char]]) { diff --git a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala index 1e7c0c18a..e4e4130c8 100644 --- a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala @@ -30,7 +30,7 @@ import scala.util.matching.Regex // saved, but before the computer was saved, leading to mismatching states in // the save file - a Bad Thing (TM). -class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceInfo with component.traits.VideoRamAware { +class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceInfo with component.traits.VideoRamDevice { override val node: Connector = Network.newNode(this, Visibility.Neighbors). withComponent("gpu"). withConnector(). @@ -142,28 +142,30 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI } else if (size > (totalVRAM - calculateUsedMemory)) { result(Unit, "not enough video memory") + } else if (node == null) { + result(Unit, "graphics card appears disconnected") } else { val format: PackedColor.ColorFormat = PackedColor.Depth.format(Settings.screenDepthsByTier(tier)) val buffer = new li.cil.oc.util.TextBuffer(width, height, format) - val page = component.GpuTextBuffer.wrap(nextAvailableBufferIndex, buffer) + val page = component.GpuTextBuffer.wrap(node.address, nextAvailableBufferIndex, buffer) addBuffer(page) result(page.id) } } // this event occurs when the gpu is told a page was removed - we need to notify the screen of this - // we do this because the VideoRamAware trait only notifies itself, it doesn't assume there is a screen - override def onBufferRamDestroy(ids: Array[Int]): Unit = { + // we do this because the VideoRamDevice trait only notifies itself, it doesn't assume there is a screen + override def onBufferRamDestroy(id: Int): Unit = { // first protect our buffer index - it needs to fall back to the screen if its buffer was removed - if (ids.contains(bufferIndex)) { - bufferIndex = RESERVED_SCREEN_INDEX - } - if (ids.nonEmpty) { + if (id != RESERVED_SCREEN_INDEX) { screen(RESERVED_SCREEN_INDEX, s => s match { - case oc: component.traits.VideoRamAware => result(oc.removeBuffers(ids)) + case oc: component.traits.VideoRamRasterizer => result(oc.removeBuffer(node.address, id)) case _ => result(true)// addon mod screen type that is not video ram aware }) - } else result(true) + } + if (id == bufferIndex) { + bufferIndex = RESERVED_SCREEN_INDEX + } } @Callback(direct = true, doc = """function(index: number): boolean -- Closes buffer at `index`. Returns true if a buffer closed. If the current buffer is closed, index moves to 0""") @@ -284,7 +286,7 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI s.setForegroundColor(0xFFFFFF) s.setBackgroundColor(0x000000) s match { - case oc: component.traits.VideoRamAware => oc.removeAllBuffers() + case oc: component.traits.VideoRamRasterizer => oc.removeAllBuffers() case _ => } } @@ -296,13 +298,7 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI } @Callback(direct = true, doc = """function():string -- Get the address of the screen the GPU is currently bound to.""") - def getScreen(context: Context, args: Arguments): Array[AnyRef] = { - if (bufferIndex == RESERVED_SCREEN_INDEX) { - screen(s => result(s.node.address)) - } else { - result(Unit, "the current device is video ram") - } - } + def getScreen(context: Context, args: Arguments): Array[AnyRef] = screen(RESERVED_SCREEN_INDEX, s => result(s.node.address)) @Callback(direct = true, doc = """function():number, boolean -- Get the current background color and whether it's from the palette or not.""") def getBackground(context: Context, args: Arguments): Array[AnyRef] = @@ -633,7 +629,7 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI val nbtPage = nbtPages.getCompoundTagAt(i) val idx: Int = nbtPage.getInteger(NBT_PAGE_IDX) val data = nbtPage.getCompoundTag(NBT_PAGE_DATA) - loadBuffer(idx, data) + loadBuffer(node.address, idx, data) } } } From 20f2cc460f69a2c2cbf2e40e5106f409d2f0f381 Mon Sep 17 00:00:00 2001 From: payonel Date: Sat, 23 May 2020 22:59:27 -0700 Subject: [PATCH 15/18] optimize color write and increase bitblt limits the color write improvements appears to give clients a 90% speed up on loading color nbt --- src/main/resources/application.conf | 6 +-- src/main/scala/li/cil/oc/Settings.scala | 7 +-- .../oc/server/component/GraphicsCard.scala | 2 +- .../scala/li/cil/oc/util/NbtDataStream.scala | 49 +++++++++++++++++++ .../scala/li/cil/oc/util/TextBuffer.scala | 15 ++---- 5 files changed, 58 insertions(+), 21 deletions(-) create mode 100644 src/main/scala/li/cil/oc/util/NbtDataStream.scala diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 300782102..9312cf05f 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -1598,8 +1598,8 @@ opencomputers { # This setting assigns the budget call cost to invoke bitblt to write vram # to a screen. Video ram can bitblit to a screen which can cause real life # network laod the defaults settings put bitblit network impact close to gpu.set - # Increase these values to throttle bitblt more. These values appear inverted - # but the cost of bitblt is based on the max resolution of the device - bitbltCosts: [ 1, 2, 8 ] + # Increase these values to throttle bitblt more. The cost tier N is bitbltCost * 2^(tier) + # default is .5, which gives: .5, 1, 4 + bitbltCost: 0.5 } } diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index 0a266ce65..72c06a1d2 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -476,12 +476,7 @@ class Settings(val config: Config) { Array(1, 2, 3) } - val bitbltCosts: Array[Double] = Array(config.getDoubleList("gpu.bitbltCosts"): _*) match { - case Array(tier1, tier2, tier3) => Array(tier1: Double, tier2: Double, tier3: Double) - case _ => - OpenComputers.log.warn("Bad number of bitblit costs (expected 3), ignoring.") - Array(1, 2, 8) - } + val bitbltCost: Double = if (config.hasPath("gpu.bitbltCost")) config.getDouble("gpu.bitbltCost") else 0.5 } object Settings { diff --git a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala index e4e4130c8..2868ee98b 100644 --- a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala @@ -71,7 +71,7 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI // These are dirty page bitblt budget costs // a single bitblt can send a screen of data, which is n*set calls where set is writing an entire line // So for each tier, we multiple the set cost with the number of lines the screen may have - final val bitbltCost: Double = Settings.get.bitbltCosts(0 max tier min 2) + final val bitbltCost: Double = Settings.get.bitbltCost * scala.math.pow(2, tier) final val totalVRAM: Double = (maxResolution._1 * maxResolution._2) * Settings.get.vramSizes(0 max tier min 2) var budgetExhausted: Boolean = false // for especially expensive calls, bitblt diff --git a/src/main/scala/li/cil/oc/util/NbtDataStream.scala b/src/main/scala/li/cil/oc/util/NbtDataStream.scala new file mode 100644 index 000000000..b56149310 --- /dev/null +++ b/src/main/scala/li/cil/oc/util/NbtDataStream.scala @@ -0,0 +1,49 @@ +package li.cil.oc.util + +import net.minecraft.nbt.NBTTagCompound + +object NbtDataStream { + def getShortArray(nbt: NBTTagCompound, key: String, array2d: Array[Array[Short]], w: Int, h: Int) : Boolean = { + if (!nbt.hasKey(key)) { + return false + } + + val rawByteReader = new java.io.ByteArrayInputStream(nbt.getByteArray(key)) + val memReader = new java.io.DataInputStream(rawByteReader) + for (y <- 0 until h) { + for (x <- 0 until w) { + if (2 > memReader.available()) { + return true // not great, but get out now + } + array2d(y)(x) = memReader.readShort() + } + } + true + } + + def getIntArrayLegacy(nbt: NBTTagCompound, key: String, array2d: Array[Array[Short]], w: Int, h: Int) : Boolean = { + if (!nbt.hasKey(key)) { + return false + } + // legacy format + val c = nbt.getIntArray(key) + for (y <- 0 until h) { + val rowColor = array2d(y) + for (x <- 0 until w) { + val index = x + y * w + if (index >= c.length) { + return true // not great, but, the read at least started + } + rowColor(x) = c(index).toShort + } + } + true + } + + def setShortArray(nbt: NBTTagCompound, key: String, array: Array[Short]): Unit = { + val rawByteWriter = new java.io.ByteArrayOutputStream() + val memWriter = new java.io.DataOutputStream(rawByteWriter) + array.foreach(memWriter.writeShort(_)) + nbt.setByteArray(key, rawByteWriter.toByteArray) + } +} diff --git a/src/main/scala/li/cil/oc/util/TextBuffer.scala b/src/main/scala/li/cil/oc/util/TextBuffer.scala index 3b4de3105..382a425a5 100644 --- a/src/main/scala/li/cil/oc/util/TextBuffer.scala +++ b/src/main/scala/li/cil/oc/util/TextBuffer.scala @@ -262,15 +262,8 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col foreground = PackedColor.Color(nbt.getInteger("foreground"), nbt.getBoolean("foregroundIsPalette")) background = PackedColor.Color(nbt.getInteger("background"), nbt.getBoolean("backgroundIsPalette")) - val c = nbt.getIntArray("color") - for (i <- 0 until h) { - val rowColor = color(i) - for (j <- 0 until w) { - val index = j + i * w - if (index < c.length) { - rowColor(j) = c(index).toShort - } - } + if (!NbtDataStream.getShortArray(nbt, "color_bytes", color, w, h)) { + NbtDataStream.getIntArrayLegacy(nbt, "color", color, w, h) } } @@ -291,10 +284,10 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col nbt.setInteger("background", _background.value) nbt.setBoolean("backgroundIsPalette", _background.isPalette) - nbt.setTag("color", new NBTTagIntArray(color.flatten.map(_.toInt))) + NbtDataStream.setShortArray(nbt, "color_bytes", color.flatten.map(_.toShort)) } - override def toString = { + override def toString: String = { val b = StringBuilder.newBuilder if (buffer.length > 0) { b.appendAll(buffer(0)) From af9a34a6af53b528ea654d23a960acdbb2425af3 Mon Sep 17 00:00:00 2001 From: payonel Date: Sat, 23 May 2020 23:45:01 -0700 Subject: [PATCH 16/18] fixed mixed tier gpu and screen bitblt --- src/main/scala/li/cil/oc/util/TextBuffer.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/util/TextBuffer.scala b/src/main/scala/li/cil/oc/util/TextBuffer.scala index 382a425a5..3f867ff49 100644 --- a/src/main/scala/li/cil/oc/util/TextBuffer.scala +++ b/src/main/scala/li/cil/oc/util/TextBuffer.scala @@ -215,7 +215,14 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col val dstColorLine = color(row_index + yOffset) for (xOffset <- 0 until w) { val srcChar = src.buffer(fromRow + yOffset - 1)(fromCol + xOffset - 1) - val srcColor = src.color(fromRow + yOffset - 1)(fromCol + xOffset - 1) + var srcColor = src.color(fromRow + yOffset - 1)(fromCol + xOffset - 1) + + if (this.format.depth != src.format.depth) { + val fg = PackedColor.Color(PackedColor.unpackForeground(srcColor, src.format)) + val bg = PackedColor.Color(PackedColor.unpackBackground(srcColor, src.format)) + srcColor = PackedColor.pack(fg, bg, format) + } + if (srcChar != dstCharLine(col_index + xOffset) || srcColor != dstColorLine(col_index + xOffset)) { changed = true dstCharLine(col_index + xOffset) = srcChar From e0f42c30ee379e350cde02a78c866a8dcf821a7e Mon Sep 17 00:00:00 2001 From: payonel Date: Sun, 24 May 2020 00:35:51 -0700 Subject: [PATCH 17/18] rename color_bytes to colors --- .../li/cil/oc/server/component/GraphicsCard.scala | 10 ++++++++++ src/main/scala/li/cil/oc/util/TextBuffer.scala | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala index 2868ee98b..ce64575fe 100644 --- a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala @@ -444,6 +444,16 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI @Callback(direct = true, doc = """function(x:number, y:number):string, number, number, number or nil, number or nil -- Get the value displayed on the screen at the specified index, as well as the foreground and background color. If the foreground or background is from the palette, returns the palette indices as fourth and fifth results, else nil, respectively.""") def get(context: Context, args: Arguments): Array[AnyRef] = { + // maybe one day: +// if (bufferIndex != RESERVED_SCREEN_INDEX && args.count() == 0) { +// return screen { +// case ram: GpuTextBuffer => { +// val nbt = new NBTTagCompound +// ram.data.save(nbt) +// result(nbt) +// } +// } +// } val x = args.checkInteger(0) - 1 val y = args.checkInteger(1) - 1 screen(s => { diff --git a/src/main/scala/li/cil/oc/util/TextBuffer.scala b/src/main/scala/li/cil/oc/util/TextBuffer.scala index 3f867ff49..ce4393a47 100644 --- a/src/main/scala/li/cil/oc/util/TextBuffer.scala +++ b/src/main/scala/li/cil/oc/util/TextBuffer.scala @@ -269,7 +269,7 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col foreground = PackedColor.Color(nbt.getInteger("foreground"), nbt.getBoolean("foregroundIsPalette")) background = PackedColor.Color(nbt.getInteger("background"), nbt.getBoolean("backgroundIsPalette")) - if (!NbtDataStream.getShortArray(nbt, "color_bytes", color, w, h)) { + if (!NbtDataStream.getShortArray(nbt, "colors", color, w, h)) { NbtDataStream.getIntArrayLegacy(nbt, "color", color, w, h) } } @@ -291,7 +291,7 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col nbt.setInteger("background", _background.value) nbt.setBoolean("backgroundIsPalette", _background.isPalette) - NbtDataStream.setShortArray(nbt, "color_bytes", color.flatten.map(_.toShort)) + NbtDataStream.setShortArray(nbt, "colors", color.flatten.map(_.toShort)) } override def toString: String = { From 7b078ada5bd658e3557cc0b2e4e66ee9e33ab516 Mon Sep 17 00:00:00 2001 From: payonel Date: Sun, 24 May 2020 00:42:20 -0700 Subject: [PATCH 18/18] use rawequal for internal table checks in machien.lua thanks bauen1 --- src/main/resources/assets/opencomputers/lua/machine.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/assets/opencomputers/lua/machine.lua b/src/main/resources/assets/opencomputers/lua/machine.lua index 1177f5f71..7c42c3b49 100644 --- a/src/main/resources/assets/opencomputers/lua/machine.lua +++ b/src/main/resources/assets/opencomputers/lua/machine.lua @@ -796,7 +796,7 @@ sandbox = { local handled = false checkArg(2, msgh, "function") local result = table.pack(xpcall(f, function(...) - if (...) == tooLongWithoutYielding then + if rawequal((...), tooLongWithoutYielding) then return tooLongWithoutYielding elseif handled then return ... @@ -805,7 +805,7 @@ sandbox = { return msgh(...) end end, ...)) - if result[2] == tooLongWithoutYielding then + if rawequal(result[2], tooLongWithoutYielding) then result = table.pack(result[1], select(2, pcallTimeoutCheck(pcall(msgh, tostring(tooLongWithoutYielding))))) end return table.unpack(result, 1, result.n)