Merge pull request #775 from MihailRis/optimizations

allocations-related optimizations
This commit is contained in:
MihailRis 2026-01-29 22:32:12 +03:00 • committed by GitHub
commit a636347edc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 207 additions and 84 deletions

View file

@ -441,7 +441,8 @@ static void load_script(const Content& content, T& def) {
def.name,
scriptfile,
def.scriptFile,
def.rt.funcsset
def.rt.funcsset,
def.rt.eventNames
);
}
}

View file

@ -15,6 +15,9 @@ struct ItemFuncsSet {
bool on_block_break_by : 1;
};
struct ItemFuncNamesCache {
};
enum class ItemIconType {
NONE, // invisible (core:empty) must not be rendered
SPRITE, // textured quad: icon is `atlas_name:texture_name`
@ -75,6 +78,8 @@ struct ItemDef {
bool emissive = false;
std::set<int> tags;
ItemFuncNamesCache eventNames;
} rt {};
ItemDef(const std::string& name);

View file

@ -1,6 +1,3 @@
#include <iostream>
#include <assert.h>
#include "LightSolver.hpp"
#include "Lightmap.hpp"
#include "content/Content.hpp"
@ -9,6 +6,8 @@
#include "voxels/voxel.hpp"
#include "voxels/Block.hpp"
#include <assert.h>
LightSolver::LightSolver(const ContentIndices& contentIds, Chunks& chunks, int channel)
: blockDefs(contentIds.blocks.getDefs()),
chunks(chunks),
@ -55,7 +54,7 @@ void LightSolver::remove(int x, int y, int z) {
lightmap.set(x-chunk->x*CHUNK_W, y, z-chunk->z*CHUNK_D, channel, 0);
}
void LightSolver::solve(){
void LightSolver::solve(Chunk* prevailingChunk) {
const int coords[] = {
0, 0, 1,
0, 0,-1,
@ -66,47 +65,54 @@ void LightSolver::solve(){
};
while (!remqueue.empty()){
const lightentry entry = remqueue.front();
lightentry entry = std::move(remqueue.front());
remqueue.pop();
for (int i = 0; i < 6; i++) {
int imul3 = i*3;
int imul3 = i * 3;
int x = entry.x+coords[imul3];
int y = entry.y+coords[imul3+1];
int z = entry.z+coords[imul3+2];
Chunk* chunk = chunks.getChunkByVoxel(x,y,z);
if (chunk) {
int lx = x - chunk->x * CHUNK_W;
int lz = z - chunk->z * CHUNK_D;
chunk->flags.modified = true;
Chunk* chunk = prevailingChunk;
if (chunk == nullptr || !chunk->isBlockInside(x, z)) {
chunk = chunks.getChunkByVoxel(x,y,z);
if (chunk == nullptr) {
continue;
}
} else if (y < 0 || y >= CHUNK_H) {
continue;
}
assert(chunk->lightmap != nullptr);
auto& lightmap = *chunk->lightmap;
int lx = x - chunk->x * CHUNK_W;
int lz = z - chunk->z * CHUNK_D;
chunk->flags.modified = true;
ubyte light = lightmap.get(lx,y,lz, channel);
if (light != 0 && light == entry.light-1){
voxel* vox = chunks.get(x, y, z);
if (vox && vox->id != 0) {
const Block* block = blockDefs[vox->id];
if (uint8_t emission = block->emission[channel]) {
addqueue.push(lightentry {x, y, z, emission});
lightmap.set(lx, y, lz, channel, emission);
}
else lightmap.set(lx, y, lz, channel, 0);
assert(chunk->lightmap != nullptr);
auto& lightmap = *chunk->lightmap;
ubyte light = lightmap.get(lx,y,lz, channel);
if (light != 0 && light == entry.light-1) {
voxel* vox = chunks.get(x, y, z);
if (vox && vox->id != 0) {
const Block* block = blockDefs[vox->id];
if (uint8_t emission = block->emission[channel]) {
addqueue.push(lightentry {x, y, z, emission});
lightmap.set(lx, y, lz, channel, emission);
}
else lightmap.set(lx, y, lz, channel, 0);
remqueue.push(lightentry {x, y, z, light});
}
else if (light >= entry.light){
addqueue.push(lightentry {x, y, z, light});
}
else lightmap.set(lx, y, lz, channel, 0);
remqueue.push(lightentry {x, y, z, light});
}
else if (light >= entry.light) {
addqueue.push(lightentry {x, y, z, light});
}
}
}
while (!addqueue.empty()){
const lightentry entry = addqueue.front();
lightentry entry = std::move(addqueue.front());
addqueue.pop();
for (int i = 0; i < 6; i++) {
@ -115,10 +121,16 @@ void LightSolver::solve(){
int y = entry.y+coords[imul3+1];
int z = entry.z+coords[imul3+2];
Chunk* chunk = chunks.getChunkByVoxel(x,y,z);
if (chunk == nullptr) {
Chunk* chunk = prevailingChunk;
if (chunk == nullptr || !chunk->isBlockInside(x, z)) {
chunk = chunks.getChunkByVoxel(x,y,z);
if (chunk == nullptr) {
continue;
}
} else if (y < 0 || y >= CHUNK_H) {
continue;
}
assert(chunk->lightmap != nullptr);
auto& lightmap = *chunk->lightmap;
int lx = x - chunk->x * CHUNK_W;

View file

@ -1,7 +1,8 @@
#pragma once
#include <queue>
#include "util/array_queue.hpp"
class Chunk;
class Chunks;
class ContentIndices;
class Block;
@ -14,8 +15,8 @@ struct lightentry {
};
class LightSolver {
std::queue<lightentry> addqueue;
std::queue<lightentry> remqueue;
util::array_queue<lightentry> addqueue;
util::array_queue<lightentry> remqueue;
const Block* const* blockDefs;
Chunks& chunks;
int channel;
@ -25,5 +26,5 @@ public:
void add(int x, int y, int z);
void add(int x, int y, int z, int emission);
void remove(int x, int y, int z);
void solve();
void solve(Chunk* prevailingChunk = nullptr);
};

View file

@ -167,10 +167,10 @@ void Lighting::onChunkLoaded(int cx, int cz, bool expand) {
}
}
}
solverR.solve();
solverG.solve();
solverB.solve();
solverS.solve();
solverR.solve(chunk);
solverG.solve(chunk);
solverB.solve(chunk);
solverS.solve(chunk);
}
void Lighting::onBlockSet(int x, int y, int z, blockid_t id){
@ -180,9 +180,10 @@ void Lighting::onBlockSet(int x, int y, int z, blockid_t id){
solverB->remove(x,y,z);
if (id == 0){
solverR->solve();
solverG->solve();
solverB->solve();
auto chunk = chunks.getChunkByVoxel(glm::ivec3{x, y, z});
solverR->solve(chunk);
solverG->solve(chunk);
solverB->solve(chunk);
if (chunks.getLight(x,y+1,z, 3) == 0xF){
for (int i = y; i >= 0; i--){
voxel* vox = chunks.get(x,i,z);
@ -191,17 +192,30 @@ void Lighting::onBlockSet(int x, int y, int z, blockid_t id){
solverS->add(x,i,z, 0xF);
}
}
solverR->add(x,y+1,z); solverG->add(x,y+1,z); solverB->add(x,y+1,z); solverS->add(x,y+1,z);
solverR->add(x,y-1,z); solverG->add(x,y-1,z); solverB->add(x,y-1,z); solverS->add(x,y-1,z);
solverR->add(x+1,y,z); solverG->add(x+1,y,z); solverB->add(x+1,y,z); solverS->add(x+1,y,z);
solverR->add(x-1,y,z); solverG->add(x-1,y,z); solverB->add(x-1,y,z); solverS->add(x-1,y,z);
solverR->add(x,y,z+1); solverG->add(x,y,z+1); solverB->add(x,y,z+1); solverS->add(x,y,z+1);
solverR->add(x,y,z-1); solverG->add(x,y,z-1); solverB->add(x,y,z-1); solverS->add(x,y,z-1);
solverR->solve();
solverG->solve();
solverB->solve();
solverS->solve();
const int coords[] = {
0, 0, 1,
0, 0,-1,
0, 1, 0,
0,-1, 0,
1, 0, 0,
-1, 0, 0
};
for (int i = 0; i < 6; i++) {
int lx = x + coords[i * 3];
int ly = y + coords[i * 3 + 1];
int lz = z + coords[i * 3 + 2];
solverR->add(lx, ly, lz);
solverG->add(lx, ly, lz);
solverB->add(lx, ly, lz);
solverS->add(lx, ly, lz);
solverR->solve(chunk);
solverG->solve(chunk);
solverB->solve(chunk);
solverS->solve(chunk);
}
} else {
auto chunk = chunks.getChunkByVoxel(glm::ivec3{x, y, z});
if (!block.skyLightPassing){
solverS->remove(x,y,z);
for (int i = y-1; i >= 0; i--){
@ -210,19 +224,19 @@ void Lighting::onBlockSet(int x, int y, int z, blockid_t id){
break;
}
}
solverS->solve();
solverS->solve(chunk);
}
solverR->solve();
solverG->solve();
solverB->solve();
solverR->solve(chunk);
solverG->solve(chunk);
solverB->solve(chunk);
if (block.emission[0] || block.emission[1] || block.emission[2]){
solverR->add(x,y,z,block.emission[0]);
solverG->add(x,y,z,block.emission[1]);
solverB->add(x,y,z,block.emission[2]);
solverR->solve();
solverG->solve();
solverB->solve();
solverR->solve(chunk);
solverG->solve(chunk);
solverB->solve(chunk);
}
}
}

View file

@ -1,7 +1,5 @@
#include "BlocksController.hpp"
#include <set>
#include "content/Content.hpp"
#include "items/Inventories.hpp"
#include "items/Inventory.hpp"
@ -175,12 +173,8 @@ void BlocksController::randomTick(
void BlocksController::randomTick(int tickid, int parts, uint padding) {
auto indices = level.content.getIndices();
std::set<uint64_t> chunksIterated;
for (const auto& [pid, player] : *level.players) {
const auto& chunks = *player->chunks;
int offsetX = chunks.getOffsetX();
int offsetY = chunks.getOffsetY();
int width = chunks.getWidth();
int height = chunks.getHeight();
int segments = 4;
@ -195,20 +189,15 @@ void BlocksController::randomTick(int tickid, int parts, uint padding) {
if (chunk == nullptr || !chunk->flags.lighted) {
continue;
}
union {
int32_t pos[2];
uint64_t key;
} posU;
posU.pos[0] = x + offsetX;
posU.pos[1] = z + offsetY;
if (chunksIterated.find(posU.key) != chunksIterated.end()) {
if (chunk->lastRandomTickId == randomTickId) {
continue;
}
chunksIterated.insert(posU.key);
chunk->lastRandomTickId = randomTickId;
randomTick(*chunk, segments, indices);
}
}
}
randomTickId++;
}
int64_t BlocksController::createBlockInventory(int x, int y, int z) {

View file

@ -33,6 +33,7 @@ class BlocksController {
util::Clock worldTickClock;
FastRandom random {};
std::vector<on_block_interaction> blockInteractionCallbacks;
uint64_t randomTickId = 0;
public:
BlocksController(const Level& level, Lighting* lighting);

View file

@ -1,8 +1,5 @@
#include "scripting.hpp"
#include <iostream>
#include <stdexcept>
#include "scripting_commons.hpp"
#include "content/Content.hpp"
#include "content/ContentPack.hpp"
@ -28,6 +25,9 @@
#include "world/World.hpp"
#include "interfaces/Process.hpp"
#include <iostream>
#include <stdexcept>
using namespace scripting;
static debug::Logger logger("scripting");
@ -391,15 +391,15 @@ void scripting::on_blocks_tick(const Block& block, int tps) {
}
void scripting::update_block(const Block& block, const glm::ivec3& pos) {
std::string name = block.name + ".update";
lua::emit_event(lua::get_main_state(), name, [pos](auto L) {
lua::emit_event(lua::get_main_state(), block.rt.eventNames.update,
[pos](auto L) {
return lua::pushivec_stack(L, pos);
});
}
void scripting::random_update_block(const Block& block, const glm::ivec3& pos) {
std::string name = block.name + ".randupdate";
lua::emit_event(lua::get_main_state(), name, [pos](auto L) {
lua::emit_event(lua::get_main_state(), block.rt.eventNames.randomUpdate,
[pos](auto L) {
return lua::pushivec_stack(L, pos);
});
}
@ -660,7 +660,8 @@ void scripting::load_content_script(
const std::string& prefix,
const io::path& file,
const std::string& fileName,
BlockFuncsSet& funcsset
BlockFuncsSet& funcsset,
BlockFuncNamesCache& namesCache
) {
int env = *senv;
lua::pop(lua::get_main_state(), load_script(env, "block", file, fileName));
@ -686,6 +687,9 @@ void scripting::load_content_script(
register_event(env, "on_block_present", prefix + ".blockpresent");
funcsset.onblockremoved =
register_event(env, "on_block_removed", prefix + ".blockremoved");
namesCache.randomUpdate = prefix + ".update";
namesCache.randomUpdate = prefix + ".randupdate";
}
void scripting::load_content_script(
@ -693,7 +697,8 @@ void scripting::load_content_script(
const std::string& prefix,
const io::path& file,
const std::string& fileName,
ItemFuncsSet& funcsset
ItemFuncsSet& funcsset,
ItemFuncNamesCache& namesCache
) {
int env = *senv;
lua::pop(lua::get_main_state(), load_script(env, "item", file, fileName));

View file

@ -23,7 +23,9 @@ struct ItemDef;
class Inventory;
class UiDocument;
struct BlockFuncsSet;
struct BlockFuncNamesCache;
struct ItemFuncsSet;
struct ItemFuncNamesCache;
struct WorldFuncsSet;
struct UserComponent;
struct uidocscript;
@ -168,7 +170,8 @@ namespace scripting {
const std::string& prefix,
const io::path& file,
const std::string& fileName,
BlockFuncsSet& funcsset
BlockFuncsSet& funcsset,
BlockFuncNamesCache& namesCache
);
/// @brief Load script associated with an Item
@ -182,7 +185,8 @@ namespace scripting {
const std::string& prefix,
const io::path& file,
const std::string& fileName,
ItemFuncsSet& funcsset
ItemFuncsSet& funcsset,
ItemFuncNamesCache& namesCache
);
/// @brief Load component script

76
src/util/array_queue.hpp Normal file
View file

@ -0,0 +1,76 @@
#pragma once
#include <memory>
#include <cstring>
#include <stdexcept>
namespace util {
template <typename T>
class array_queue {
public:
array_queue(size_t initCapacity = 8)
: _capacity(initCapacity),
_size(0),
_front(0),
_back(0),
_data(std::make_unique<T[]>(_capacity)) {
if (initCapacity == 0 || (initCapacity & (initCapacity - 1)) != 0) {
throw std::invalid_argument("initCapacity must be positive power of 2");
}
}
~array_queue() {
while (!empty()) {
pop();
}
}
void push(T&& value) {
if (_back == _front && _size) {
grow();
}
_data[_back] = std::forward<T>(value);
_back = (_back + 1) & (_capacity - 1);
++_size;
}
T& front() {
return _data[_front];
}
void pop() {
_data[_front].~T();
_front = (_front + 1) & (_capacity - 1);
--_size;
}
bool empty() const {
return _size == 0;
}
size_t size() const {
return _size;
}
private:
size_t _capacity;
size_t _size;
size_t _front;
size_t _back;
std::unique_ptr<T[]> _data;
void grow() {
size_t newCapacity = _capacity * 2;
auto newData = std::make_unique<T[]>(newCapacity);
std::move(_data.get() + _front, _data.get() + _capacity, newData.get());
std::move(_data.get(), _data.get() + _front, newData.get() + (_capacity - _front));
_back = _size;
_front = 0;
_data.reset(newData.release());
_capacity = newCapacity;
}
};
}

View file

@ -54,6 +54,11 @@ struct BlockFuncsSet {
bool onblockremoved : 1;
};
struct BlockFuncNamesCache {
std::string update;
std::string randomUpdate;
};
struct CoordSystem {
std::array<glm::ivec3, 3> axes;
/// @brief Grid 3d position fix offset (for negative vectors)
@ -296,6 +301,8 @@ public:
blockid_t surfaceReplacement = 0;
std::set<int> tags;
BlockFuncNamesCache eventNames;
} rt {};
Block(const std::string& name);

View file

@ -40,6 +40,8 @@ public:
bool dirtyHeights : 1;
} flags {};
uint64_t lastRandomTickId = -1;
/// @brief Block inventories map where key is index of block in voxels array
ChunkInventoriesMap inventories;
/// @brief Blocks metadata heap
@ -81,4 +83,10 @@ public:
glm::vec3((x + 1) * CHUNK_W, INFINITY, (z + 1) * CHUNK_D)
);
}
bool isBlockInside(int x, int z) const {
x -= this->x * CHUNK_W;
z -= this->z * CHUNK_D;
return x >= 0 && z >= 0 && x < CHUNK_W && z < CHUNK_D;
}
};