/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
*/
/**
* @file newgrf_internal.h Internal NewGRF processing definitions.
*/
#ifndef NEWGRF_INTERNAL_H
#define NEWGRF_INTERNAL_H
#include "newgrf.h"
#include "newgrf_spritegroup.h"
#include "spriteloader/spriteloader.hpp"
#include "core/arena_alloc.hpp"
#include "3rdparty/cpp-btree/btree_map.h"
#include
#include
/** Base GRF ID for OpenTTD's base graphics GRFs. */
static const uint32_t OPENTTD_GRAPHICS_BASE_GRF_ID = BSWAP32(0xFF4F5400);
struct VarAction2GroupVariableTracking {
std::bitset<256> in;
std::bitset<256> out;
std::bitset<256> proc_call_out;
std::bitset<256> proc_call_in;
};
struct VarAction2ProcedureAnnotation {
std::bitset<256> stores;
uint32_t special_register_values[16];
uint16_t special_register_mask = 0;
bool unskippable = false;
};
/** Temporary data during loading of GRFs */
struct GrfProcessingState {
private:
/** Definition of a single Action1 spriteset */
struct SpriteSet {
SpriteID sprite; ///< SpriteID of the first sprite of the set.
uint num_sprites; ///< Number of sprites in the set.
};
/** Currently referenceable spritesets */
btree::btree_map spritesets[GSF_END];
public:
/* Global state */
GrfLoadingStage stage; ///< Current loading stage
SpriteID spriteid; ///< First available SpriteID for loading realsprites.
/* Local state in the file */
SpriteFile *file; ///< File of currently processed GRF file.
GRFFile *grffile; ///< Currently processed GRF file.
GRFConfig *grfconfig; ///< Config of the currently processed GRF file.
uint32_t nfo_line; ///< Currently processed pseudo sprite number in the GRF.
/* Kind of return values when processing certain actions */
int skip_sprites; ///< Number of pseudo sprites to skip before processing the next one. (-1 to skip to end of file)
/* Currently referenceable spritegroups */
std::vector spritegroups;
/* VarAction2 temporary storage variable tracking */
btree::btree_map group_temp_store_variable_tracking;
UniformArenaAllocator group_temp_store_variable_tracking_storage;
btree::btree_map procedure_annotations;
UniformArenaAllocator procedure_annotations_storage;
btree::btree_map *> inlinable_adjust_groups;
UniformArenaAllocator), 1024> inlinable_adjust_groups_storage;
std::vector dead_store_elimination_candidates;
VarAction2GroupVariableTracking *GetVarAction2GroupVariableTracking(const SpriteGroup *group, bool make_new)
{
if (make_new) {
VarAction2GroupVariableTracking *&ptr = this->group_temp_store_variable_tracking[group];
if (!ptr) ptr = new (this->group_temp_store_variable_tracking_storage.Allocate()) VarAction2GroupVariableTracking();
return ptr;
} else {
auto iter = this->group_temp_store_variable_tracking.find(group);
if (iter != this->group_temp_store_variable_tracking.end()) return iter->second;
return nullptr;
}
}
std::pair GetVarAction2ProcedureAnnotation(const SpriteGroup *group)
{
VarAction2ProcedureAnnotation *&ptr = this->procedure_annotations[group];
if (!ptr) {
ptr = new (this->procedure_annotations_storage.Allocate()) VarAction2ProcedureAnnotation();
return std::make_pair(ptr, true);
} else {
return std::make_pair(ptr, false);
}
}
std::vector *GetInlinableGroupAdjusts(const DeterministicSpriteGroup *group, bool make_new)
{
if (make_new) {
std::vector *&ptr = this->inlinable_adjust_groups[group];
if (!ptr) ptr = new (this->inlinable_adjust_groups_storage.Allocate()) std::vector();
return ptr;
} else {
auto iter = this->inlinable_adjust_groups.find(group);
if (iter != this->inlinable_adjust_groups.end()) return iter->second;
return nullptr;
}
}
/** Clear temporary data before processing the next file in the current loading stage */
void ClearDataForNextFile()
{
this->nfo_line = 0;
this->skip_sprites = 0;
for (uint i = 0; i < GSF_END; i++) {
this->spritesets[i].clear();
}
this->spritegroups.clear();
this->group_temp_store_variable_tracking.clear();
this->group_temp_store_variable_tracking_storage.EmptyArena();
this->procedure_annotations.clear();
this->procedure_annotations_storage.EmptyArena();
for (auto iter : this->inlinable_adjust_groups) {
iter.second->~vector();
}
this->inlinable_adjust_groups.clear();
this->inlinable_adjust_groups_storage.EmptyArena();
this->dead_store_elimination_candidates.clear();
}
/**
* Records new spritesets.
* @param feature GrfSpecFeature the set is defined for.
* @param first_sprite SpriteID of the first sprite in the set.
* @param first_set First spriteset to define.
* @param numsets Number of sets to define.
* @param numents Number of sprites per set to define.
*/
void AddSpriteSets(uint8_t feature, SpriteID first_sprite, uint first_set, uint numsets, uint numents)
{
assert(feature < GSF_END);
for (uint i = 0; i < numsets; i++) {
SpriteSet &set = this->spritesets[feature][first_set + i];
set.sprite = first_sprite + i * numents;
set.num_sprites = numents;
}
}
/**
* Check whether there are any valid spritesets for a feature.
* @param feature GrfSpecFeature to check.
* @return true if there are any valid sets.
* @note Spritesets with zero sprites are valid to allow callback-failures.
*/
bool HasValidSpriteSets(uint8_t feature) const
{
assert(feature < GSF_END);
return !this->spritesets[feature].empty();
}
/**
* Check whether a specific set is defined.
* @param feature GrfSpecFeature to check.
* @param set Set to check.
* @return true if the set is valid.
* @note Spritesets with zero sprites are valid to allow callback-failures.
*/
bool IsValidSpriteSet(uint8_t feature, uint set) const
{
assert(feature < GSF_END);
return this->spritesets[feature].find(set) != this->spritesets[feature].end();
}
/**
* Returns the first sprite of a spriteset.
* @param feature GrfSpecFeature to query.
* @param set Set to query.
* @return First sprite of the set.
*/
SpriteID GetSprite(uint8_t feature, uint set) const
{
assert(IsValidSpriteSet(feature, set));
return this->spritesets[feature].find(set)->second.sprite;
}
/**
* Returns the number of sprites in a spriteset
* @param feature GrfSpecFeature to query.
* @param set Set to query.
* @return Number of sprites in the set.
*/
uint GetNumEnts(uint8_t feature, uint set) const
{
assert(IsValidSpriteSet(feature, set));
return this->spritesets[feature].find(set)->second.num_sprites;
}
};
extern GrfProcessingState _cur;
enum VarAction2AdjustInferenceFlags {
VA2AIF_NONE = 0x00,
VA2AIF_SIGNED_NON_NEGATIVE = 0x01,
VA2AIF_ONE_OR_ZERO = 0x02,
VA2AIF_PREV_TERNARY = 0x04,
VA2AIF_PREV_MASK_ADJUST = 0x08,
VA2AIF_PREV_STORE_TMP = 0x10,
VA2AIF_HAVE_CONSTANT = 0x20,
VA2AIF_SINGLE_LOAD = 0x40,
VA2AIF_MUL_BOOL = 0x80,
VA2AIF_PREV_SCMP_DEC = 0x100,
VA2AIF_PREV_MASK = VA2AIF_PREV_TERNARY | VA2AIF_PREV_MASK_ADJUST | VA2AIF_PREV_STORE_TMP | VA2AIF_PREV_SCMP_DEC,
};
DECLARE_ENUM_AS_BIT_SET(VarAction2AdjustInferenceFlags)
struct VarAction2TempStoreInferenceVarSource {
DeterministicSpriteGroupAdjustType type;
uint16_t variable;
uint8_t shift_num;
uint32_t parameter;
uint32_t and_mask;
uint32_t add_val;
uint32_t divmod_val;
};
struct VarAction2TempStoreInference {
VarAction2AdjustInferenceFlags inference = VA2AIF_NONE;
uint32_t store_constant = 0;
VarAction2TempStoreInferenceVarSource var_source;
uint version = 0;
};
struct VarAction2InferenceBackup {
VarAction2AdjustInferenceFlags inference = VA2AIF_NONE;
uint32_t current_constant = 0;
uint adjust_size = 0;
};
struct VarAction2OptimiseState {
VarAction2AdjustInferenceFlags inference = VA2AIF_NONE;
uint32_t current_constant = 0;
btree::btree_map temp_stores;
VarAction2InferenceBackup inference_backup;
VarAction2GroupVariableTracking *var_tracking = nullptr;
bool seen_procedure_call = false;
bool var_1C_present = false;
bool check_expensive_vars = false;
bool enable_dse = false;
uint default_variable_version = 0;
uint32_t special_register_store_values[16];
uint16_t special_register_store_mask = 0;
inline VarAction2GroupVariableTracking *GetVarTracking(DeterministicSpriteGroup *group)
{
if (this->var_tracking == nullptr) {
this->var_tracking = _cur.GetVarAction2GroupVariableTracking(group, true);
}
return this->var_tracking;
}
};
inline void OptimiseVarAction2PreCheckAdjust(VarAction2OptimiseState &state, const DeterministicSpriteGroupAdjust &adjust)
{
uint16_t variable = adjust.variable;
if (variable == 0x7B) variable = adjust.parameter;
if (variable == 0x1C) state.var_1C_present = true;
}
struct VarAction2AdjustInfo {
GrfSpecFeature feature;
GrfSpecFeature scope_feature;
uint8_t varsize;
};
const SpriteGroup *PruneTargetSpriteGroup(const SpriteGroup *result);
void OptimiseVarAction2Adjust(VarAction2OptimiseState &state, const VarAction2AdjustInfo info, DeterministicSpriteGroup *group, DeterministicSpriteGroupAdjust &adjust);
void OptimiseVarAction2DeterministicSpriteGroup(VarAction2OptimiseState &state, const VarAction2AdjustInfo info, DeterministicSpriteGroup *group, std::vector &saved_adjusts);
void HandleVarAction2OptimisationPasses();
#endif /* NEWGRF_INTERNAL_H */