From 9e6265d5bbecb6f9d7ac262ac3e0e69aa85256d2 Mon Sep 17 00:00:00 2001 From: shylie Date: Wed, 16 Jul 2025 13:08:07 -0400 Subject: [PATCH] Reduce VRAM usage by a lot --- main.lua | 20 ++++++----- zprite/zchunk.lua | 34 +++++++++++------- zprite/zprite.lua | 88 +++++++++++++++++++++++++++++++++-------------- 3 files changed, 95 insertions(+), 47 deletions(-) diff --git a/main.lua b/main.lua index 12a1dca..226c223 100644 --- a/main.lua +++ b/main.lua @@ -21,30 +21,32 @@ end local BIG_BLOCK = love.graphics.newQuad(24 * 9, 24 * 9, 24, 24, tex) -local z = require("zprite").zchunk.new(tex, 384, 40) -z._height_scale = 10 +local z = require("zprite").zchunk.new(tex, 384, 32) +z._height_scale = 8 local function update_map() - for i = -32, 32 do - for j = -32, 32 do - local layers = love.math.simplexNoise(i * 0.02, j * 0.02) * 34 + 6 + z:clear() + for i = -64, 64 do + for j = -64, 64 do + local layers = love.math.simplexNoise(i * 0.02, j * 0.02) * 29 + 3 z:put(i * 24, j * 24, BIG_BLOCK, math.floor(layers) + 1, layers < 23 and dirt_color or mountain_color) coroutine.yield() end end end - local co = coroutine.create(update_map) local angle = 0 function love.update(dt) - if coroutine.status(co) == "suspended" then - coroutine.resume(co) + for _ = 1, 4 do + if coroutine.status(co) == "suspended" then + coroutine.resume(co) + end end angle = math.fmod(angle + dt * 0.8, 2 * math.pi) end function love.draw() - z:draw(love.graphics.getWidth() / 2, love.graphics.getHeight() / 2 + 128, angle, 0.3, 0.3) + z:draw(love.graphics.getWidth() / 2, love.graphics.getHeight() / 2 + 128, angle, 0.2, 0.2) end diff --git a/zprite/zchunk.lua b/zprite/zchunk.lua index 248e1f6..41702c2 100644 --- a/zprite/zchunk.lua +++ b/zprite/zchunk.lua @@ -45,24 +45,34 @@ function zchunk:_chunk_index(x, y) return math.floor(x / self._chunk_size) + math.floor(y / self._chunk_size) * 1e7 end ----@param x integer ----@param y integer +---@param x integer? +---@param y integer? function zchunk:remove(x, y) - local index = self:_chunk_index(x, y) - if not self._chunks[index] then - return + if not x then + self._chunks = {} + else + local index = self:_chunk_index(x, y) + if not self._chunks[index] then + return + end + self._chunks[index] = {} end - self._chunks[index] = {} end ----@param x integer ----@param y integer +---@param x integer? +---@param y integer? function zchunk:clear(x, y) - local index = self:_chunk_index(x, y) - if not self._chunks[index] then - return + if not x then + for _, chunk in pairs(self._chunks) do + chunk.z:clear() + end + else + local index = self:_chunk_index(x, y) + if not self._chunks[index] then + return + end + self._chunks[index].z:clear() end - self._chunks[index].z:clear() end --- Add an 'entity' to be rendered diff --git a/zprite/zprite.lua b/zprite/zprite.lua index 802b748..b35bf46 100644 --- a/zprite/zprite.lua +++ b/zprite/zprite.lua @@ -12,26 +12,42 @@ local zprite = {} zprite.__index = zprite local ZPRITE_SHADER = love.graphics.newShader([[ +#pragma language glsl4 + varying vec4 VsColorOut; #ifdef VERTEX -layout (location = 3) attribute vec2 InstancePosition; -layout (location = 4) attribute vec2 InstanceTextureOffset; -layout (location = 5) attribute vec2 InstanceScale; -layout (location = 6) attribute float InstanceLayer; -layout (location = 7) attribute vec4 InstanceColor; +layout (location = 3) attribute uvec3 InstanceData; uniform float HeightScale; vec4 position(mat4 transform_projection, vec4 vertex_position) { - vertex_position.xy *= InstanceScale; - vertex_position.xy += InstancePosition.xy; - vertex_position.z = InstanceLayer / 1024; - VaryingTexCoord.xy *= InstanceScale; - VaryingTexCoord.xy += InstanceTextureOffset; - VsColorOut = InstanceColor; - return (transform_projection * vertex_position) + vec4(0, (InstanceLayer * HeightScale) / love_ScreenSize.y, 0, 0); + uint position_x = bitfieldExtract(InstanceData.x, 0, 16); + uint position_y = bitfieldExtract(InstanceData.x, 16, 16); + vec2 position = vec2(position_x, position_y) - vec2(32768, 32768); + + uint uv_x = bitfieldExtract(InstanceData.y, 0, 10); + uint uv_y = bitfieldExtract(InstanceData.y, 10, 10); + vec2 uv = vec2(uv_x, uv_y) / vec2(1024, 1024); + + uint scale_x = bitfieldExtract(InstanceData.y, 20, 6); + uint scale_y = bitfieldExtract(InstanceData.y, 26, 6); + vec2 scale = vec2(scale_x, scale_y); + + uint layer = bitfieldExtract(InstanceData.z, 0, 8); + + uint r = bitfieldExtract(InstanceData.z, 8, 8); + uint g = bitfieldExtract(InstanceData.z, 16, 8); + uint b = bitfieldExtract(InstanceData.z, 24, 8); + vec3 color = vec3(r, g, b) / vec3(256, 256, 256); + + vertex_position.xy *= scale; + vertex_position.xy += position; + VaryingTexCoord.xy *= scale; + VaryingTexCoord.xy += uv; + VsColorOut = vec4(color, 1); + return (transform_projection * vertex_position) + vec4(0, (layer * HeightScale) / love_ScreenSize.y, 0, 0); } #endif @@ -76,21 +92,13 @@ function zprite.new(texture, max_instances) }, vertices, "strip", "static") obj._mesh:setTexture(texture) obj._instance_buffer = love.graphics.newBuffer({ - { name = "InstancePosition", format = "floatvec2", location = 3 }, - { name = "InstanceTextureOffset", format = "floatvec2", location = 4 }, - { name = "InstanceScale", format = "floatvec2", location = 5 }, - { name = "InstanceLayer", format = "float", location = 6 }, - { name = "InstanceColor", format = "floatvec4", location = 7 }, + { name = "InstanceData", format = "uint32vec3", location = 3 }, }, obj._instance_vertex_count, { vertex = true }) obj._instances = {} obj._height_scale = 4 obj._changed = false obj._mesh:attachAttribute(3, obj._instance_buffer, "perinstance") - obj._mesh:attachAttribute(4, obj._instance_buffer, "perinstance") - obj._mesh:attachAttribute(5, obj._instance_buffer, "perinstance") - obj._mesh:attachAttribute(6, obj._instance_buffer, "perinstance") - obj._mesh:attachAttribute(7, obj._instance_buffer, "perinstance") obj._top_layer = 0 obj._count = 0 @@ -123,21 +131,49 @@ function zprite:put(x, y, quad, layer_count, color_map, base_layer) self._changed = true + local pos_x = bit.band(x + 32768, 0xFFFF) + local pos_y = bit.lshift(bit.band(y + 32768, 0xFFFF), 16) + local pos = bit.bor(pos_x, pos_y) + local qx, qy, qw, qh = quad:getViewport() local tw, th = self._texture:getPixelDimensions() + local uv_x = bit.band(1024 * qx / tw, 0x3FF) + local uv_y = bit.lshift(bit.band(1024 * qy / th, 0x3FF), 10) + local uv = bit.bor(uv_x, uv_y) + + local scale_x = bit.lshift(bit.band(qw, 0x3F), 20) + local scale_y = bit.lshift(bit.band(qh, 0x3F), 26) + local scale = bit.bor(scale_x, scale_y) + + local uv_scale = bit.bor(uv, scale) + for i = 1, layer_count do - local r, g, b, a = color_map(i) - if not a then - a = 1 - end if not self._instances[i + base_layer] then self._instances[i + base_layer] = {} if i > self._top_layer then self._top_layer = i end end - table.insert(self._instances[i], { x, y, qx / tw, qy / th, qw, qh, i + base_layer, r, g, b, a }) + local layer = bit.band(i + base_layer, 0xFF) + + local r, g, b = color_map(i) + if r > 1 then + r = 1 + end + if g > 1 then + g = 1 + end + if b > 1 then + b = 1 + end + local rb = bit.lshift(bit.band(r * 0xFF, 0xFF), 8) + local gb = bit.lshift(bit.band(g * 0xFF, 0xFF), 16) + local bb = bit.lshift(bit.band(b * 0xFF, 0xFF), 24) + + local layer_rgb = bit.bor(bit.bor(layer, rb), bit.bor(gb, bb)) + + table.insert(self._instances[i], { pos, uv_scale, layer_rgb }) self._count = self._count + 1 end end