Make state sensitive custom models cache more efficient (save memory)

This commit is contained in:
Mike Primm 2021-12-29 18:41:54 -06:00
parent 0166895a7f
commit c48ed688b2
22 changed files with 141 additions and 15 deletions

View File

@ -366,8 +366,8 @@ public class OBJExport {
/* If no patches, see if custom model */
if(patches == null) {
CustomBlockModel cbm = models.getCustomBlockModel(blk);
if(cbm != null) { /* If so, get our meshes */
patches = cbm.getMeshForBlock(map);
if (cbm != null) { /* If so, get our meshes */
patches = cbm.getMeshForBlock(map);
}
}
if (patches != null) {

View File

@ -43,6 +43,9 @@ public class CustomBlockModel extends HDBlockModel {
return render.getMaximumTextureCount(HDBlockModels.pdf);
}
public boolean isOnlyBlockStateSensitive() {
return render.isOnlyBlockStateSensitive();
}
private static final RenderPatch[] empty_list = new RenderPatch[0];
public RenderPatch[] getMeshForBlock(MapDataContext ctx) {

View File

@ -5,14 +5,22 @@ import org.dynmap.utils.PatchDefinition;
public class HDScaledBlockModels {
private short[][] modelvectors;
private PatchDefinition[][] patches;
private CustomBlockModel[] custom;
// These are scale invariant - only need once
private static PatchDefinition[][] patches;
private static CustomBlockModel[] custom;
public HDScaledBlockModels(int scale) {
short[][] blockmodels = new short[DynmapBlockState.getGlobalIndexMax()][];
PatchDefinition[][] patches = new PatchDefinition[DynmapBlockState.getGlobalIndexMax()][];
CustomBlockModel[] custom = new CustomBlockModel[DynmapBlockState.getGlobalIndexMax()];
PatchDefinition[][] newpatches = null;
if (patches == null) {
newpatches = new PatchDefinition[DynmapBlockState.getGlobalIndexMax()][];
patches = newpatches;
}
CustomBlockModel[] newcustom = null;
if (custom == null) {
newcustom = new CustomBlockModel[DynmapBlockState.getGlobalIndexMax()];
custom = newcustom;
}
for(Integer gidx : HDBlockModels.models_by_id_data.keySet()) {
HDBlockModel m = HDBlockModels.models_by_id_data.get(gidx);
@ -34,18 +42,19 @@ public class HDScaledBlockModels {
}
}
else if(m instanceof HDBlockPatchModel) {
HDBlockPatchModel pm = (HDBlockPatchModel)m;
patches[gidx] = pm.getPatches();
if (newpatches != null) {
HDBlockPatchModel pm = (HDBlockPatchModel)m;
newpatches[gidx] = pm.getPatches();
}
}
else if(m instanceof CustomBlockModel) {
CustomBlockModel cbm = (CustomBlockModel)m;
custom[gidx] = cbm;
if (newcustom != null) {
CustomBlockModel cbm = (CustomBlockModel)m;
newcustom[gidx] = cbm;
}
}
}
this.modelvectors = blockmodels;
this.patches = patches;
this.custom = custom;
}
public final short[] getScaledModel(DynmapBlockState blk) {

View File

@ -8,6 +8,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.dynmap.Client;
import org.dynmap.Color;
@ -27,6 +28,7 @@ import org.dynmap.renderer.RenderPatchFactory.SideVisible;
import org.dynmap.storage.MapStorage;
import org.dynmap.storage.MapStorageTile;
import org.dynmap.utils.BlockStep;
import org.dynmap.utils.DynIntHashMap;
import org.dynmap.hdmap.TexturePack.BlockTransparency;
import org.dynmap.utils.DynmapBufferedImage;
import org.dynmap.utils.LightLevels;
@ -79,6 +81,9 @@ public class IsoHDPerspective implements HDPerspective {
private static final BlockStep [] semi_steps = { BlockStep.Y_PLUS, BlockStep.X_MINUS, BlockStep.X_PLUS, BlockStep.Z_MINUS, BlockStep.Z_PLUS };
// Cache for custom meshes by state (shared, reusable)
private static RenderPatch[][] custom_meshes_by_globalstateindex = null;
private class OurPerspectiveState implements HDPerspectiveState {
DynmapBlockState blocktype = DynmapBlockState.AIR;
DynmapBlockState lastblocktype = DynmapBlockState.AIR;
@ -554,7 +559,15 @@ public class IsoHDPerspective implements HDPerspective {
this.setCustomFluidMesh(patches);
}
}
else {
// Else, if block state specific model
else if (cbm.isOnlyBlockStateSensitive()) {
patches = this.getCustomMeshForState(bt); // Look up mesh by state
if (patches == null) { // Miss, generate it
patches = cbm.getMeshForBlock(mapiter);
this.setCustomMeshForState(bt, patches);
}
}
else { // Else, block specific
patches = this.getCustomMesh();
if (patches == null) {
patches = cbm.getMeshForBlock(mapiter);
@ -919,6 +932,12 @@ public class IsoHDPerspective implements HDPerspective {
long key = this.mapiter.getBlockKey(); /* Get key for current block */
return (RenderPatch[])custom_meshes.get(key);
}
/**
* Get custom mesh for block, if defined (null if not)
*/
public final RenderPatch[] getCustomMeshForState(DynmapBlockState bs) {
return (RenderPatch[]) custom_meshes_by_globalstateindex[bs.globalStateIndex];
}
/**
* Save custom mesh for block
*/
@ -926,6 +945,12 @@ public class IsoHDPerspective implements HDPerspective {
long key = this.mapiter.getBlockKey(); /* Get key for current block */
custom_meshes.put(key, mesh);
}
/**
* Set custom mesh for block, if defined (null if not)
*/
public final void setCustomMeshForState(DynmapBlockState bs, RenderPatch[] mesh) {
custom_meshes_by_globalstateindex[bs.globalStateIndex] = mesh;
}
/**
* Get custom fluid mesh for block, if defined (null if not)
*/
@ -943,6 +968,9 @@ public class IsoHDPerspective implements HDPerspective {
}
public IsoHDPerspective(DynmapCore core, ConfigurationNode configuration) {
if (custom_meshes_by_globalstateindex == null) {
custom_meshes_by_globalstateindex = new RenderPatch[DynmapBlockState.getGlobalIndexMax()][];
}
name = configuration.getString("name", null);
if(name == null) {
Log.severe("Perspective definition missing name - must be defined and unique");

View File

@ -73,4 +73,8 @@ public class BoxRenderer extends CustomRenderer {
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
return model;
}
@Override
public boolean isOnlyBlockStateSensitive() {
return true;
}
}

View File

@ -89,4 +89,8 @@ public class BoxStateRenderer extends CustomRenderer {
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
return models[ctx.getBlockType().stateIndex];
}
@Override
public boolean isOnlyBlockStateSensitive() {
return true;
}
}

View File

@ -31,4 +31,8 @@ public class ChestStateRenderer extends ChestRenderer {
}
return models[byIndex[idx].ordinal()];
}
@Override
public boolean isOnlyBlockStateSensitive() {
return true;
}
}

View File

@ -123,4 +123,8 @@ public class CuboidRenderer extends CustomRenderer {
int idx = ctx.getBlockType().stateIndex;
return models[idx % models.length];
}
@Override
public boolean isOnlyBlockStateSensitive() {
return true;
}
}

View File

@ -108,4 +108,8 @@ public class FenceGateBlockRenderer extends CustomRenderer {
return meshes[meta & 0x7];
}
@Override
public boolean isOnlyBlockStateSensitive() {
return true;
}
}

View File

@ -120,5 +120,9 @@ public class FenceGateBlockStateRenderer extends CustomRenderer {
// 32 states: meta%2=powered|unpowered, (meta/2)%2=open/closed, (meta/4)%2=in-wall/not-in-wall, (meta/8)%4=n/s/w/e
return meshes[(meta >> 1) & 0xF]; // Don't care about powered: models are 0-15
}
@Override
public boolean isOnlyBlockStateSensitive() {
return true;
}
}

View File

@ -216,4 +216,8 @@ public class FenceWallBlockStateRenderer extends CustomRenderer {
}
return meshes[off];
}
@Override
public boolean isOnlyBlockStateSensitive() {
return true;
}
}

View File

@ -55,4 +55,8 @@ public class GlowLichenStateRenderer extends CustomRenderer {
int idx = ((ctx.getBlockType().stateIndex & 0x7C) >> 1) + (ctx.getBlockType().stateIndex & 0x1); // Shift out waterlogged bit
return meshes[idx];
}
@Override
public boolean isOnlyBlockStateSensitive() {
return true;
}
}

View File

@ -61,4 +61,8 @@ public class HeadRenderer extends CustomRenderer {
else
return meshes[0];
}
@Override
public boolean isOnlyBlockStateSensitive() {
return true;
}
}

View File

@ -14,4 +14,8 @@ public class PaneStateRenderer extends PaneRenderer {
int meshidx = (((idx & 0x10) == 0) ? SIDE_XP : 0) | (((idx & 0x08) == 0) ? SIDE_ZN : 0) | (((idx & 0x04) == 0) ? SIDE_ZP : 0) | (((idx & 0x01) == 0) ? SIDE_XN : 0);
return meshes[meshidx];
}
@Override
public boolean isOnlyBlockStateSensitive() {
return true;
}
}

View File

@ -45,4 +45,8 @@ public class RedstoneWireStateRenderer extends RedstoneWireRenderer {
}
return getMesh(pidx);
}
@Override
public boolean isOnlyBlockStateSensitive() {
return true;
}
}

View File

@ -131,4 +131,8 @@ public class RotatedBoxRenderer extends CustomRenderer {
Log.info("Unmatched rotation index: " + textureIdx + " for " + ctx.getBlockType());
return models[0];
}
@Override
public boolean isOnlyBlockStateSensitive() {
return idx_attrib == null;
}
}

View File

@ -154,4 +154,8 @@ public class RotatedPatchRenderer extends CustomRenderer {
return basemodel;
}
}
@Override
public boolean isOnlyBlockStateSensitive() {
return idx_attrib == null;
}
}

View File

@ -159,4 +159,8 @@ public class StairStateRenderer extends CustomRenderer {
}
return rp;
}
@Override
public boolean isOnlyBlockStateSensitive() {
return true;
}
}

View File

@ -52,4 +52,8 @@ public class VineStateRenderer extends CustomRenderer {
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
return meshes[ctx.getBlockType().stateIndex];
}
@Override
public boolean isOnlyBlockStateSensitive() {
return true;
}
}

View File

@ -61,4 +61,8 @@ public class WallHeadRenderer extends CustomRenderer {
else
return meshes[0];
}
@Override
public boolean isOnlyBlockStateSensitive() {
return true;
}
}

View File

@ -184,4 +184,12 @@ public abstract class CustomRenderer {
public RenderPatch getSidePatch(RenderPatchFactory rpf, int side, int rot, int textureidx) {
return getSidePatch(rpf, side, 0.0, 0.0, 1.0, 0.0, 1.0, rot, textureidx);
}
/**
* Test if the given renderer is purely a function of the block state of the given block (that is, not
* directly tied to neighbor blocks, location, or other factors). If true, the returned model for a getRenderPatchList() is
* cached by block state, versus by specific block location/instance.
*/
public boolean isOnlyBlockStateSensitive() {
return false;
}
}

View File

@ -549,4 +549,18 @@ public class DynmapBlockState {
public String toString() {
return fullName;
}
// Equals check
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj instanceof DynmapBlockState) {
return ((DynmapBlockState)obj).globalStateIndex == this.globalStateIndex;
}
return false;
}
// Hashcode
@Override
public int hashCode() {
return this.globalStateIndex;
}
}