mirror of https://github.com/inolen/redream.git
invalidate textures through process signals
This commit is contained in:
parent
b540e5b80b
commit
3897fe557f
|
@ -115,6 +115,7 @@ set(DREAVM_SOURCES
|
|||
src/hw/holly/pvr2.cc
|
||||
src/hw/holly/tile_accelerator.cc
|
||||
src/hw/holly/tile_renderer.cc
|
||||
src/hw/holly/tile_texture_cache.cc
|
||||
src/hw/maple/maple.cc
|
||||
src/hw/maple/maple_controller.cc
|
||||
src/hw/sh4/sh4.cc
|
||||
|
@ -141,12 +142,22 @@ set(DREAVM_SOURCES
|
|||
src/jit/runtime.cc
|
||||
src/renderer/gl_backend.cc
|
||||
src/renderer/gl_shader.cc
|
||||
src/sigsegv/sigsegv_handler.cc
|
||||
src/system/keys.cc
|
||||
src/system/system.cc
|
||||
src/trace/trace.cc
|
||||
src/trace/trace_viewer.cc
|
||||
src/main.cc)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND DREAVM_SOURCES src/sigsegv/sigsegv_handler_win.cc)
|
||||
elseif(APPLE)
|
||||
list(APPEND DREAVM_SOURCES src/sigsegv/sigsegv_handler_mac.cc)
|
||||
else()
|
||||
list(APPEND DREAVM_SOURCES src/sigsegv/sigsegv_handler_linux.cc)
|
||||
endif()
|
||||
|
||||
|
||||
# assign source groups for visual studio projects
|
||||
source_group_by_dir(DREAVM_SOURCES)
|
||||
|
||||
|
@ -277,6 +288,7 @@ endif()
|
|||
set(DREAVM_TEST_SOURCES
|
||||
${DREAVM_SOURCES}
|
||||
test/sh4_test.cc
|
||||
test/test_interval_tree.cc
|
||||
test/test_intrusive_list.cc
|
||||
test/test_ring_buffer.cc
|
||||
test/test_sh4.cc
|
||||
|
|
|
@ -5,11 +5,9 @@
|
|||
#include <gflags/gflags.h>
|
||||
#include "core/arena.h"
|
||||
#include "core/assert.h"
|
||||
#include "core/intrusive_list.h"
|
||||
#include "core/log.h"
|
||||
#include "core/math.h"
|
||||
#include "core/filesystem.h"
|
||||
#include "core/platform.h"
|
||||
#include "core/ring_buffer.h"
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,309 @@
|
|||
#ifndef INTERVAL_TREE_H
|
||||
#define INTERVAL_TREE_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "core/assert.h"
|
||||
|
||||
namespace dreavm {
|
||||
|
||||
// Interval tree implemented using a randomized bst. Based on implementation at
|
||||
// http://algs4.cs.princeton.edu/93intersection/IntervalST.java
|
||||
//
|
||||
// Parent pointers have been added in order to make removal by node (as opposed
|
||||
// to key) possible.
|
||||
|
||||
template <typename IT, typename VT>
|
||||
class IntervalTree {
|
||||
public:
|
||||
struct Node;
|
||||
|
||||
typedef IT interval_type;
|
||||
typedef VT value_type;
|
||||
typedef IntervalTree<interval_type, value_type> self_type;
|
||||
typedef std::function<void(const self_type &, Node *)> iterate_cb;
|
||||
|
||||
struct Node {
|
||||
Node(const interval_type &low, const interval_type &high,
|
||||
const value_type &value)
|
||||
: parent(nullptr),
|
||||
left(nullptr),
|
||||
right(nullptr),
|
||||
low(low),
|
||||
high(high),
|
||||
max(high),
|
||||
value(value),
|
||||
num(1) {}
|
||||
|
||||
bool operator<(const Node &rhs) const {
|
||||
return low < rhs.low || (low == rhs.low && high < rhs.high);
|
||||
}
|
||||
|
||||
Node *parent, *left, *right;
|
||||
interval_type low, high, max;
|
||||
value_type value;
|
||||
int num;
|
||||
};
|
||||
|
||||
IntervalTree() : root_(nullptr) {}
|
||||
|
||||
~IntervalTree() { Clear(); }
|
||||
|
||||
int Size() { return Size(root_); }
|
||||
|
||||
int Height() { return Height(root_); }
|
||||
|
||||
Node *Insert(const interval_type &low, const interval_type &high,
|
||||
const value_type &value) {
|
||||
Node *n = new Node(low, high, value);
|
||||
|
||||
SetRoot(RandomizedInsert(root_, n));
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void Remove(Node *n) {
|
||||
// join left and right subtrees, assign new joined subtree to parent
|
||||
Node *joined = Join(n->left, n->right);
|
||||
|
||||
if (!n->parent) {
|
||||
// removed node had no parent, must have been root
|
||||
CHECK_EQ(root_, n);
|
||||
SetRoot(joined);
|
||||
} else if (n->parent->left == n) {
|
||||
SetLeft(n->parent, joined);
|
||||
} else {
|
||||
SetRight(n->parent, joined);
|
||||
}
|
||||
|
||||
// fix up each node in the parent chain
|
||||
Node *parent = n->parent;
|
||||
while (parent) {
|
||||
FixCounts(parent);
|
||||
parent = parent->parent;
|
||||
}
|
||||
|
||||
// remove the node
|
||||
delete n;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
Clear(root_);
|
||||
|
||||
SetRoot(nullptr);
|
||||
}
|
||||
|
||||
Node *Find(interval_type low, interval_type high) {
|
||||
Node *n = root_;
|
||||
|
||||
while (n) {
|
||||
if (high >= n->low && n->high >= low) {
|
||||
return n;
|
||||
} else if (!n->left || n->left->max < low) {
|
||||
n = n->right;
|
||||
} else {
|
||||
n = n->left;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Iterate(interval_type low, interval_type high, iterate_cb cb) {
|
||||
Iterate(root_, low, high, cb);
|
||||
}
|
||||
|
||||
private:
|
||||
int Size(Node *n) { return n ? n->num : 0; }
|
||||
|
||||
int Height(Node *n) {
|
||||
return n ? 1 + std::max(Height(n->left), Height(n->right)) : 0;
|
||||
}
|
||||
|
||||
//
|
||||
// insertion
|
||||
//
|
||||
Node *RootInsert(Node *root, Node *n) {
|
||||
if (!root) {
|
||||
return n;
|
||||
}
|
||||
|
||||
if (*n < *root) {
|
||||
SetLeft(root, RootInsert(root->left, n));
|
||||
root = RotateRight(root);
|
||||
} else {
|
||||
SetRight(root, RootInsert(root->right, n));
|
||||
root = RotateLeft(root);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
Node *RandomizedInsert(Node *root, Node *n) {
|
||||
if (!root) {
|
||||
return n;
|
||||
}
|
||||
|
||||
// make new node the root with uniform probability
|
||||
if (rand() % (Size(root) + 1) == 0) {
|
||||
return RootInsert(root, n);
|
||||
}
|
||||
|
||||
if (*n < *root) {
|
||||
SetLeft(root, RandomizedInsert(root->left, n));
|
||||
} else {
|
||||
SetRight(root, RandomizedInsert(root->right, n));
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
//
|
||||
// removal
|
||||
//
|
||||
void Clear(Node *n) {
|
||||
if (!n) {
|
||||
return;
|
||||
}
|
||||
|
||||
Clear(n->left);
|
||||
Clear(n->right);
|
||||
|
||||
delete n;
|
||||
}
|
||||
|
||||
//
|
||||
// iteration
|
||||
//
|
||||
bool Iterate(Node *n, interval_type low, interval_type high, iterate_cb cb) {
|
||||
if (!n) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool found1 = false;
|
||||
bool found2 = false;
|
||||
bool found3 = false;
|
||||
|
||||
if (high >= n->low && n->high >= low) {
|
||||
cb(*this, n);
|
||||
found1 = true;
|
||||
}
|
||||
|
||||
if (n->left && n->left->max >= low) {
|
||||
found2 = Iterate(n->left, low, high, cb);
|
||||
}
|
||||
|
||||
if (found2 || !n->left || n->left->max < low) {
|
||||
found3 = Iterate(n->right, low, high, cb);
|
||||
}
|
||||
|
||||
return found1 || found2 || found3;
|
||||
}
|
||||
|
||||
//
|
||||
// helper methods
|
||||
//
|
||||
void SetRoot(Node *n) {
|
||||
root_ = n;
|
||||
|
||||
if (root_) {
|
||||
root_->parent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SetLeft(Node *parent, Node *n) {
|
||||
parent->left = n;
|
||||
|
||||
if (parent->left) {
|
||||
parent->left->parent = parent;
|
||||
}
|
||||
|
||||
FixCounts(parent);
|
||||
}
|
||||
|
||||
void SetRight(Node *parent, Node *n) {
|
||||
parent->right = n;
|
||||
|
||||
if (parent->right) {
|
||||
parent->right->parent = parent;
|
||||
}
|
||||
|
||||
FixCounts(parent);
|
||||
}
|
||||
|
||||
void FixCounts(Node *n) {
|
||||
if (!n) {
|
||||
return;
|
||||
}
|
||||
|
||||
n->num = 1 + Size(n->left) + Size(n->right);
|
||||
n->max = n->high;
|
||||
if (n->left) {
|
||||
n->max = std::max(n->max, n->left->max);
|
||||
}
|
||||
if (n->right) {
|
||||
n->max = std::max(n->max, n->right->max);
|
||||
}
|
||||
}
|
||||
|
||||
Node *RotateRight(Node *root) {
|
||||
Node *parent = root->parent;
|
||||
Node *n = root->left;
|
||||
|
||||
SetLeft(root, n->right);
|
||||
SetRight(n, root);
|
||||
|
||||
if (parent) {
|
||||
if (parent->left == root) {
|
||||
SetLeft(parent, n);
|
||||
} else {
|
||||
SetRight(parent, n);
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
Node *RotateLeft(Node *root) {
|
||||
Node *parent = root->parent;
|
||||
Node *n = root->right;
|
||||
|
||||
SetRight(root, n->left);
|
||||
SetLeft(n, root);
|
||||
|
||||
if (parent) {
|
||||
if (parent->left == root) {
|
||||
SetLeft(parent, n);
|
||||
} else {
|
||||
SetRight(parent, n);
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
Node *Join(Node *a, Node *b) {
|
||||
if (!a) {
|
||||
return b;
|
||||
} else if (!b) {
|
||||
return a;
|
||||
}
|
||||
|
||||
if ((rand() % (Size(a) + Size(b))) < Size(a)) {
|
||||
SetRight(a, Join(a->right, b));
|
||||
return a;
|
||||
} else {
|
||||
SetLeft(b, Join(a, b->left));
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
Node *root_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -16,6 +16,7 @@ using namespace dreavm::jit::backend::interpreter;
|
|||
using namespace dreavm::jit::backend::x64;
|
||||
using namespace dreavm::jit::frontend::sh4;
|
||||
using namespace dreavm::renderer;
|
||||
using namespace dreavm::sigsegv;
|
||||
using namespace dreavm::trace;
|
||||
|
||||
Dreamcast::Dreamcast()
|
||||
|
@ -43,16 +44,16 @@ Dreamcast::Dreamcast()
|
|||
#include "hw/holly/pvr2_regs.inc"
|
||||
#undef PVR_REG
|
||||
|
||||
aica_regs_ = new uint8_t[AICA_REG_SIZE]();
|
||||
bios_ = new uint8_t[BIOS_SIZE]();
|
||||
expdev_mem_ = new uint8_t[EXPDEV_SIZE]();
|
||||
flash_ = new uint8_t[FLASH_SIZE]();
|
||||
modem_mem_ = new uint8_t[MODEM_REG_SIZE]();
|
||||
palette_ram_ = new uint8_t[PVR_PALETTE_SIZE]();
|
||||
ram_ = new uint8_t[MAIN_RAM_SIZE]();
|
||||
unassigned_ = new uint8_t[UNASSIGNED_SIZE]();
|
||||
modem_mem_ = new uint8_t[MODEM_REG_SIZE]();
|
||||
aica_regs_ = new uint8_t[AICA_REG_SIZE]();
|
||||
wave_ram_ = new uint8_t[WAVE_RAM_SIZE]();
|
||||
expdev_mem_ = new uint8_t[EXPDEV_SIZE]();
|
||||
video_ram_ = new uint8_t[PVR_VRAM32_SIZE]();
|
||||
palette_ram_ = new uint8_t[PVR_PALETTE_SIZE]();
|
||||
wave_ram_ = new uint8_t[WAVE_RAM_SIZE]();
|
||||
|
||||
scheduler_ = new Scheduler();
|
||||
memory_ = new Memory();
|
||||
|
@ -98,6 +99,10 @@ Dreamcast::~Dreamcast() {
|
|||
}
|
||||
|
||||
bool Dreamcast::Init() {
|
||||
if (!(sigsegv_ = SIGSEGVHandler::Install())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MapMemory();
|
||||
|
||||
if (!aica_->Init()) {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "jit/frontend/frontend.h"
|
||||
#include "jit/runtime.h"
|
||||
#include "renderer/backend.h"
|
||||
#include "sigsegv/sigsegv_handler.h"
|
||||
#include "trace/trace.h"
|
||||
|
||||
namespace dreavm {
|
||||
|
@ -116,6 +117,8 @@ class Dreamcast {
|
|||
hw::sh4::SH4 *sh4() { return sh4_; }
|
||||
hw::holly::TileAccelerator *ta() { return ta_; }
|
||||
|
||||
sigsegv::SIGSEGVHandler *sigsegv() { return sigsegv_; }
|
||||
|
||||
renderer::Backend *rb() { return rb_; }
|
||||
void set_rb(renderer::Backend *rb) { rb_ = rb; }
|
||||
|
||||
|
@ -148,16 +151,16 @@ class Dreamcast {
|
|||
private:
|
||||
void MapMemory();
|
||||
|
||||
uint8_t *aica_regs_;
|
||||
uint8_t *bios_;
|
||||
uint8_t *expdev_mem_;
|
||||
uint8_t *flash_;
|
||||
uint8_t *modem_mem_;
|
||||
uint8_t *palette_ram_;
|
||||
uint8_t *ram_;
|
||||
uint8_t *unassigned_;
|
||||
uint8_t *modem_mem_;
|
||||
uint8_t *aica_regs_;
|
||||
uint8_t *wave_ram_;
|
||||
uint8_t *expdev_mem_;
|
||||
uint8_t *video_ram_;
|
||||
uint8_t *palette_ram_;
|
||||
uint8_t *wave_ram_;
|
||||
|
||||
hw::Memory *memory_;
|
||||
hw::Scheduler *scheduler_;
|
||||
|
@ -173,6 +176,7 @@ class Dreamcast {
|
|||
hw::holly::TileAccelerator *ta_;
|
||||
|
||||
// not owned by us
|
||||
sigsegv::SIGSEGVHandler *sigsegv_;
|
||||
renderer::Backend *rb_;
|
||||
trace::TraceWriter *trace_writer_;
|
||||
};
|
||||
|
|
|
@ -164,95 +164,6 @@ static int GetVertexType_raw(const PCW &pcw) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
TileTextureCache::TileTextureCache(Dreamcast *dc)
|
||||
: dc_(dc), trace_writer_(nullptr) {}
|
||||
|
||||
void TileTextureCache::Clear() {
|
||||
for (auto it : textures_) {
|
||||
if (!it.second) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dc_->rb()->FreeTexture(it.second);
|
||||
}
|
||||
|
||||
textures_.clear();
|
||||
}
|
||||
|
||||
// void TileTextureCache::RemoveTexture(uint32_t addr) {
|
||||
// auto it = textures_.find(addr);
|
||||
// if (it == textures_.end()) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// TextureHandle handle = it->second;
|
||||
// dc_->rb()->FreeTexture(handle);
|
||||
// textures_.erase(it);
|
||||
// }
|
||||
|
||||
TextureHandle TileTextureCache::GetTexture(
|
||||
const TSP &tsp, const TCW &tcw, RegisterTextureCallback register_cb) {
|
||||
TextureKey texture_key = TextureCache::GetTextureKey(tsp, tcw);
|
||||
|
||||
// if the trace writer has changed, clear the cache to force insert events
|
||||
if (dc_->trace_writer() != trace_writer_) {
|
||||
Clear();
|
||||
trace_writer_ = dc_->trace_writer();
|
||||
}
|
||||
|
||||
// see if we already have an entry
|
||||
auto it = textures_.find(texture_key);
|
||||
if (it != textures_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// TCW texture_addr field is in 64-bit units
|
||||
uint32_t texture_addr = tcw.texture_addr << 3;
|
||||
|
||||
// get the texture data
|
||||
const uint8_t *video_ram = dc_->video_ram();
|
||||
const uint8_t *texture = &video_ram[texture_addr];
|
||||
int width = 8 << tsp.texture_u_size;
|
||||
int height = 8 << tsp.texture_v_size;
|
||||
int element_size_bits = tcw.pixel_format == TA_PIXEL_8BPP
|
||||
? 8
|
||||
: tcw.pixel_format == TA_PIXEL_4BPP ? 4 : 16;
|
||||
int texture_size = (width * height * element_size_bits) >> 3;
|
||||
|
||||
// get the palette data
|
||||
const uint8_t *palette = dc_->palette_ram();
|
||||
int palette_size = 0;
|
||||
|
||||
if (tcw.pixel_format == TA_PIXEL_4BPP || tcw.pixel_format == TA_PIXEL_8BPP) {
|
||||
// palette ram is 4096 bytes, with each palette entry being 4 bytes each,
|
||||
// resulting in 1 << 10 indexes
|
||||
if (tcw.pixel_format == TA_PIXEL_4BPP) {
|
||||
// in 4bpp mode, the palette selector represents the upper 6 bits of the
|
||||
// palette index, with the remaining 4 bits being filled in by the texture
|
||||
palette += (tcw.p.palette_selector << 4) * 4;
|
||||
palette_size = (1 << 4) * 4;
|
||||
} else if (tcw.pixel_format == TA_PIXEL_8BPP) {
|
||||
// in 4bpp mode, the palette selector represents the upper 2 bits of the
|
||||
// palette index, with the remaining 8 bits being filled in by the texture
|
||||
palette += ((tcw.p.palette_selector & 0x30) << 4) * 4;
|
||||
palette_size = (1 << 8) * 4;
|
||||
}
|
||||
}
|
||||
|
||||
// register and insert into the cache
|
||||
TextureHandle handle = register_cb(palette, texture);
|
||||
auto result = textures_.insert(std::make_pair(texture_key, handle));
|
||||
CHECK(result.second, "Texture already in the map?");
|
||||
|
||||
// add insert to trace
|
||||
if (trace_writer_) {
|
||||
trace_writer_->WriteInsertTexture(tsp, tcw, palette, palette_size, texture,
|
||||
texture_size);
|
||||
}
|
||||
|
||||
return result.first->second;
|
||||
}
|
||||
|
||||
int TileAccelerator::GetParamSize(const PCW &pcw, int vertex_type) {
|
||||
int size =
|
||||
param_size_lookup[pcw.obj_control * TA_NUM_PARAMS * TA_NUM_VERT_TYPES +
|
||||
|
@ -399,9 +310,6 @@ void TileAccelerator::WriteCommand32(uint32_t addr, uint32_t value) {
|
|||
void TileAccelerator::WriteTexture32(uint32_t addr, uint32_t value) {
|
||||
addr &= 0xeeffffff;
|
||||
|
||||
// // FIXME this is terrible
|
||||
// texcache_.RemoveTexture(addr);
|
||||
|
||||
*reinterpret_cast<uint32_t *>(&video_ram_[addr]) = value;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include "hw/holly/tile_renderer.h"
|
||||
#include "hw/holly/tile_texture_cache.h"
|
||||
#include "renderer/backend.h"
|
||||
|
||||
namespace dreavm {
|
||||
|
@ -474,22 +475,6 @@ struct TileContext {
|
|||
int vertex_type;
|
||||
};
|
||||
|
||||
class TileTextureCache : public TextureCache {
|
||||
public:
|
||||
TileTextureCache(hw::Dreamcast *dc);
|
||||
|
||||
void Clear();
|
||||
// void RemoveTexture(uint32_t addr);
|
||||
renderer::TextureHandle GetTexture(const TSP &tsp, const TCW &tcw,
|
||||
RegisterTextureCallback register_cb);
|
||||
|
||||
private:
|
||||
hw::Dreamcast *dc_;
|
||||
|
||||
trace::TraceWriter *trace_writer_;
|
||||
std::unordered_map<TextureKey, renderer::TextureHandle> textures_;
|
||||
};
|
||||
|
||||
typedef std::unordered_map<TextureKey, TileContext *> TileContextMap;
|
||||
typedef TileContextMap::iterator TileContextIterator;
|
||||
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
#include "core/core.h"
|
||||
#include "hw/dreamcast.h"
|
||||
|
||||
using namespace dreavm::hw;
|
||||
using namespace dreavm::hw::holly;
|
||||
using namespace dreavm::renderer;
|
||||
|
||||
TileTextureCache::TileTextureCache(Dreamcast *dc)
|
||||
: dc_(dc), trace_writer_(nullptr) {}
|
||||
|
||||
TextureHandle TileTextureCache::GetTexture(
|
||||
const TSP &tsp, const TCW &tcw, RegisterTextureCallback register_cb) {
|
||||
TextureKey texture_key = TextureCache::GetTextureKey(tsp, tcw);
|
||||
|
||||
// if there are any pending removals, do so at this time
|
||||
if (pending_invalidations_.size()) {
|
||||
ClearPending();
|
||||
}
|
||||
|
||||
// if the trace writer has changed, clear the cache to force insert events
|
||||
if (dc_->trace_writer() != trace_writer_) {
|
||||
ClearAll();
|
||||
trace_writer_ = dc_->trace_writer();
|
||||
}
|
||||
|
||||
// see if an an entry already exists
|
||||
auto it = textures_.find(texture_key);
|
||||
if (it != textures_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// TCW texture_addr field is in 64-bit units
|
||||
uint32_t texture_addr = tcw.texture_addr << 3;
|
||||
|
||||
// get the texture data
|
||||
uint8_t *video_ram = dc_->video_ram();
|
||||
uint8_t *texture = &video_ram[texture_addr];
|
||||
int width = 8 << tsp.texture_u_size;
|
||||
int height = 8 << tsp.texture_v_size;
|
||||
int element_size_bits = tcw.pixel_format == TA_PIXEL_8BPP
|
||||
? 8
|
||||
: tcw.pixel_format == TA_PIXEL_4BPP ? 4 : 16;
|
||||
int texture_size = (width * height * element_size_bits) >> 3;
|
||||
|
||||
// get the palette data
|
||||
uint8_t *palette = nullptr;
|
||||
int palette_size = 0;
|
||||
|
||||
if (tcw.pixel_format == TA_PIXEL_4BPP || tcw.pixel_format == TA_PIXEL_8BPP) {
|
||||
// palette ram is 4096 bytes, with each palette entry being 4 bytes each,
|
||||
// resulting in 1 << 10 indexes
|
||||
if (tcw.pixel_format == TA_PIXEL_4BPP) {
|
||||
// in 4bpp mode, the palette selector represents the upper 6 bits of the
|
||||
// palette index, with the remaining 4 bits being filled in by the texture
|
||||
palette = dc_->palette_ram() + (tcw.p.palette_selector << 4) * 4;
|
||||
palette_size = (1 << 4) * 4;
|
||||
} else if (tcw.pixel_format == TA_PIXEL_8BPP) {
|
||||
// in 4bpp mode, the palette selector represents the upper 2 bits of the
|
||||
// palette index, with the remaining 8 bits being filled in by the texture
|
||||
palette = dc_->palette_ram() + ((tcw.p.palette_selector & 0x30) << 4) * 4;
|
||||
palette_size = (1 << 8) * 4;
|
||||
}
|
||||
}
|
||||
|
||||
// register and insert into the cache
|
||||
TextureHandle handle = register_cb(palette, texture);
|
||||
auto result = textures_.insert(std::make_pair(texture_key, handle));
|
||||
CHECK(result.second, "Texture already in the map?");
|
||||
|
||||
// add write callback in order to invalidate on future writes. the callback
|
||||
// address will be page aligned, therefore it will be triggered falsely in
|
||||
// some cases. over invalidate in these cases
|
||||
TileTextureCacheMap::value_type *map_entry = &(*result.first);
|
||||
|
||||
auto callback = [](void *ctx, void *data) {
|
||||
TileTextureCache *texcache = reinterpret_cast<TileTextureCache *>(ctx);
|
||||
TileTextureCacheMap::value_type *map_entry =
|
||||
reinterpret_cast<TileTextureCacheMap::value_type *>(data);
|
||||
TextureKey texture_key = map_entry->first;
|
||||
|
||||
// add to pending invalidation list (can't remove inside of signal handler)
|
||||
texcache->pending_invalidations_.insert(texture_key);
|
||||
};
|
||||
|
||||
dc_->sigsegv()->AddWriteWatch(texture, texture_size, callback, this,
|
||||
map_entry);
|
||||
|
||||
// TODO generate id for watch, so it can be cleared by both callbacks
|
||||
// if (palette) {
|
||||
// dc_->mmio()->AddWriteWatch(palette, palette_size, callback, this,
|
||||
// map_entry);
|
||||
// }
|
||||
|
||||
// add insert to trace
|
||||
if (trace_writer_) {
|
||||
trace_writer_->WriteInsertTexture(tsp, tcw, palette, palette_size, texture,
|
||||
texture_size);
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void TileTextureCache::ClearPending() {
|
||||
for (auto texture_key : pending_invalidations_) {
|
||||
auto it = textures_.find(texture_key);
|
||||
CHECK_NE(it, textures_.end());
|
||||
|
||||
TextureHandle handle = it->second;
|
||||
textures_.erase(it);
|
||||
|
||||
dc_->rb()->FreeTexture(handle);
|
||||
}
|
||||
|
||||
pending_invalidations_.clear();
|
||||
}
|
||||
|
||||
void TileTextureCache::ClearAll() {
|
||||
for (auto it : textures_) {
|
||||
TextureHandle handle = it.second;
|
||||
|
||||
dc_->rb()->FreeTexture(handle);
|
||||
}
|
||||
|
||||
textures_.clear();
|
||||
|
||||
pending_invalidations_.clear();
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef TILE_TEXTURE_CACHE_H
|
||||
#define TILE_TEXTURE_CACHE_H
|
||||
|
||||
#include <set>
|
||||
#include "hw/holly/tile_renderer.h"
|
||||
#include "renderer/backend.h"
|
||||
|
||||
namespace dreavm {
|
||||
namespace trace {
|
||||
class TraceWriter;
|
||||
}
|
||||
|
||||
namespace hw {
|
||||
class Dreamcast;
|
||||
|
||||
namespace holly {
|
||||
|
||||
typedef std::unordered_map<TextureKey, renderer::TextureHandle>
|
||||
TileTextureCacheMap;
|
||||
|
||||
class TileTextureCache : public TextureCache {
|
||||
public:
|
||||
TileTextureCache(hw::Dreamcast *dc);
|
||||
|
||||
renderer::TextureHandle GetTexture(const TSP &tsp, const TCW &tcw,
|
||||
RegisterTextureCallback register_cb);
|
||||
|
||||
private:
|
||||
void ClearPending();
|
||||
void ClearAll();
|
||||
|
||||
hw::Dreamcast *dc_;
|
||||
trace::TraceWriter *trace_writer_;
|
||||
TileTextureCacheMap textures_;
|
||||
std::set<TextureKey> pending_invalidations_;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <unordered_map>
|
||||
#include "core/core.h"
|
||||
#include "core/intrusive_list.h"
|
||||
|
||||
namespace dreavm {
|
||||
namespace jit {
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
#include "core/core.h"
|
||||
#include "core/interval_tree.h"
|
||||
#include "sigsegv/sigsegv_handler.h"
|
||||
|
||||
using namespace dreavm::sigsegv;
|
||||
|
||||
SIGSEGVHandler *dreavm::sigsegv::SIGSEGVHandler::global_handler_ = nullptr;
|
||||
|
||||
SIGSEGVHandler *SIGSEGVHandler::Install() {
|
||||
if (global_handler_) {
|
||||
return global_handler_;
|
||||
}
|
||||
|
||||
global_handler_ = CreateSIGSEGVHandler();
|
||||
|
||||
if (!global_handler_->Init()) {
|
||||
LOG_WARNING("Failed to install SIGSEGV handler");
|
||||
|
||||
delete global_handler_;
|
||||
global_handler_ = nullptr;
|
||||
}
|
||||
|
||||
return global_handler_;
|
||||
}
|
||||
|
||||
SIGSEGVHandler::~SIGSEGVHandler() { global_handler_ = nullptr; }
|
||||
|
||||
// TODO track handler counts with microprofile
|
||||
|
||||
void SIGSEGVHandler::AddWriteWatch(void *ptr, int size,
|
||||
WriteWatchHandler handler, void *ctx,
|
||||
void *data) {
|
||||
int page_size = GetPageSize();
|
||||
uintptr_t physical_start = dreavm::align(reinterpret_cast<uintptr_t>(ptr),
|
||||
static_cast<uintptr_t>(page_size));
|
||||
uintptr_t physical_end =
|
||||
dreavm::align(reinterpret_cast<uintptr_t>(ptr) + size,
|
||||
static_cast<uintptr_t>(page_size));
|
||||
|
||||
// write-protect the pages
|
||||
CHECK(Protect(reinterpret_cast<void *>(physical_start),
|
||||
static_cast<int>(physical_end - physical_start), ACC_READONLY));
|
||||
|
||||
write_watches_.Insert(
|
||||
physical_start, physical_end - 1,
|
||||
WriteWatch(handler, ctx, data, physical_start, physical_end));
|
||||
}
|
||||
|
||||
bool SIGSEGVHandler::HandleAccessFault(uintptr_t rip, uintptr_t fault_addr) {
|
||||
WatchTree::Node *node = write_watches_.Find(fault_addr, fault_addr);
|
||||
|
||||
bool handled = node != nullptr;
|
||||
|
||||
while (node) {
|
||||
WriteWatch &watch = node->value;
|
||||
|
||||
watch.handler(watch.ctx, watch.data);
|
||||
|
||||
// remove write-protection
|
||||
CHECK(Protect(reinterpret_cast<void *>(watch.physical_start),
|
||||
static_cast<int>(watch.physical_end - watch.physical_start),
|
||||
ACC_READWRITE));
|
||||
|
||||
write_watches_.Remove(node);
|
||||
|
||||
node = write_watches_.Find(fault_addr, fault_addr);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
#ifndef SIGSEGV_HANDLER_H
|
||||
#define SIGSEGV_HANDLER_H
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include "core/interval_tree.h"
|
||||
|
||||
namespace dreavm {
|
||||
namespace sigsegv {
|
||||
|
||||
// implemented in the platform specific souce file
|
||||
class SIGSEGVHandler;
|
||||
extern SIGSEGVHandler *CreateSIGSEGVHandler();
|
||||
|
||||
enum PageAccess { ACC_READONLY, ACC_READWRITE };
|
||||
|
||||
typedef std::function<void(void *, void *)> WriteWatchHandler;
|
||||
|
||||
struct WriteWatch {
|
||||
WriteWatch(WriteWatchHandler handler, void *ctx, void *data,
|
||||
uintptr_t physical_start, uintptr_t physical_end)
|
||||
: handler(handler),
|
||||
ctx(ctx),
|
||||
data(data),
|
||||
physical_start(physical_start),
|
||||
physical_end(physical_end) {}
|
||||
|
||||
WriteWatchHandler handler;
|
||||
void *ctx;
|
||||
void *data;
|
||||
uintptr_t physical_start;
|
||||
uintptr_t physical_end;
|
||||
};
|
||||
|
||||
typedef IntervalTree<uintptr_t, WriteWatch> WatchTree;
|
||||
|
||||
class SIGSEGVHandler {
|
||||
public:
|
||||
static SIGSEGVHandler *global_handler() { return global_handler_; }
|
||||
|
||||
static SIGSEGVHandler *Install();
|
||||
|
||||
virtual ~SIGSEGVHandler();
|
||||
|
||||
void AddWriteWatch(void *ptr, int size, WriteWatchHandler handler, void *ctx,
|
||||
void *data);
|
||||
bool HandleAccessFault(uintptr_t rip, uintptr_t fault_addr);
|
||||
|
||||
protected:
|
||||
static SIGSEGVHandler *global_handler_;
|
||||
|
||||
virtual bool Init() = 0;
|
||||
virtual int GetPageSize() = 0;
|
||||
virtual bool Protect(void *ptr, int size, PageAccess access) = 0;
|
||||
|
||||
WatchTree write_watches_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,61 @@
|
|||
#include <sys/mman.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include "core/core.h"
|
||||
#include "sigsegv/sigsegv_handler_linux.h"
|
||||
|
||||
using namespace dreavm::sigsegv;
|
||||
|
||||
SIGSEGVHandler *dreavm::sigsegv::CreateSIGSEGVHandler() {
|
||||
return new SIGSEGVHandlerLinux();
|
||||
}
|
||||
|
||||
static struct sigaction old_sa;
|
||||
|
||||
static void SignalHandler(int signo, siginfo_t *info, void *ctx) {
|
||||
ucontext_t *uctx = reinterpret_cast<ucontext_t *>(ctx);
|
||||
|
||||
uintptr_t rip = uctx->uc_mcontext.gregs[REG_RIP];
|
||||
uintptr_t fault_addr = reinterpret_cast<uintptr_t>(info->si_addr);
|
||||
bool handled =
|
||||
SIGSEGVHandler::global_handler()->HandleAccessFault(rip, fault_addr);
|
||||
|
||||
if (!handled) {
|
||||
// call into the original handler if the installed handler fails to handle
|
||||
// the signal
|
||||
(*old_sa.sa_sigaction)(signo, info, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
SIGSEGVHandlerLinux::~SIGSEGVHandlerLinux() {
|
||||
sigaction(SIGSEGV, &old_sa, nullptr);
|
||||
}
|
||||
|
||||
bool SIGSEGVHandlerLinux::Init() {
|
||||
struct sigaction new_sa;
|
||||
new_sa.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&new_sa.sa_mask);
|
||||
new_sa.sa_sigaction = &SignalHandler;
|
||||
|
||||
if (sigaction(SIGSEGV, &new_sa, &old_sa) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int SIGSEGVHandlerLinux::GetPageSize() { return getpagesize(); }
|
||||
|
||||
bool SIGSEGVHandlerLinux::Protect(void *ptr, int size, PageAccess access) {
|
||||
int prot = PROT_NONE;
|
||||
switch (access) {
|
||||
case ACC_READONLY:
|
||||
prot = PROT_READ;
|
||||
break;
|
||||
case ACC_READWRITE:
|
||||
prot = PROT_READ | PROT_WRITE;
|
||||
break;
|
||||
}
|
||||
|
||||
return mprotect(ptr, size, prot) == 0;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef SIGSEGV_HANDLER_LINUX
|
||||
#define SIGSEGV_HANDLER_LINUX
|
||||
|
||||
#include <thread>
|
||||
#include "sigsegv/sigsegv_handler.h"
|
||||
|
||||
namespace dreavm {
|
||||
namespace sigsegv {
|
||||
|
||||
class SIGSEGVHandlerLinux : public SIGSEGVHandler {
|
||||
public:
|
||||
~SIGSEGVHandlerLinux();
|
||||
|
||||
protected:
|
||||
bool Init();
|
||||
int GetPageSize();
|
||||
bool Protect(void *ptr, int size, PageAccess access);
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,141 @@
|
|||
#include <mach/mach.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include "core/core.h"
|
||||
#include "sigsegv/sigsegv_handler_mac.h"
|
||||
|
||||
using namespace dreavm::sigsegv;
|
||||
|
||||
SIGSEGVHandler *dreavm::sigsegv::CreateSIGSEGVHandler() {
|
||||
return new SIGSEGVHandlerMac();
|
||||
}
|
||||
|
||||
// http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/exc_server.html
|
||||
extern "C" boolean_t exc_server(mach_msg_header_t *request_msg,
|
||||
mach_msg_header_t *reply_msg);
|
||||
|
||||
// http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/catch_exception_raise.html
|
||||
extern "C" kern_return_t catch_exception_raise(
|
||||
mach_port_t exception_port, mach_port_t thread, mach_port_t task,
|
||||
exception_type_t exception, exception_data_t code,
|
||||
mach_msg_type_number_t code_count) {
|
||||
// get exception state
|
||||
mach_msg_type_number_t state_count = x86_EXCEPTION_STATE64_COUNT;
|
||||
x86_exception_state64_t exc_state;
|
||||
if (thread_get_state(thread, x86_EXCEPTION_STATE64,
|
||||
reinterpret_cast<thread_state_t>(&exc_state),
|
||||
&state_count) != KERN_SUCCESS) {
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
|
||||
// get thread state
|
||||
state_count = x86_THREAD_STATE64_COUNT;
|
||||
x86_thread_state64_t thread_state;
|
||||
if (thread_get_state(thread, x86_THREAD_STATE64,
|
||||
reinterpret_cast<thread_state_t>(&thread_state),
|
||||
&state_count) != KERN_SUCCESS) {
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
|
||||
uintptr_t rip = thread_state.__rip;
|
||||
uintptr_t fault_addr = exc_state.__faultvaddr;
|
||||
bool handled =
|
||||
SIGSEGVHandler::global_handler()->HandleAccessFault(rip, fault_addr);
|
||||
if (!handled) {
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
|
||||
// reset thread state
|
||||
if (thread_set_state(thread, x86_THREAD_STATE64,
|
||||
reinterpret_cast<thread_state_t>(&thread_state),
|
||||
state_count) != KERN_SUCCESS) {
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
SIGSEGVHandlerMac::~SIGSEGVHandlerMac() {
|
||||
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, 0,
|
||||
EXCEPTION_DEFAULT, 0);
|
||||
mach_port_deallocate(mach_task_self(), listen_port_);
|
||||
}
|
||||
|
||||
bool SIGSEGVHandlerMac::Init() {
|
||||
// allocate port to listen for exceptions
|
||||
if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
|
||||
&listen_port_) != KERN_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_port_insert_right.html
|
||||
if (mach_port_insert_right(mach_task_self(), listen_port_, listen_port_,
|
||||
MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// filter out any exception other than EXC_BAD_ACCESS
|
||||
// http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_set_exception_ports.html
|
||||
if (task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS,
|
||||
listen_port_, EXCEPTION_DEFAULT,
|
||||
MACHINE_THREAD_STATE) != KERN_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// launch thread to listen for exceptions
|
||||
thread_ = std::thread([this] { ThreadEntry(); });
|
||||
thread_.detach();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int SIGSEGVHandlerMac::GetPageSize() { return getpagesize(); }
|
||||
|
||||
bool SIGSEGVHandlerMac::Protect(void *ptr, int size, PageAccess access) {
|
||||
int prot = PROT_NONE;
|
||||
switch (access) {
|
||||
case ACC_READONLY:
|
||||
prot = PROT_READ;
|
||||
break;
|
||||
case ACC_READWRITE:
|
||||
prot = PROT_READ | PROT_WRITE;
|
||||
break;
|
||||
}
|
||||
|
||||
return mprotect(ptr, size, prot) == 0;
|
||||
}
|
||||
|
||||
void SIGSEGVHandlerMac::ThreadEntry() {
|
||||
while (true) {
|
||||
struct {
|
||||
mach_msg_header_t head;
|
||||
mach_msg_body_t msgh_body;
|
||||
char data[1024];
|
||||
} msg;
|
||||
struct {
|
||||
mach_msg_header_t head;
|
||||
char data[1024];
|
||||
} reply;
|
||||
|
||||
// wait for a message on the exception port
|
||||
mach_msg_return_t ret =
|
||||
mach_msg(&msg.head, MACH_RCV_MSG | MACH_RCV_LARGE, 0, sizeof(msg),
|
||||
listen_port_, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
if (ret != MACH_MSG_SUCCESS) {
|
||||
LOG_INFO("mach_msg receive failed with %d %s", ret,
|
||||
mach_error_string(ret));
|
||||
break;
|
||||
}
|
||||
|
||||
// call exc_server, which will call back into catch_exception_raise
|
||||
exc_server(&msg.head, &reply.head);
|
||||
|
||||
// send the reply
|
||||
ret = mach_msg(&reply.head, MACH_SEND_MSG, reply.head.msgh_size, 0,
|
||||
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
if (ret != MACH_MSG_SUCCESS) {
|
||||
LOG_INFO("mach_msg send failed with %d %s", ret, mach_error_string(ret));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef SIGSEGV_HANDLER_MAC
|
||||
#define SIGSEGV_HANDLER_MAC
|
||||
|
||||
#include <thread>
|
||||
#include "sigsegv/sigsegv_handler.h"
|
||||
|
||||
namespace dreavm {
|
||||
namespace sigsegv {
|
||||
|
||||
class SIGSEGVHandlerMac : public SIGSEGVHandler {
|
||||
public:
|
||||
~SIGSEGVHandlerMac();
|
||||
|
||||
protected:
|
||||
bool Init();
|
||||
int GetPageSize();
|
||||
bool Protect(void *ptr, int size, PageAccess access);
|
||||
|
||||
private:
|
||||
void ThreadEntry();
|
||||
|
||||
mach_port_t listen_port_;
|
||||
std::thread thread_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,56 @@
|
|||
#include <windows.h>
|
||||
#include "core/core.h"
|
||||
#include "sigsegv/sigsegv_handler_win.h"
|
||||
|
||||
using namespace dreavm::sigsegv;
|
||||
|
||||
SIGSEGVHandler *dreavm::sigsegv::CreateSIGSEGVHandler() {
|
||||
return new SIGSEGVHandlerWin();
|
||||
}
|
||||
|
||||
static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ex_info) {
|
||||
auto code = ex_info->ExceptionRecord->ExceptionCode;
|
||||
if (code != STATUS_ACCESS_VIOLATION) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
uintptr_t rip = ex_info->ContextRecord->Rip;
|
||||
uintptr_t fault_addr = ex_info->ExceptionRecord->ExceptionInformation[1];
|
||||
bool handled =
|
||||
SIGSEGVHandler::global_handler()->HandleAccessFault(rip, fault_addr);
|
||||
|
||||
if (!handled) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
SIGSEGVHandlerWin::~SIGSEGVHandlerWin() {
|
||||
RemoveVectoredExceptionHandler(ExceptionHandler);
|
||||
}
|
||||
|
||||
bool SIGSEGVHandlerWin::Init() {
|
||||
return AddVectoredExceptionHandler(1, ExceptionHandler) != nullptr;
|
||||
}
|
||||
|
||||
int SIGSEGVHandlerWin::GetPageSize() {
|
||||
SYSTEM_INFO system_info;
|
||||
GetSystemInfo(&system_info);
|
||||
return system_info.dwPageSize;
|
||||
}
|
||||
|
||||
bool SIGSEGVHandlerWin::Protect(void *ptr, int size, PageAccess access) {
|
||||
DWORD new_protect = PAGE_NOACCESS;
|
||||
DWORD old_protect;
|
||||
switch (access) {
|
||||
case ACC_READONLY:
|
||||
new_protect = PAGE_READONLY;
|
||||
break;
|
||||
case ACC_READWRITE:
|
||||
new_protect = PAGE_READWRITE;
|
||||
break;
|
||||
}
|
||||
|
||||
return VirtualProtect(ptr, size, new_protect, &old_protect);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef SIGSEGV_HANDLER_WIN
|
||||
#define SIGSEGV_HANDLER_WIN
|
||||
|
||||
#include <thread>
|
||||
#include "sigsegv/sigsegv_handler.h"
|
||||
|
||||
namespace dreavm {
|
||||
namespace sigsegv {
|
||||
|
||||
class SIGSEGVHandlerWin : public SIGSEGVHandler {
|
||||
public:
|
||||
~SIGSEGVHandlerWin();
|
||||
|
||||
protected:
|
||||
bool Init();
|
||||
int GetPageSize();
|
||||
bool Protect(void *ptr, int size, PageAccess access);
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,150 @@
|
|||
#include <math.h>
|
||||
#include <set>
|
||||
#include "gtest/gtest.h"
|
||||
#include "core/core.h"
|
||||
#include "core/interval_tree.h"
|
||||
|
||||
using namespace dreavm;
|
||||
|
||||
typedef IntervalTree<uint32_t, void *> TestTree;
|
||||
|
||||
class IntervalTreeTest : public ::testing::Test {
|
||||
public:
|
||||
IntervalTreeTest() {}
|
||||
|
||||
static const int LOW = 0x0;
|
||||
static const int HIGH = 0x10000;
|
||||
static const int INTERVAL = 0x2000;
|
||||
|
||||
void SetUp() {
|
||||
// insert dummy intervals
|
||||
for (int i = 0; i < 0x10000; i++) {
|
||||
uint32_t low = 0;
|
||||
uint32_t high = 0;
|
||||
|
||||
while (high <= low) {
|
||||
low = LOW + (rand() % (HIGH - LOW));
|
||||
high = low + INTERVAL;
|
||||
}
|
||||
|
||||
TestTree::Node *n = intervals.Insert(low, high, nullptr);
|
||||
nodes.insert(n);
|
||||
}
|
||||
}
|
||||
|
||||
TestTree intervals;
|
||||
std::set<TestTree::Node *> nodes;
|
||||
};
|
||||
|
||||
TEST_F(IntervalTreeTest, ValidateRelations) {
|
||||
// make sure all children parent pointers match
|
||||
for (auto n : nodes) {
|
||||
if (n->left) {
|
||||
ASSERT_EQ(n->left->parent, n);
|
||||
}
|
||||
if (n->right) {
|
||||
ASSERT_EQ(n->right->parent, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(IntervalTreeTest, Size) {
|
||||
ASSERT_EQ(intervals.Size(), (int)nodes.size());
|
||||
}
|
||||
|
||||
TEST_F(IntervalTreeTest, Height) {
|
||||
// make sure height is within expected range of ~2*log2(n)
|
||||
int height = intervals.Height();
|
||||
int size = intervals.Size();
|
||||
ASSERT_TRUE(height >= log2(size) && height < (3 * log2(size)));
|
||||
}
|
||||
|
||||
TEST_F(IntervalTreeTest, Remove) {
|
||||
// remove all results and ensure size is updated in the process
|
||||
int size = nodes.size();
|
||||
|
||||
for (auto n : nodes) {
|
||||
intervals.Remove(n);
|
||||
|
||||
ASSERT_EQ(intervals.Size(), --size);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(IntervalTreeTest, Clear) {
|
||||
int original_size = intervals.Size();
|
||||
|
||||
intervals.Clear();
|
||||
|
||||
ASSERT_NE(original_size, intervals.Size());
|
||||
ASSERT_EQ(0, intervals.Size());
|
||||
}
|
||||
|
||||
TEST_F(IntervalTreeTest, Find) {
|
||||
for (uint32_t i = 0; i < HIGH; i += 0x1000) {
|
||||
// manually generate a list of results
|
||||
std::set<TestTree::Node *> expected;
|
||||
|
||||
for (auto n : nodes) {
|
||||
if (i < n->low || i > n->high) {
|
||||
continue;
|
||||
}
|
||||
|
||||
expected.insert(n);
|
||||
}
|
||||
|
||||
// query the tree for nodes and compare with the expected results
|
||||
int found = 0;
|
||||
TestTree::Node *n = intervals.Find(i, i);
|
||||
|
||||
while (n) {
|
||||
// validate that it's in the expected set
|
||||
auto it = expected.find(n);
|
||||
ASSERT_NE(it, expected.end());
|
||||
found++;
|
||||
|
||||
// remove from nodes so the node isn't expected by the next loop iteration
|
||||
auto it2 = nodes.find(n);
|
||||
ASSERT_NE(it2, nodes.end());
|
||||
nodes.erase(it2);
|
||||
|
||||
// remove from intervals so Find() can locate the next node
|
||||
intervals.Remove(n);
|
||||
|
||||
// locate the next node
|
||||
n = intervals.Find(i, i);
|
||||
}
|
||||
|
||||
// validate that the same number of nodes were matched
|
||||
ASSERT_EQ(found, (int)expected.size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(IntervalTreeTest, Iterate) {
|
||||
for (uint32_t i = 0; i < HIGH; i += 0x1000) {
|
||||
// manually generate a list of expected nodes
|
||||
std::set<TestTree::Node *> expected;
|
||||
|
||||
for (auto n : nodes) {
|
||||
if (i < n->low || i > n->high) {
|
||||
continue;
|
||||
}
|
||||
|
||||
expected.insert(n);
|
||||
}
|
||||
|
||||
// query the tree for nodes
|
||||
std::set<TestTree::Node *> results;
|
||||
|
||||
intervals.Iterate(i, i, [&](const TestTree &tree, TestTree::Node *node) {
|
||||
results.insert(node);
|
||||
});
|
||||
|
||||
// compare the results
|
||||
ASSERT_EQ(expected.size(), results.size());
|
||||
|
||||
for (auto n : results) {
|
||||
auto it = expected.find(n);
|
||||
ASSERT_NE(it, expected.end());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
#include "gtest/gtest.h"
|
||||
#include "core/core.h"
|
||||
#include "core/intrusive_list.h"
|
||||
|
||||
using namespace dreavm;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "gtest/gtest.h"
|
||||
#include "core/core.h"
|
||||
#include "core/ring_buffer.h"
|
||||
|
||||
using namespace dreavm;
|
||||
|
||||
|
|
Loading…
Reference in New Issue