removed sigsegv handler, after profiling the overhead in querying the interval tree on each PVR write isn't significant enough to warrant the added complication

invalidate textures on palette writes
This commit is contained in:
Anthony Pesch 2015-09-18 20:55:52 -07:00
parent 265369f864
commit 0d455e710a
27 changed files with 298 additions and 695 deletions

View File

@ -112,9 +112,9 @@ set(DREAVM_SOURCES
src/hw/gdrom/gdrom.cc
src/hw/holly/holly.cc
src/hw/holly/pvr2.cc
src/hw/holly/texture_cache.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
@ -143,20 +143,11 @@ set(DREAVM_SOURCES
src/renderer/gl_shader.cc
src/sys/files.cc
src/sys/keys.cc
src/sys/sigsegv_handler.cc
src/sys/system.cc
src/trace/trace.cc
src/trace/trace_viewer.cc
src/main.cc)
if(WIN32)
list(APPEND DREAVM_SOURCES src/sys/sigsegv_handler_win.cc)
elseif(APPLE)
list(APPEND DREAVM_SOURCES src/sys/sigsegv_handler_mac.cc)
else()
list(APPEND DREAVM_SOURCES src/sys/sigsegv_handler_linux.cc)
endif()
# assign source groups for visual studio projects
source_group_by_dir(DREAVM_SOURCES)

View File

@ -7,6 +7,7 @@ using namespace dreavm;
using namespace dreavm::emu;
using namespace dreavm::hw;
using namespace dreavm::hw::gdrom;
using namespace dreavm::hw::holly;
using namespace dreavm::renderer;
using namespace dreavm::sys;
using namespace dreavm::trace;
@ -243,7 +244,12 @@ void Emulator::ToggleTracing() {
void Emulator::RenderFrame() {
rb_->BeginFrame();
dc_.ta()->RenderLastContext();
// render the last tile context
TileContext *last_context = dc_.ta()->GetLastContext();
if (last_context) {
dc_.tile_renderer()->RenderContext(last_context, rb_);
}
// render stats
char stats[512];

View File

@ -14,8 +14,7 @@
#define PROFILER_RUNTIME(name) \
MICROPROFILE_SCOPEI("runtime", name, dreavm::emu::Profiler::ScopeColor(name))
#define PROFILER_COUNT(name, count) \
MICROPROFILE_COUNTER_SET(name, count)
#define PROFILER_COUNT(name, count) MICROPROFILE_COUNTER_SET(name, count)
namespace dreavm {
namespace emu {

View File

@ -67,6 +67,8 @@ Dreamcast::Dreamcast()
pvr_ = new PVR2(this);
sh4_ = new SH4(*memory_, *runtime_);
ta_ = new TileAccelerator(this);
texcache_ = new TextureCache(this);
tile_renderer_ = new TileRenderer(*texcache_);
}
Dreamcast::~Dreamcast() {
@ -96,13 +98,11 @@ Dreamcast::~Dreamcast() {
delete pvr_;
delete sh4_;
delete ta_;
delete texcache_;
delete tile_renderer_;
}
bool Dreamcast::Init() {
if (!(sigsegv_ = SIGSEGVHandler::Install())) {
return false;
}
MapMemory();
if (!aica_->Init()) {
@ -133,6 +133,10 @@ bool Dreamcast::Init() {
return false;
}
if (!texcache_->Init()) {
return false;
}
scheduler_->AddDevice(aica_);
scheduler_->AddDevice(sh4_);
@ -223,7 +227,15 @@ void Dreamcast::MapMemory() {
nullptr, //
std::bind(&PVR2::WriteRegister32, pvr(), _1, _2), //
nullptr);
memory_->Mount(PVR_PALETTE_START, PVR_PALETTE_END, MIRROR_MASK, palette_ram_);
memory_->Handle(PVR_PALETTE_START, PVR_PALETTE_END, MIRROR_MASK,
nullptr, //
nullptr, //
nullptr, //
nullptr, //
nullptr, //
nullptr, //
std::bind(&PVR2::WritePalette32, pvr(), _1, _2), //
nullptr);
// ta
// TODO handle YUV transfers from 0x10800000 - 0x10ffffe0

View File

@ -5,7 +5,9 @@
#include "hw/gdrom/gdrom.h"
#include "hw/holly/holly.h"
#include "hw/holly/pvr2.h"
#include "hw/holly/texture_cache.h"
#include "hw/holly/tile_accelerator.h"
#include "hw/holly/tile_renderer.h"
#include "hw/maple/maple.h"
#include "hw/sh4/sh4.h"
#include "hw/memory.h"
@ -14,7 +16,6 @@
#include "jit/frontend/frontend.h"
#include "jit/runtime.h"
#include "renderer/backend.h"
#include "sys/sigsegv_handler.h"
#include "trace/trace.h"
namespace dreavm {
@ -116,8 +117,8 @@ class Dreamcast {
hw::holly::PVR2 *pvr() { return pvr_; }
hw::sh4::SH4 *sh4() { return sh4_; }
hw::holly::TileAccelerator *ta() { return ta_; }
sys::SIGSEGVHandler *sigsegv() { return sigsegv_; }
hw::holly::TextureCache *texcache() { return texcache_; }
hw::holly::TileRenderer *tile_renderer() { return tile_renderer_; }
renderer::Backend *rb() { return rb_; }
void set_rb(renderer::Backend *rb) { rb_ = rb; }
@ -174,9 +175,10 @@ class Dreamcast {
hw::holly::PVR2 *pvr_;
hw::sh4::SH4 *sh4_;
hw::holly::TileAccelerator *ta_;
hw::holly::TextureCache *texcache_;
hw::holly::TileRenderer *tile_renderer_;
// not owned by us
sys::SIGSEGVHandler *sigsegv_;
renderer::Backend *rb_;
trace::TraceWriter *trace_writer_;
};

View File

@ -154,7 +154,8 @@ void Holly::WriteRegister32(uint32_t addr, uint32_t value) {
case SB_PDEN_OFFSET:
case SB_PDST_OFFSET:
if (value) {
LOG_WARNING("Ignored palette DMA request");
// NOTE PVR DMA can invalidate texture cache
LOG_WARNING("Ignored PVR DMA request");
}
break;

View File

@ -17,7 +17,9 @@ bool PVR2::Init() {
scheduler_ = dc_->scheduler();
holly_ = dc_->holly();
ta_ = dc_->ta();
texcache_ = dc_->texcache();
pvr_regs_ = dc_->pvr_regs();
palette_ram_ = dc_->palette_ram();
video_ram_ = dc_->video_ram();
ReconfigureSPG();
@ -69,7 +71,7 @@ void PVR2::WriteRegister32(uint32_t addr, uint32_t value) {
fps_ = 1000000000.0f / delta.count();
}
ta_->SaveLastContext(dc_->PARAM_BASE.base_address);
ta_->SwapContext(dc_->PARAM_BASE.base_address);
} break;
case SPG_LOAD_OFFSET:
@ -79,6 +81,12 @@ void PVR2::WriteRegister32(uint32_t addr, uint32_t value) {
}
}
void PVR2::WritePalette32(uint32_t addr, uint32_t value) {
*reinterpret_cast<uint32_t *>(&palette_ram_[addr]) = value;
texcache_->CheckPaletteWrite(addr);
}
// the dreamcast has 8MB of vram, split into two 4MB banks, with two ways of
// accessing it:
// 0x04000000 -> 0x047fffff, 32-bit sequential access
@ -102,27 +110,36 @@ static uint32_t MAP64(uint32_t addr) {
uint8_t PVR2::ReadInterleaved8(uint32_t addr) {
addr = MAP64(addr);
return *reinterpret_cast<uint8_t *>(&video_ram_[addr]);
}
uint16_t PVR2::ReadInterleaved16(uint32_t addr) {
addr = MAP64(addr);
return *reinterpret_cast<uint16_t *>(&video_ram_[addr]);
}
uint32_t PVR2::ReadInterleaved32(uint32_t addr) {
addr = MAP64(addr);
return *reinterpret_cast<uint32_t *>(&video_ram_[addr]);
}
void PVR2::WriteInterleaved16(uint32_t addr, uint16_t value) {
addr = MAP64(addr);
*reinterpret_cast<uint16_t *>(&video_ram_[addr]) = value;
texcache_->CheckTextureWrite(addr);
}
void PVR2::WriteInterleaved32(uint32_t addr, uint32_t value) {
addr = MAP64(addr);
*reinterpret_cast<uint32_t *>(&video_ram_[addr]) = value;
texcache_->CheckTextureWrite(addr);
}
void PVR2::ReconfigureSPG() {

View File

@ -11,6 +11,7 @@ class Dreamcast;
namespace holly {
class Holly;
class TextureCache;
class TileAccelerator;
union PARAM_BASE_T {
@ -206,6 +207,8 @@ class PVR2 {
uint32_t ReadRegister32(uint32_t addr);
void WriteRegister32(uint32_t addr, uint32_t value);
void WritePalette32(uint32_t addr, uint32_t value);
uint8_t ReadInterleaved8(uint32_t addr);
uint16_t ReadInterleaved16(uint32_t addr);
uint32_t ReadInterleaved32(uint32_t addr);
@ -220,7 +223,9 @@ class PVR2 {
hw::Scheduler *scheduler_;
hw::holly::Holly *holly_;
hw::holly::TileAccelerator *ta_;
hw::holly::TextureCache *texcache_;
hw::Register *pvr_regs_;
uint8_t *palette_ram_;
uint8_t *video_ram_;
hw::TimerHandle line_timer_;

View File

@ -0,0 +1,153 @@
#include "core/core.h"
#include "emu/profiler.h"
#include "hw/dreamcast.h"
using namespace dreavm::hw;
using namespace dreavm::hw::holly;
using namespace dreavm::renderer;
TextureCache::TextureCache(Dreamcast *dc)
: dc_(dc), trace_writer_(nullptr), num_checks_(0), num_invalidated_(0) {}
bool TextureCache::Init() { return true; }
void TextureCache::CheckPaletteWrite(uint32_t offset) {
CheckWrite(PVR_PALETTE_START + offset);
}
void TextureCache::CheckTextureWrite(uint32_t offset) {
CheckWrite(PVR_VRAM32_START + offset);
}
TextureHandle TextureCache::GetTexture(const TSP &tsp, const TCW &tcw,
RegisterTextureCallback register_cb) {
TextureKey texture_key = TextureProvider::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 an an entry already exists
auto it = textures_.find(texture_key);
if (it != textures_.end()) {
return it->second.handle;
}
// 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_ram = dc_->palette_ram();
uint8_t *palette = nullptr;
uint32_t palette_addr = 0;
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_addr = (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_addr = ((tcw.p.palette_selector & 0x30) << 4) * 4;
palette_size = (1 << 8) * 4;
}
palette = &palette_ram[palette_addr];
}
// register and insert into the cache
TextureHandle handle = register_cb(palette, texture);
auto result =
textures_.insert(std::make_pair(texture_key, TextureEntry(handle)));
CHECK(result.second, "Texture already in the map?");
// add write watches in order to invalidate on future writes
TextureEntry &entry = result.first->second;
entry.texture_watch = watches_.Insert(
PVR_VRAM32_START + texture_addr,
PVR_VRAM32_START + texture_addr + texture_size - 1, texture_key);
if (palette) {
entry.palette_watch = watches_.Insert(
PVR_PALETTE_START + palette_addr,
PVR_PALETTE_START + palette_addr + palette_size - 1, texture_key);
}
PROFILER_COUNT("TextureCache watches", watches_.Size());
// add insert to trace
if (trace_writer_) {
trace_writer_->WriteInsertTexture(tsp, tcw, palette, palette_size, texture,
texture_size);
}
return handle;
}
void TextureCache::Clear() {
for (auto it = textures_.begin(), e = textures_.end(); it != e; ++it) {
Invalidate(it);
}
}
void TextureCache::CheckWrite(uint32_t addr) {
PROFILER_GPU("TextureCache::CheckWrite");
TextureWatchTree::Node *node = watches_.Find(addr, addr);
bool handled = node != nullptr;
while (node) {
Invalidate(node->value);
node = watches_.Find(addr, addr);
}
// monitor cache invalidation success
num_checks_++;
if (handled) {
num_invalidated_++;
}
PROFILER_COUNT("TextureCache num checks", num_checks_);
PROFILER_COUNT("TextureCache num invalidated", num_invalidated_);
}
void TextureCache::Invalidate(TextureKey texture_key) {
auto it = textures_.find(texture_key);
CHECK_NE(it, textures_.end());
Invalidate(it);
}
void TextureCache::Invalidate(TextureCacheMap::iterator it) {
TextureEntry &entry = it->second;
watches_.Remove(entry.texture_watch);
if (entry.palette_watch) {
watches_.Remove(entry.palette_watch);
}
dc_->rb()->FreeTexture(entry.handle);
textures_.erase(it);
}

View File

@ -0,0 +1,59 @@
#ifndef TILE_TEXTURE_CACHE_H
#define TILE_TEXTURE_CACHE_H
#include <unordered_map>
#include "core/interval_tree.h"
#include "hw/holly/tile_renderer.h"
#include "renderer/backend.h"
namespace dreavm {
namespace trace {
class TraceWriter;
}
namespace hw {
class Dreamcast;
namespace holly {
struct TextureEntry;
typedef std::unordered_map<TextureKey, TextureEntry> TextureCacheMap;
typedef IntervalTree<uint32_t, TextureKey> TextureWatchTree;
struct TextureEntry {
TextureEntry(renderer::TextureHandle handle)
: handle(handle), texture_watch(nullptr), palette_watch(nullptr) {}
renderer::TextureHandle handle;
TextureWatchTree::Node *texture_watch;
TextureWatchTree::Node *palette_watch;
};
class TextureCache : public TextureProvider {
public:
TextureCache(hw::Dreamcast *dc);
bool Init();
void CheckPaletteWrite(uint32_t offset);
void CheckTextureWrite(uint32_t offset);
renderer::TextureHandle GetTexture(const TSP &tsp, const TCW &tcw,
RegisterTextureCallback register_cb);
private:
void Clear();
void CheckWrite(uint32_t addr);
void Invalidate(TextureKey key);
void Invalidate(TextureCacheMap::iterator it);
hw::Dreamcast *dc_;
trace::TraceWriter *trace_writer_;
TextureWatchTree watches_;
TextureCacheMap textures_;
uint64_t num_checks_;
uint64_t num_invalidated_;
};
}
}
}
#endif

View File

@ -183,10 +183,7 @@ int TileAccelerator::GetVertexType(const PCW &pcw) {
}
TileAccelerator::TileAccelerator(Dreamcast *dc)
: dc_(dc),
texcache_(dc_),
tile_renderer_(texcache_),
last_context_(nullptr) {}
: dc_(dc), last_context_(nullptr) {}
TileAccelerator::~TileAccelerator() {
while (contexts_.size()) {
@ -202,6 +199,7 @@ TileAccelerator::~TileAccelerator() {
bool TileAccelerator::Init() {
memory_ = dc_->memory();
holly_ = dc_->holly();
texcache_ = dc_->texcache();
video_ram_ = dc_->video_ram();
return true;
@ -269,7 +267,7 @@ void TileAccelerator::WriteContext(uint32_t addr, uint32_t value) {
}
}
void TileAccelerator::SaveLastContext(uint32_t addr) {
void TileAccelerator::SwapContext(uint32_t addr) {
if (!last_context_) {
last_context_ = &scratch_context_;
}
@ -284,24 +282,18 @@ void TileAccelerator::SaveLastContext(uint32_t addr) {
WritePVRState(last_context_);
WriteBackgroundState(last_context_);
// add context to trace
if (dc_->trace_writer()) {
dc_->trace_writer()->WriteRenderContext(last_context_);
}
// tell holly that rendering is complete
holly_->RequestInterrupt(HOLLY_INTC_PCEOVINT);
holly_->RequestInterrupt(HOLLY_INTC_PCEOIINT);
holly_->RequestInterrupt(HOLLY_INTC_PCEOTINT);
}
void TileAccelerator::RenderLastContext() {
if (!last_context_) {
return;
}
tile_renderer_.RenderContext(last_context_, dc_->rb());
// add render to trace
if (dc_->trace_writer()) {
dc_->trace_writer()->WriteRenderContext(last_context_);
}
}
TileContext *TileAccelerator::GetLastContext() { return last_context_; }
void TileAccelerator::WriteCommand32(uint32_t addr, uint32_t value) {
WriteContext(dc_->TA_ISP_BASE.base_address, value);
@ -311,6 +303,8 @@ void TileAccelerator::WriteTexture32(uint32_t addr, uint32_t value) {
addr &= 0xeeffffff;
*reinterpret_cast<uint32_t *>(&video_ram_[addr]) = value;
texcache_->CheckTextureWrite(addr);
}
TileContextIterator TileAccelerator::FindContext(uint32_t addr) {

View File

@ -4,7 +4,6 @@
#include <memory>
#include <unordered_map>
#include "hw/holly/tile_renderer.h"
#include "hw/holly/tile_texture_cache.h"
#include "renderer/backend.h"
namespace dreavm {
@ -19,6 +18,7 @@ class Memory;
namespace holly {
class Holly;
class TextureCache;
enum {
// control params
@ -492,8 +492,8 @@ class TileAccelerator {
void SoftReset();
void InitContext(uint32_t addr);
void WriteContext(uint32_t addr, uint32_t value);
void SaveLastContext(uint32_t addr);
void RenderLastContext();
void SwapContext(uint32_t addr);
TileContext *GetLastContext();
void WriteCommand32(uint32_t addr, uint32_t value);
void WriteTexture32(uint32_t addr, uint32_t value);
@ -507,10 +507,9 @@ class TileAccelerator {
hw::Dreamcast *dc_;
hw::Memory *memory_;
hw::holly::Holly *holly_;
hw::holly::TextureCache *texcache_;
uint8_t *video_ram_;
TileTextureCache texcache_;
TileRenderer tile_renderer_;
TileContextMap contexts_;
TileContext scratch_context_;
TileContext *last_context_;

View File

@ -99,11 +99,12 @@ static inline uint32_t float_to_rgba(float r, float g, float b, float a) {
(float_to_u8(g) << 8) | float_to_u8(r);
}
TextureKey TextureCache::GetTextureKey(const TSP &tsp, const TCW &tcw) {
TextureKey TextureProvider::GetTextureKey(const TSP &tsp, const TCW &tcw) {
return ((uint64_t)tsp.full << 32) | tcw.full;
}
TileRenderer::TileRenderer(TextureCache &texcache) : texcache_(texcache) {}
TileRenderer::TileRenderer(TextureProvider &texture_provider)
: texture_provider_(texture_provider) {}
void TileRenderer::RenderContext(const TileContext *tactx, Backend *rb) {
PROFILER_GPU("TileRenderer::RenderContext");
@ -881,7 +882,7 @@ TextureHandle TileRenderer::RegisterTexture(const TileContext *tactx,
TextureHandle TileRenderer::GetTexture(const TileContext *tactx, Backend *rb,
const TSP &tsp, const TCW &tcw) {
return texcache_.GetTexture(
return texture_provider_.GetTexture(
tsp, tcw, [&](const uint8_t *palette, const uint8_t *texture) {
return RegisterTexture(tactx, rb, tsp, tcw, palette, texture);
});

View File

@ -24,10 +24,12 @@ typedef std::function<renderer::TextureHandle(const uint8_t *, const uint8_t *)>
typedef uint64_t TextureKey;
class TextureCache {
class TextureProvider {
public:
static TextureKey GetTextureKey(const TSP &tsp, const TCW &tcw);
virtual ~TextureProvider() {}
virtual renderer::TextureHandle GetTexture(
const TSP &tsp, const TCW &tcw, RegisterTextureCallback register_cb) = 0;
};
@ -40,7 +42,7 @@ enum { MAX_SURFACES = 0x10000, MAX_VERTICES = 0x10000 };
class TileRenderer {
public:
TileRenderer(TextureCache &texcache_);
TileRenderer(TextureProvider &texture_provider);
void RenderContext(const TileContext *tactx, renderer::Backend *rb);
@ -72,7 +74,7 @@ class TileRenderer {
renderer::Backend *rb, const TSP &tsp,
const TCW &tcw);
TextureCache &texcache_;
TextureProvider &texture_provider_;
// current global state
const PolyParam *last_poly_;

View File

@ -1,127 +0,0 @@
#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();
}

View File

@ -1,41 +0,0 @@
#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

View File

@ -1,75 +0,0 @@
#include "core/core.h"
#include "core/interval_tree.h"
#include "emu/profiler.h"
#include "sys/sigsegv_handler.h"
using namespace dreavm::sys;
SIGSEGVHandler *dreavm::sys::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; }
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));
// track number of watches
PROFILER_COUNT("WriteWatches", write_watches_.Size());
}
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);
}
// track number of watches
PROFILER_COUNT("WriteWatches", write_watches_.Size());
return handled;
}

View File

@ -1,61 +0,0 @@
#ifndef SIGSEGV_HANDLER_H
#define SIGSEGV_HANDLER_H
#include <functional>
#include <memory>
#include "core/interval_tree.h"
namespace dreavm {
namespace sys {
// 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

View File

@ -1,61 +0,0 @@
#include <sys/mman.h>
#include <signal.h>
#include <unistd.h>
#include "core/core.h"
#include "sys/sigsegv_handler_linux.h"
using namespace dreavm::sys;
SIGSEGVHandler *dreavm::sys::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;
}

View File

@ -1,24 +0,0 @@
#ifndef SIGSEGV_HANDLER_LINUX
#define SIGSEGV_HANDLER_LINUX
#include <thread>
#include "sys/sigsegv_handler.h"
namespace dreavm {
namespace sys {
class SIGSEGVHandlerLinux : public SIGSEGVHandler {
public:
~SIGSEGVHandlerLinux();
protected:
bool Init();
int GetPageSize();
bool Protect(void *ptr, int size, PageAccess access);
private:
};
}
}
#endif

View File

@ -1,141 +0,0 @@
#include <mach/mach.h>
#include <sys/mman.h>
#include <unistd.h>
#include "core/core.h"
#include "sys/sigsegv_handler_mac.h"
using namespace dreavm::sys;
SIGSEGVHandler *dreavm::sys::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;
}
}
}

View File

@ -1,28 +0,0 @@
#ifndef SIGSEGV_HANDLER_MAC
#define SIGSEGV_HANDLER_MAC
#include <thread>
#include "sys/sigsegv_handler.h"
namespace dreavm {
namespace sys {
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

View File

@ -1,56 +0,0 @@
#include <windows.h>
#include "core/core.h"
#include "sys/sigsegv_handler_win.h"
using namespace dreavm::sys;
SIGSEGVHandler *dreavm::sys::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);
}

View File

@ -1,24 +0,0 @@
#ifndef SIGSEGV_HANDLER_WIN
#define SIGSEGV_HANDLER_WIN
#include <thread>
#include "sys/sigsegv_handler.h"
namespace dreavm {
namespace sys {
class SIGSEGVHandlerWin : public SIGSEGVHandler {
public:
~SIGSEGVHandlerWin();
protected:
bool Init();
int GetPageSize();
bool Protect(void *ptr, int size, PageAccess access);
private:
};
}
}
#endif

View File

@ -114,7 +114,7 @@ bool TraceReader::PatchOverrides() {
while (cmd) {
switch (cmd->type) {
case TRACE_INSERT_TEXTURE: {
TextureKey texture_key = TextureCache::GetTextureKey(
TextureKey texture_key = TextureProvider::GetTextureKey(
cmd->insert_texture.tsp, cmd->insert_texture.tcw);
auto last_insert = last_inserts.find(texture_key);

View File

@ -14,19 +14,19 @@ using namespace dreavm::trace;
void TraceTextureCache::AddTexture(const TSP &tsp, TCW &tcw,
const uint8_t *palette,
const uint8_t *texture) {
TextureKey texture_key = TextureCache::GetTextureKey(tsp, tcw);
TextureKey texture_key = TextureProvider::GetTextureKey(tsp, tcw);
textures_[texture_key] =
TextureInst{tsp, tcw, palette, texture, (TextureHandle)0};
}
void TraceTextureCache::RemoveTexture(const TSP &tsp, TCW &tcw) {
TextureKey texture_key = TextureCache::GetTextureKey(tsp, tcw);
TextureKey texture_key = TextureProvider::GetTextureKey(tsp, tcw);
textures_.erase(texture_key);
}
TextureHandle TraceTextureCache::GetTexture(
const TSP &tsp, const TCW &tcw, RegisterTextureCallback register_cb) {
TextureKey texture_key = TextureCache::GetTextureKey(tsp, tcw);
TextureKey texture_key = TextureProvider::GetTextureKey(tsp, tcw);
auto it = textures_.find(texture_key);
CHECK_NE(it, textures_.end(), "Texture wasn't available in cache");

View File

@ -18,7 +18,7 @@ struct TextureInst {
renderer::TextureHandle handle;
};
class TraceTextureCache : public hw::holly::TextureCache {
class TraceTextureCache : public hw::holly::TextureProvider {
public:
void AddTexture(const hw::holly::TSP &tsp, hw::holly::TCW &tcw,
const uint8_t *palette, const uint8_t *texture);