mirror of https://github.com/aap/librw.git
567 lines
12 KiB
C++
567 lines
12 KiB
C++
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <new>
|
|
|
|
#include "rwbase.h"
|
|
#include "rwerror.h"
|
|
#include "rwplg.h"
|
|
#include "rwpipeline.h"
|
|
#include "rwobjects.h"
|
|
#include "rwengine.h"
|
|
#include "ps2/rwps2.h"
|
|
#include "d3d/rwxbox.h"
|
|
#include "d3d/rwd3d.h"
|
|
#include "d3d/rwd3d8.h"
|
|
#include "d3d/rwd3d9.h"
|
|
#include "gl/rwgl3.h"
|
|
#include "gl/rwwdgl.h"
|
|
|
|
#define PLUGIN_ID 0
|
|
|
|
// on windows
|
|
//#ifdef DEBUG
|
|
//#include <crtdbg.h>
|
|
//#define free(p) _free_dbg(p, _NORMAL_BLOCK);
|
|
//#define malloc(sz) _malloc_dbg(sz, _NORMAL_BLOCK, __FILE__, __LINE__)
|
|
//#define realloc(p, sz) _realloc_dbg(p, sz, _NORMAL_BLOCK, __FILE__, __LINE__)
|
|
//#endif
|
|
|
|
namespace rw {
|
|
|
|
Engine *engine;
|
|
PluginList Engine::s_plglist(sizeof(Engine));
|
|
Engine::State Engine::state = Dead;
|
|
MemoryFunctions Engine::memfuncs;
|
|
PluginList Driver::s_plglist[NUM_PLATFORMS];
|
|
|
|
const char *allocLocation;
|
|
|
|
void *malloc_h(size_t sz, uint32 hint) { if(sz == 0) return nil; return malloc(sz); }
|
|
void *realloc_h(void *p, size_t sz, uint32 hint) { return realloc(p, sz); }
|
|
|
|
struct MemoryBlock
|
|
{
|
|
size_t sz;
|
|
uint32 hint;
|
|
void *origPtr;
|
|
const char *codeline;
|
|
LLLink inAllocList;
|
|
};
|
|
LinkList allocations;
|
|
size_t totalMemoryAllocated;
|
|
|
|
// We align managed memory blocks on a 16 byte boundary
|
|
|
|
#define ALIGN16(x) ((x) + 0xF & ~0xF)
|
|
void*
|
|
malloc_managed(size_t sz, uint32 hint)
|
|
{
|
|
void *origPtr;
|
|
uint8 *data;
|
|
MemoryBlock *mem;
|
|
|
|
if(sz == 0) return nil;
|
|
origPtr = malloc(sz + sizeof(MemoryBlock) + 15);
|
|
if(origPtr == nil)
|
|
return nil;
|
|
totalMemoryAllocated += sz;
|
|
data = (uint8*)origPtr;
|
|
data += sizeof(MemoryBlock);
|
|
data = (uint8*)ALIGN16((uintptr)data);
|
|
mem = (MemoryBlock*)(data-sizeof(MemoryBlock));
|
|
|
|
mem->sz = sz;
|
|
mem->hint = hint;
|
|
mem->origPtr = origPtr;
|
|
mem->codeline = allocLocation;
|
|
allocations.add(&mem->inAllocList);
|
|
|
|
return data;
|
|
}
|
|
|
|
void*
|
|
realloc_managed(void *p, size_t sz, uint32 hint)
|
|
{
|
|
void *origPtr;
|
|
MemoryBlock *mem;
|
|
uint32 offset;
|
|
|
|
if(p == nil)
|
|
return malloc_managed(sz, hint);
|
|
|
|
mem = (MemoryBlock*)((uint8*)p-sizeof(MemoryBlock));
|
|
offset = (uint8*)p - (uint8*)mem->origPtr;
|
|
|
|
mem->inAllocList.remove();
|
|
|
|
origPtr = realloc(mem->origPtr, sz + sizeof(MemoryBlock) + 15);
|
|
if(origPtr == nil){
|
|
allocations.add(&mem->inAllocList);
|
|
return nil;
|
|
}
|
|
p = (uint8*)origPtr + offset;
|
|
mem = (MemoryBlock*)((uint8*)p-sizeof(MemoryBlock));
|
|
totalMemoryAllocated -= mem->sz;
|
|
mem->sz = sz;
|
|
mem->hint = hint;
|
|
mem->origPtr = origPtr;
|
|
mem->codeline = allocLocation;
|
|
allocations.add(&mem->inAllocList);
|
|
totalMemoryAllocated += mem->sz;
|
|
|
|
return p;
|
|
}
|
|
|
|
void
|
|
free_managed(void *p)
|
|
{
|
|
MemoryBlock *mem;
|
|
if(p == nil)
|
|
return;
|
|
mem = (MemoryBlock*)((uint8*)p-sizeof(MemoryBlock));
|
|
mem->inAllocList.remove();
|
|
totalMemoryAllocated -= mem->sz;
|
|
free(mem->origPtr);
|
|
}
|
|
|
|
void
|
|
printleaks(void)
|
|
{
|
|
FORLIST(lnk, allocations){
|
|
MemoryBlock *mem = LLLinkGetData(lnk, MemoryBlock, inAllocList);
|
|
printf("sz %zu hint %X\n %s\n", mem->sz, mem->hint, mem->codeline);
|
|
}
|
|
}
|
|
|
|
// TODO: make the debug out configurable
|
|
void *mustmalloc_h(size_t sz, uint32 hint)
|
|
{
|
|
void *ret;
|
|
ret = Engine::memfuncs.rwmalloc(sz, hint);
|
|
if(ret || sz == 0)
|
|
return ret;
|
|
fprintf(stderr, "Error: out of memory\n");
|
|
exit(1);
|
|
return nil;
|
|
}
|
|
void *mustrealloc_h(void *p, size_t sz, uint32 hint)
|
|
{
|
|
void *ret;
|
|
ret = Engine::memfuncs.rwrealloc(p, sz, hint);
|
|
if(ret || sz == 0)
|
|
return ret;
|
|
fprintf(stderr, "Error: out of memory\n");
|
|
exit(1);
|
|
return nil;
|
|
}
|
|
|
|
char *strdup_LOC(const char *s, uint32 hint, const char *here) {
|
|
char *t;
|
|
size_t sz = strlen(s)+1;
|
|
t = (char*)malloc_LOC(sz, hint, here);
|
|
if(t)
|
|
memcpy(t, s, sz);
|
|
return t;
|
|
}
|
|
|
|
MemoryFunctions defaultMemfuncs = {
|
|
malloc_h,
|
|
realloc_h,
|
|
free,
|
|
nil,
|
|
nil
|
|
};
|
|
|
|
MemoryFunctions managedMemfuncs = {
|
|
malloc_managed,
|
|
realloc_managed,
|
|
free_managed,
|
|
nil,
|
|
nil
|
|
};
|
|
|
|
// This function mainly registers engine plugins
|
|
bool32
|
|
Engine::init(MemoryFunctions *memfuncs)
|
|
{
|
|
if(engine || Engine::state != Dead){
|
|
RWERROR((ERR_ENGINEINIT));
|
|
return 0;
|
|
}
|
|
|
|
totalMemoryAllocated = 0;
|
|
allocations.init();
|
|
|
|
if(memfuncs)
|
|
Engine::memfuncs = *memfuncs;
|
|
else
|
|
Engine::memfuncs = defaultMemfuncs;
|
|
|
|
if(Engine::memfuncs.rwmustmalloc == nil)
|
|
Engine::memfuncs.rwmustmalloc = mustmalloc_h;
|
|
if(Engine::memfuncs.rwmustrealloc == nil)
|
|
Engine::memfuncs.rwmustrealloc = mustrealloc_h;
|
|
|
|
PluginList::open();
|
|
|
|
for(uint i = 0; i < NUM_PLATFORMS; i++)
|
|
new (&Driver::s_plglist[i]) PluginList(sizeof(Driver));
|
|
|
|
// core plugin attach here
|
|
Frame::registerModule();
|
|
Image::registerModule();
|
|
Raster::registerModule();
|
|
Texture::registerModule();
|
|
|
|
// TODO: reset all allocation counts here. or maybe do that in modules?
|
|
Frame::numAllocated = 0;
|
|
Image::numAllocated = 0;
|
|
Raster::numAllocated = 0;
|
|
Texture::numAllocated = 0;
|
|
TexDictionary::numAllocated = 0;
|
|
Geometry::numAllocated = 0;
|
|
Material::numAllocated = 0;
|
|
Atomic::numAllocated = 0;
|
|
Light::numAllocated = 0;
|
|
Camera::numAllocated = 0;
|
|
Clump::numAllocated = 0;
|
|
World::numAllocated = 0;
|
|
|
|
// driver plugin attach
|
|
ps2::registerPlatformPlugins();
|
|
xbox::registerPlatformPlugins();
|
|
d3d8::registerPlatformPlugins();
|
|
d3d9::registerPlatformPlugins();
|
|
wdgl::registerPlatformPlugins();
|
|
gl3::registerPlatformPlugins();
|
|
|
|
Engine::state = Initialized;
|
|
return 1;
|
|
}
|
|
|
|
// This is where RW allocates the engine and e.g. opens d3d
|
|
bool32
|
|
Engine::open(EngineOpenParams *p)
|
|
{
|
|
if(engine || Engine::state != Initialized){
|
|
RWERROR((ERR_ENGINEOPEN));
|
|
return 0;
|
|
}
|
|
|
|
// Allocate engine
|
|
engine = (Engine*)rwNew(Engine::s_plglist.size, MEMDUR_GLOBAL);
|
|
engine->currentCamera = nil;
|
|
engine->currentWorld = nil;
|
|
|
|
// Initialize device
|
|
// Device and possibly OS specific!
|
|
#ifdef RW_PS2
|
|
engine->device = ps2::renderdevice;
|
|
#elif RW_GL3
|
|
engine->device = gl3::renderdevice;
|
|
#elif RW_D3D9
|
|
engine->device = d3d::renderdevice;
|
|
#else
|
|
engine->device = null::renderdevice;
|
|
#endif
|
|
|
|
engine->device.system(DEVICEOPEN, (void*)p, 0);
|
|
|
|
engine->dummyDefaultPipeline = ObjPipeline::create();
|
|
for(uint i = 0; i < NUM_PLATFORMS; i++){
|
|
rw::engine->driver[i] = (Driver*)rwNew(Driver::s_plglist[i].size,
|
|
MEMDUR_GLOBAL);
|
|
|
|
engine->driver[i]->defaultPipeline = engine->dummyDefaultPipeline;
|
|
|
|
engine->driver[i]->rasterCreate = null::rasterCreate;
|
|
engine->driver[i]->rasterLock = null::rasterLock;
|
|
engine->driver[i]->rasterUnlock = null::rasterUnlock;
|
|
engine->driver[i]->rasterLockPalette = null::rasterLockPalette;
|
|
engine->driver[i]->rasterUnlockPalette = null::rasterUnlockPalette;
|
|
engine->driver[i]->rasterNumLevels = null::rasterNumLevels;
|
|
engine->driver[i]->imageFindRasterFormat = null::imageFindRasterFormat;
|
|
engine->driver[i]->rasterFromImage = null::rasterFromImage;
|
|
engine->driver[i]->rasterToImage = null::rasterToImage;
|
|
}
|
|
|
|
Engine::state = Opened;
|
|
return 1;
|
|
}
|
|
|
|
// This is where RW creates the actual rendering device
|
|
// and calls the engine plugin ctors
|
|
bool32
|
|
Engine::start(void)
|
|
{
|
|
if(engine == nil || Engine::state != Opened){
|
|
RWERROR((ERR_ENGINESTART));
|
|
return 0;
|
|
}
|
|
|
|
engine->device.system(DEVICEINIT, nil, 0);
|
|
|
|
Engine::s_plglist.construct(engine);
|
|
for(uint i = 0; i < NUM_PLATFORMS; i++)
|
|
Driver::s_plglist[i].construct(rw::engine->driver[i]);
|
|
|
|
engine->device.system(DEVICEFINALIZE, nil, 0);
|
|
|
|
// Register some image formats. Or should we leave that to the user?
|
|
Image::registerFileFormat("tga", readTGA, writeTGA);
|
|
Image::registerFileFormat("bmp", readBMP, writeBMP);
|
|
Image::registerFileFormat("png", readPNG, writePNG);
|
|
|
|
Engine::state = Started;
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
Engine::term(void)
|
|
{
|
|
if(engine || Engine::state != Initialized){
|
|
RWERROR((ERR_GENERAL));
|
|
return;
|
|
}
|
|
|
|
PluginList::close();
|
|
|
|
// This has to be reset because it won't be opened again otherwise
|
|
// TODO: maybe reset more stuff here?
|
|
d3d::nativeRasterOffset = 0;
|
|
|
|
Engine::state = Dead;
|
|
}
|
|
|
|
void
|
|
Engine::close(void)
|
|
{
|
|
if(engine == nil || Engine::state != Opened){
|
|
RWERROR((ERR_GENERAL));
|
|
return;
|
|
}
|
|
|
|
engine->device.system(DEVICECLOSE, nil, 0);
|
|
for(uint i = 0; i < NUM_PLATFORMS; i++)
|
|
rwFree(rw::engine->driver[i]);
|
|
engine->dummyDefaultPipeline->destroy();
|
|
rwFree(engine);
|
|
engine = nil;
|
|
Engine::state = Initialized;
|
|
}
|
|
|
|
void
|
|
Engine::stop(void)
|
|
{
|
|
if(engine == nil || Engine::state != Started){
|
|
RWERROR((ERR_GENERAL));
|
|
return;
|
|
}
|
|
|
|
for(uint i = 0; i < NUM_PLATFORMS; i++)
|
|
Driver::s_plglist[i].destruct(rw::engine->driver[i]);
|
|
Engine::s_plglist.destruct(engine);
|
|
|
|
engine->device.system(DEVICETERM, nil, 0);
|
|
|
|
Engine::state = Opened;
|
|
}
|
|
|
|
|
|
int32
|
|
Engine::getNumSubSystems(void)
|
|
{
|
|
return engine->device.system(DEVICEGETNUMSUBSYSTEMS, nil, 0);
|
|
}
|
|
|
|
int32
|
|
Engine::getCurrentSubSystem(void)
|
|
{
|
|
return engine->device.system(DEVICEGETCURRENTSUBSYSTEM, nil, 0);
|
|
}
|
|
|
|
bool32
|
|
Engine::setSubSystem(int32 subsys)
|
|
{
|
|
return engine->device.system(DEVICESETSUBSYSTEM, nil, subsys);
|
|
}
|
|
|
|
SubSystemInfo*
|
|
Engine::getSubSystemInfo(SubSystemInfo *info, int32 subsys)
|
|
{
|
|
if(engine->device.system(DEVICEGETSUBSSYSTEMINFO, info, subsys))
|
|
return info;
|
|
return nil;
|
|
}
|
|
|
|
|
|
int32
|
|
Engine::getNumVideoModes(void)
|
|
{
|
|
return engine->device.system(DEVICEGETNUMVIDEOMODES, nil, 0);
|
|
}
|
|
|
|
int32
|
|
Engine::getCurrentVideoMode(void)
|
|
{
|
|
return engine->device.system(DEVICEGETCURRENTVIDEOMODE, nil, 0);
|
|
}
|
|
|
|
bool32
|
|
Engine::setVideoMode(int32 mode)
|
|
{
|
|
return engine->device.system(DEVICESETVIDEOMODE, nil, mode);
|
|
}
|
|
|
|
VideoMode*
|
|
Engine::getVideoModeInfo(VideoMode *info, int32 mode)
|
|
{
|
|
if(engine->device.system(DEVICEGETVIDEOMODEINFO, info, mode))
|
|
return info;
|
|
return nil;
|
|
}
|
|
|
|
|
|
uint32
|
|
Engine::getMaxMultiSamplingLevels(void)
|
|
{
|
|
return engine->device.system(DEVICEGETMAXMULTISAMPLINGLEVELS, nil, 0);
|
|
}
|
|
|
|
uint32
|
|
Engine::getMultiSamplingLevels(void)
|
|
{
|
|
return engine->device.system(DEVICEGETMULTISAMPLINGLEVELS, nil, 0);
|
|
}
|
|
|
|
bool32
|
|
Engine::setMultiSamplingLevels(uint32 levels)
|
|
{
|
|
return engine->device.system(DEVICESETMULTISAMPLINGLEVELS, nil, levels);
|
|
}
|
|
|
|
|
|
namespace null {
|
|
|
|
void beginUpdate(Camera*) { }
|
|
void endUpdate(Camera*) { }
|
|
void clearCamera(Camera*,RGBA*,uint32) { }
|
|
void showRaster(Raster*,uint32) { }
|
|
|
|
void setRenderState(int32, void*) { }
|
|
void *getRenderState(int32) { return 0; }
|
|
|
|
bool32 rasterRenderFast(Raster *raster, int32 x, int32 y) { return 0; }
|
|
|
|
void im2DRenderLine(void*, int32, int32, int32) { }
|
|
void im2DRenderTriangle(void*, int32, int32, int32, int32) { }
|
|
void im2DRenderPrimitive(PrimitiveType, void*, int32) { }
|
|
void im2DRenderIndexedPrimitive(PrimitiveType, void*, int32, void*, int32) { }
|
|
|
|
void im3DTransform(void *vertices, int32 numVertices, Matrix *world, uint32 flags) { }
|
|
void im3DRenderPrimitive(PrimitiveType primType) { }
|
|
void im3DRenderIndexedPrimitive(PrimitiveType primType, void *indices, int32 numIndices) { }
|
|
void im3DEnd(void) { }
|
|
|
|
Raster*
|
|
rasterCreate(Raster*)
|
|
{
|
|
assert(0 && "rasterCreate not implemented");
|
|
return nil;
|
|
}
|
|
|
|
uint8*
|
|
rasterLock(Raster*, int32, int32)
|
|
{
|
|
assert(0 && "lockRaster not implemented");
|
|
return nil;
|
|
}
|
|
|
|
void
|
|
rasterUnlock(Raster*, int32)
|
|
{
|
|
assert(0 && "unlockRaster not implemented");
|
|
}
|
|
|
|
uint8*
|
|
rasterLockPalette(Raster*, int32)
|
|
{
|
|
assert(0 && "rasterLockPalette not implemented");
|
|
return nil;
|
|
}
|
|
|
|
void
|
|
rasterUnlockPalette(Raster*)
|
|
{
|
|
assert(0 && "rasterUnlockPalette not implemented");
|
|
}
|
|
|
|
int32
|
|
rasterNumLevels(Raster*)
|
|
{
|
|
assert(0 && "rasterNumLevels not implemented");
|
|
return 0;
|
|
}
|
|
|
|
bool32
|
|
imageFindRasterFormat(Image *img, int32 type,
|
|
int32 *width, int32 *height, int32 *depth, int32 *format)
|
|
{
|
|
assert(0 && "imageFindRasterFormat not implemented");
|
|
return 0;
|
|
}
|
|
|
|
bool32
|
|
rasterFromImage(Raster*, Image*)
|
|
{
|
|
assert(0 && "rasterFromImage not implemented");
|
|
return 0;
|
|
}
|
|
|
|
Image*
|
|
rasterToImage(Raster*)
|
|
{
|
|
assert(0 && "rasterToImage not implemented");
|
|
return nil;
|
|
}
|
|
|
|
int
|
|
deviceSystem(DeviceReq req, void *arg0, int32 n)
|
|
{
|
|
switch(req){
|
|
case DEVICEGETNUMSUBSYSTEMS:
|
|
return 0;
|
|
case DEVICEGETCURRENTSUBSYSTEM:
|
|
return 0;
|
|
case DEVICEGETSUBSSYSTEMINFO:
|
|
return 0;
|
|
default: break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
Device renderdevice = {
|
|
0.0f, 1.0f,
|
|
null::beginUpdate,
|
|
null::endUpdate,
|
|
null::clearCamera,
|
|
null::showRaster,
|
|
null::rasterRenderFast,
|
|
null::setRenderState,
|
|
null::getRenderState,
|
|
null::im2DRenderLine,
|
|
null::im2DRenderTriangle,
|
|
null::im2DRenderPrimitive,
|
|
null::im2DRenderIndexedPrimitive,
|
|
null::im3DTransform,
|
|
null::im3DRenderPrimitive,
|
|
null::im3DRenderIndexedPrimitive,
|
|
null::im3DEnd,
|
|
null::deviceSystem
|
|
};
|
|
|
|
}
|
|
}
|