2010-08-09 13:28:56 +00:00
|
|
|
#undef interface
|
|
|
|
#define interface struct
|
|
|
|
#include <d3d9.h>
|
|
|
|
#include <d3dx9.h>
|
|
|
|
#undef interface
|
|
|
|
|
|
|
|
#define D3DVERTEX (D3DFVF_XYZRHW | D3DFVF_TEX1)
|
|
|
|
|
2013-05-02 11:25:45 +00:00
|
|
|
typedef HRESULT (__stdcall* EffectProc)(LPDIRECT3DDEVICE9, LPCVOID, UINT, D3DXMACRO const*, LPD3DXINCLUDE, DWORD, LPD3DXEFFECTPOOL, LPD3DXEFFECT*, LPD3DXBUFFER*);
|
|
|
|
typedef HRESULT (__stdcall* TextureProc)(LPDIRECT3DDEVICE9, LPCTSTR, LPDIRECT3DTEXTURE9*);
|
2010-08-09 13:28:56 +00:00
|
|
|
|
2015-06-20 05:44:05 +00:00
|
|
|
struct VideoD3D : Video {
|
|
|
|
~VideoD3D() { term(); }
|
2010-08-09 13:28:56 +00:00
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
LPDIRECT3D9 lpd3d = nullptr;
|
|
|
|
LPDIRECT3DDEVICE9 device = nullptr;
|
|
|
|
LPDIRECT3DVERTEXBUFFER9 vertex_buffer = nullptr;
|
|
|
|
LPDIRECT3DVERTEXBUFFER9* vertex_ptr = nullptr;
|
2013-05-02 11:25:45 +00:00
|
|
|
D3DPRESENT_PARAMETERS presentation;
|
|
|
|
D3DSURFACE_DESC d3dsd;
|
|
|
|
D3DLOCKED_RECT d3dlr;
|
|
|
|
D3DRASTER_STATUS d3drs;
|
|
|
|
D3DCAPS9 d3dcaps;
|
2015-06-12 13:14:38 +00:00
|
|
|
LPDIRECT3DTEXTURE9 texture = nullptr;
|
|
|
|
LPDIRECT3DSURFACE9 surface = nullptr;
|
|
|
|
LPD3DXEFFECT effect = nullptr;
|
2013-05-02 11:25:45 +00:00
|
|
|
string shader_source_markup;
|
2010-08-09 13:28:56 +00:00
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
bool lost = true;
|
|
|
|
unsigned iwidth;
|
|
|
|
unsigned iheight;
|
2010-08-09 13:28:56 +00:00
|
|
|
|
|
|
|
struct d3dvertex {
|
|
|
|
float x, y, z, rhw; //screen coords
|
|
|
|
float u, v; //texture coords
|
|
|
|
};
|
|
|
|
|
|
|
|
struct {
|
|
|
|
uint32_t t_usage, v_usage;
|
|
|
|
uint32_t t_pool, v_pool;
|
|
|
|
uint32_t lock;
|
|
|
|
uint32_t filter;
|
|
|
|
} flags;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
bool dynamic; //device supports dynamic textures
|
|
|
|
bool shader; //device supports pixel shaders
|
|
|
|
} caps;
|
|
|
|
|
|
|
|
struct {
|
2015-06-12 13:14:38 +00:00
|
|
|
HWND handle = nullptr;
|
|
|
|
bool synchronize = false;
|
|
|
|
unsigned filter = Video::FilterLinear;
|
2010-08-09 13:28:56 +00:00
|
|
|
|
|
|
|
unsigned width;
|
|
|
|
unsigned height;
|
|
|
|
} settings;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
unsigned width;
|
|
|
|
unsigned height;
|
|
|
|
} state;
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto cap(const string& name) -> bool {
|
2010-08-09 13:28:56 +00:00
|
|
|
if(name == Video::Handle) return true;
|
|
|
|
if(name == Video::Synchronize) return true;
|
|
|
|
if(name == Video::Filter) return true;
|
2013-04-09 13:31:46 +00:00
|
|
|
if(name == Video::Shader) return false;
|
2010-08-09 13:28:56 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto get(const string& name) -> any {
|
2010-08-09 13:28:56 +00:00
|
|
|
if(name == Video::Handle) return (uintptr_t)settings.handle;
|
|
|
|
if(name == Video::Synchronize) return settings.synchronize;
|
|
|
|
if(name == Video::Filter) return settings.filter;
|
2015-06-12 13:14:38 +00:00
|
|
|
return {};
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto set(const string& name, const any& value) -> bool {
|
|
|
|
if(name == Video::Handle && value.is<uintptr_t>()) {
|
|
|
|
settings.handle = (HWND)value.get<uintptr_t>();
|
2010-08-09 13:28:56 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
if(name == Video::Synchronize && value.is<bool>()) {
|
|
|
|
settings.synchronize = value.get<bool>();
|
2010-08-09 13:28:56 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
if(name == Video::Filter && value.is<unsigned>()) {
|
|
|
|
settings.filter = value.get<unsigned>();
|
2010-08-09 13:28:56 +00:00
|
|
|
if(lpd3d) update_filter();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
if(name == Video::Shader && value.is<string>()) {
|
2013-04-09 13:31:46 +00:00
|
|
|
return false;
|
2015-06-12 13:14:38 +00:00
|
|
|
//set_shader(value.get<string>());
|
|
|
|
//return true;
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto recover() -> bool {
|
2010-08-09 13:28:56 +00:00
|
|
|
if(!device) return false;
|
|
|
|
|
|
|
|
if(lost) {
|
|
|
|
release_resources();
|
|
|
|
if(device->Reset(&presentation) != D3D_OK) return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
lost = false;
|
|
|
|
|
|
|
|
device->SetDialogBoxMode(false);
|
|
|
|
|
|
|
|
device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
|
|
|
|
device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
|
|
|
device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
|
|
|
|
|
|
|
|
device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
|
|
|
|
device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
|
|
|
|
device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
|
|
|
|
|
|
|
|
device->SetRenderState(D3DRS_LIGHTING, false);
|
|
|
|
device->SetRenderState(D3DRS_ZENABLE, false);
|
|
|
|
device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
|
|
|
|
|
|
|
device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
|
|
|
device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
|
|
|
device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
|
|
|
|
|
|
|
|
device->SetVertexShader(NULL);
|
|
|
|
device->SetFVF(D3DVERTEX);
|
|
|
|
|
|
|
|
device->CreateVertexBuffer(sizeof(d3dvertex) * 4, flags.v_usage, D3DVERTEX, (D3DPOOL)flags.v_pool, &vertex_buffer, NULL);
|
|
|
|
iwidth = 0;
|
|
|
|
iheight = 0;
|
|
|
|
resize(settings.width = 256, settings.height = 256);
|
|
|
|
update_filter();
|
|
|
|
clear();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto rounded_power_of_two(unsigned n) -> unsigned {
|
2010-08-09 13:28:56 +00:00
|
|
|
n--;
|
|
|
|
n |= n >> 1;
|
|
|
|
n |= n >> 2;
|
|
|
|
n |= n >> 4;
|
|
|
|
n |= n >> 8;
|
|
|
|
n |= n >> 16;
|
|
|
|
return n + 1;
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto resize(unsigned width, unsigned height) -> void {
|
2010-08-09 13:28:56 +00:00
|
|
|
if(iwidth >= width && iheight >= height) return;
|
|
|
|
|
|
|
|
iwidth = rounded_power_of_two(max(width, iwidth ));
|
|
|
|
iheight = rounded_power_of_two(max(height, iheight));
|
|
|
|
|
|
|
|
if(d3dcaps.MaxTextureWidth < iwidth || d3dcaps.MaxTextureWidth < iheight) {
|
|
|
|
//TODO: attempt to handle this more gracefully
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(texture) texture->Release();
|
|
|
|
device->CreateTexture(iwidth, iheight, 1, flags.t_usage, D3DFMT_X8R8G8B8, (D3DPOOL)flags.t_pool, &texture, NULL);
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto update_filter() -> void {
|
2010-08-09 13:28:56 +00:00
|
|
|
if(!device) return;
|
|
|
|
if(lost && !recover()) return;
|
|
|
|
|
2013-05-02 11:25:45 +00:00
|
|
|
flags.filter = (settings.filter == Video::FilterNearest ? D3DTEXF_POINT : D3DTEXF_LINEAR);
|
2010-08-09 13:28:56 +00:00
|
|
|
device->SetSamplerState(0, D3DSAMP_MINFILTER, flags.filter);
|
|
|
|
device->SetSamplerState(0, D3DSAMP_MAGFILTER, flags.filter);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Vertex format:
|
|
|
|
//
|
|
|
|
// 0----------1
|
|
|
|
// | /|
|
|
|
|
// | / |
|
|
|
|
// | / |
|
|
|
|
// | / |
|
|
|
|
// | / |
|
|
|
|
// 2----------3
|
|
|
|
//
|
|
|
|
// (x,y) screen coords, in pixels
|
|
|
|
// (u,v) texture coords, betweeen 0.0 (top, left) to 1.0 (bottom, right)
|
2015-06-12 13:14:38 +00:00
|
|
|
auto set_vertex(
|
2010-08-09 13:28:56 +00:00
|
|
|
uint32_t px, uint32_t py, uint32_t pw, uint32_t ph,
|
|
|
|
uint32_t tw, uint32_t th,
|
|
|
|
uint32_t x, uint32_t y, uint32_t w, uint32_t h
|
2015-06-12 13:14:38 +00:00
|
|
|
) -> void {
|
2010-08-09 13:28:56 +00:00
|
|
|
d3dvertex vertex[4];
|
|
|
|
vertex[0].x = vertex[2].x = (double)(x - 0.5);
|
|
|
|
vertex[1].x = vertex[3].x = (double)(x + w - 0.5);
|
|
|
|
vertex[0].y = vertex[1].y = (double)(y - 0.5);
|
|
|
|
vertex[2].y = vertex[3].y = (double)(y + h - 0.5);
|
|
|
|
|
|
|
|
//Z-buffer and RHW are unused for 2D blit, set to normal values
|
|
|
|
vertex[0].z = vertex[1].z = vertex[2].z = vertex[3].z = 0.0;
|
|
|
|
vertex[0].rhw = vertex[1].rhw = vertex[2].rhw = vertex[3].rhw = 1.0;
|
|
|
|
|
|
|
|
double rw = (double)w / (double)pw * (double)tw;
|
|
|
|
double rh = (double)h / (double)ph * (double)th;
|
|
|
|
vertex[0].u = vertex[2].u = (double)(px ) / rw;
|
|
|
|
vertex[1].u = vertex[3].u = (double)(px + w) / rw;
|
|
|
|
vertex[0].v = vertex[1].v = (double)(py ) / rh;
|
|
|
|
vertex[2].v = vertex[3].v = (double)(py + h) / rh;
|
|
|
|
|
|
|
|
vertex_buffer->Lock(0, sizeof(d3dvertex) * 4, (void**)&vertex_ptr, 0);
|
|
|
|
memcpy(vertex_ptr, vertex, sizeof(d3dvertex) * 4);
|
|
|
|
vertex_buffer->Unlock();
|
|
|
|
|
|
|
|
device->SetStreamSource(0, vertex_buffer, 0, sizeof(d3dvertex));
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto clear() -> void {
|
2010-08-09 13:28:56 +00:00
|
|
|
if(lost && !recover()) return;
|
|
|
|
|
|
|
|
texture->GetLevelDesc(0, &d3dsd);
|
|
|
|
texture->GetSurfaceLevel(0, &surface);
|
|
|
|
|
|
|
|
if(surface) {
|
|
|
|
device->ColorFill(surface, 0, D3DCOLOR_XRGB(0x00, 0x00, 0x00));
|
|
|
|
surface->Release();
|
2012-03-31 08:17:36 +00:00
|
|
|
surface = nullptr;
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//clear primary display and all backbuffers
|
|
|
|
for(unsigned i = 0; i < 3; i++) {
|
|
|
|
device->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0x00, 0x00, 0x00), 1.0f, 0);
|
|
|
|
device->Present(0, 0, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) -> bool {
|
2010-08-09 13:28:56 +00:00
|
|
|
if(lost && !recover()) return false;
|
|
|
|
|
|
|
|
if(width != settings.width || height != settings.height) {
|
|
|
|
resize(settings.width = width, settings.height = height);
|
|
|
|
}
|
|
|
|
|
|
|
|
texture->GetLevelDesc(0, &d3dsd);
|
|
|
|
texture->GetSurfaceLevel(0, &surface);
|
|
|
|
|
|
|
|
surface->LockRect(&d3dlr, 0, flags.lock);
|
|
|
|
pitch = d3dlr.Pitch;
|
|
|
|
return data = (uint32_t*)d3dlr.pBits;
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto unlock() -> void {
|
2010-08-09 13:28:56 +00:00
|
|
|
surface->UnlockRect();
|
|
|
|
surface->Release();
|
2012-03-31 08:17:36 +00:00
|
|
|
surface = nullptr;
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto refresh() -> void {
|
2010-08-09 13:28:56 +00:00
|
|
|
if(lost && !recover()) return;
|
|
|
|
|
|
|
|
RECT rd, rs; //dest, source rectangles
|
|
|
|
GetClientRect(settings.handle, &rd);
|
|
|
|
SetRect(&rs, 0, 0, settings.width, settings.height);
|
|
|
|
|
|
|
|
//if output size changed, driver must be re-initialized.
|
|
|
|
//failure to do so causes scaling issues on some video drivers.
|
|
|
|
if(state.width != rd.right || state.height != rd.bottom) {
|
|
|
|
init();
|
Update to v082r29 release.
byuu says:
I doubt anyone is going to like these changes, but oh well.
The base height output for NES+SNES is now always 256x240. The Enable
Overscan option blanks out borders around the screen. This eliminates
the need for an overscan software filter. For NES, it's 16px from the
top and bottom, and 8px from the left and right. Anything less and you
get scrolling artifacts in countless games. For the SNES, it's only 16px
from the top and bottom. Main point is that most NTSC SNES games are
224-height games, so you'll have black borders. Oh well, hack the source
if you want. Game Boy overscan option does nothing.
Everything except for the cheats.xml file now uses BML markup. I need to
write a converter for cheats.xml still. Cut the SNES board parsing code
in half, 30KB->16KB. Much cleaner now.
Took the opportunity to fix a mistake I made back with the XML spec: all
numbers are integers, but can be prefixed with 0x to become hexadecimal.
Before, offset/size values defaulted to hex-mode even without a prefix,
unlike frequency/etc values.
The XML shaders have gone in their own direction anyway, with most being
multi-pass and incompatible with bsnes. So that said, please don't
extend the BML functionality from your end. But f eel free to add to the
XML spec, since other emulators now use that as well. And don't
misunderstand, I love the work that's being done there. It's pretty
awesome to see multi-pass shader capabilities, and the RAM watching
stuff is just amazing.
If there are any really awesome single-pass shaders that people would
like, I can convert it from XML and include it with future releases.
On that topic, I removed the watercolor/hdr-tv ones from the binary
packages (still in the source archive) ... they are neat, but not very
useful for actual gaming.
If we had more than one, I'd remove the Direct3D sepia one. Not going to
use shaders from a certain bipolar manic, because I'd never hear the end
of it if I did :/
Oh, one change I think people will like: MSU1 no longer requires
a memory map specification, so MSU1 authors won't have to keep updating
to my newer revisions of board markups. Basically, if there's not
a board with an msu1 section, it'll check if "gamename.msu" exists. If
it does, MSU1 gets mapped to 00-3f,80-bf:2000-2007. If all you want is
music, make a blank, zero-byte gamename.msu file.
2011-10-04 11:55:39 +00:00
|
|
|
set_shader(shader_source_markup);
|
2010-08-09 13:28:56 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(caps.shader && effect) {
|
|
|
|
device->BeginScene();
|
|
|
|
set_vertex(0, 0, settings.width, settings.height, iwidth, iheight, 0, 0, rd.right, rd.bottom);
|
|
|
|
|
|
|
|
D3DXVECTOR4 rubyTextureSize;
|
|
|
|
rubyTextureSize.x = iwidth;
|
|
|
|
rubyTextureSize.y = iheight;
|
|
|
|
rubyTextureSize.z = 1.0 / iheight;
|
|
|
|
rubyTextureSize.w = 1.0 / iwidth;
|
|
|
|
effect->SetVector("rubyTextureSize", &rubyTextureSize);
|
|
|
|
|
|
|
|
D3DXVECTOR4 rubyInputSize;
|
|
|
|
rubyInputSize.x = settings.width;
|
|
|
|
rubyInputSize.y = settings.height;
|
|
|
|
rubyInputSize.z = 1.0 / settings.height;
|
|
|
|
rubyInputSize.w = 1.0 / settings.width;
|
|
|
|
effect->SetVector("rubyInputSize", &rubyInputSize);
|
|
|
|
|
|
|
|
D3DXVECTOR4 rubyOutputSize;
|
|
|
|
rubyOutputSize.x = rd.right;
|
|
|
|
rubyOutputSize.y = rd.bottom;
|
|
|
|
rubyOutputSize.z = 1.0 / rd.bottom;
|
|
|
|
rubyOutputSize.w = 1.0 / rd.right;
|
|
|
|
effect->SetVector("rubyOutputSize", &rubyOutputSize);
|
|
|
|
|
|
|
|
UINT passes;
|
|
|
|
effect->Begin(&passes, 0);
|
|
|
|
effect->SetTexture("rubyTexture", texture);
|
|
|
|
device->SetTexture(0, texture);
|
|
|
|
for(unsigned pass = 0; pass < passes; pass++) {
|
|
|
|
effect->BeginPass(pass);
|
|
|
|
device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
|
|
|
|
effect->EndPass();
|
|
|
|
}
|
|
|
|
effect->End();
|
|
|
|
device->EndScene();
|
|
|
|
} else {
|
|
|
|
device->BeginScene();
|
|
|
|
set_vertex(0, 0, settings.width, settings.height, iwidth, iheight, 0, 0, rd.right, rd.bottom);
|
|
|
|
device->SetTexture(0, texture);
|
|
|
|
device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
|
|
|
|
device->EndScene();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(settings.synchronize) {
|
Update to v088r15 release.
byuu says:
Changelog:
- default placement of presentation window optimized for 1024x768
displays or larger (sorry if yours is smaller, move the window
yourself.)
- Direct3D waits until a previous Vblank ends before waiting for the
next Vblank to begin (fixes video timing analysis, and ---really---
fast computers.)
- Window::setVisible(false) clears modality, but also fixed in Browser
code as well (fixes loading images on Windows hanging)
- Browser won't consume full CPU resources (but timing analysis will,
I don't want stalls to affect the results.)
- closing settings window while analyzing stops analysis
- you can load the SGB BIOS without a game (why the hell you would want
to ...)
- escape closes the Browser window (it won't close other dialogs, it has
to be hooked up per-window)
- just for fun, joypad hat up/down moves in Browser file list, any
joypad button loads selected game [not very useful, lacks repeat, and
there aren't GUI load file open buttons]
- Super Scope and Justifier crosshairs render correctly (probably
doesn't belong in the core, but it's not something I suspect people
want to do themselves ...)
- you can load GB, SGB, GB, SGB ... without problems (not happy with how
I did this, but I don't want to add an Interface::setInterface()
function yet)
- PAL timing works as I want now (if you want 50fps on a 60hz monitor,
you must not use sync video) [needed to update the DSP frequency when
toggling video/audio sync]
- not going to save input port selection for now (lot of work), but it
will properly keep your port setting across cartridge loads at least
[just goes to controller on emulator restart]
- SFC overscan on and off both work as expected now (off centers image,
on shows entire image)
- laevateinn compiles properly now
- ethos goes to ~/.config/bsnes now that target-ui is dead [honestly,
I recommend deleting the old folder and starting over]
- Emulator::Interface callbacks converted to virtual binding structure
that GUI inherits from (simplifies binding callbacks)
- this breaks Super Game Boy for a bit, I need to rethink
system-specific bindings without direct inheritance
Timing analysis works spectacularly well on Windows, too. You won't get
your 100% perfect rate (unless maybe you leave the analysis running
overnight?), but it'll get really freaking close this way.
2012-05-07 23:29:03 +00:00
|
|
|
D3DRASTER_STATUS status;
|
|
|
|
//wait for a previous vblank to finish, if necessary
|
|
|
|
while(true) {
|
|
|
|
device->GetRasterStatus(0, &status);
|
|
|
|
if(status.InVBlank == false) break;
|
|
|
|
}
|
|
|
|
//wait for next vblank to begin
|
2010-08-09 13:28:56 +00:00
|
|
|
while(true) {
|
|
|
|
device->GetRasterStatus(0, &status);
|
|
|
|
if(status.InVBlank == true) break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(device->Present(0, 0, 0, 0) == D3DERR_DEVICELOST) lost = true;
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto set_shader(const char* source) -> void {
|
2010-08-09 13:28:56 +00:00
|
|
|
if(!caps.shader) return;
|
|
|
|
|
|
|
|
if(effect) {
|
|
|
|
effect->Release();
|
|
|
|
effect = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!source || !*source) {
|
Update to v082r29 release.
byuu says:
I doubt anyone is going to like these changes, but oh well.
The base height output for NES+SNES is now always 256x240. The Enable
Overscan option blanks out borders around the screen. This eliminates
the need for an overscan software filter. For NES, it's 16px from the
top and bottom, and 8px from the left and right. Anything less and you
get scrolling artifacts in countless games. For the SNES, it's only 16px
from the top and bottom. Main point is that most NTSC SNES games are
224-height games, so you'll have black borders. Oh well, hack the source
if you want. Game Boy overscan option does nothing.
Everything except for the cheats.xml file now uses BML markup. I need to
write a converter for cheats.xml still. Cut the SNES board parsing code
in half, 30KB->16KB. Much cleaner now.
Took the opportunity to fix a mistake I made back with the XML spec: all
numbers are integers, but can be prefixed with 0x to become hexadecimal.
Before, offset/size values defaulted to hex-mode even without a prefix,
unlike frequency/etc values.
The XML shaders have gone in their own direction anyway, with most being
multi-pass and incompatible with bsnes. So that said, please don't
extend the BML functionality from your end. But f eel free to add to the
XML spec, since other emulators now use that as well. And don't
misunderstand, I love the work that's being done there. It's pretty
awesome to see multi-pass shader capabilities, and the RAM watching
stuff is just amazing.
If there are any really awesome single-pass shaders that people would
like, I can convert it from XML and include it with future releases.
On that topic, I removed the watercolor/hdr-tv ones from the binary
packages (still in the source archive) ... they are neat, but not very
useful for actual gaming.
If we had more than one, I'd remove the Direct3D sepia one. Not going to
use shaders from a certain bipolar manic, because I'd never hear the end
of it if I did :/
Oh, one change I think people will like: MSU1 no longer requires
a memory map specification, so MSU1 authors won't have to keep updating
to my newer revisions of board markups. Basically, if there's not
a board with an msu1 section, it'll check if "gamename.msu" exists. If
it does, MSU1 gets mapped to 00-3f,80-bf:2000-2007. If all you want is
music, make a blank, zero-byte gamename.msu file.
2011-10-04 11:55:39 +00:00
|
|
|
shader_source_markup = "";
|
2010-08-09 13:28:56 +00:00
|
|
|
return;
|
|
|
|
}
|
Update to v082r29 release.
byuu says:
I doubt anyone is going to like these changes, but oh well.
The base height output for NES+SNES is now always 256x240. The Enable
Overscan option blanks out borders around the screen. This eliminates
the need for an overscan software filter. For NES, it's 16px from the
top and bottom, and 8px from the left and right. Anything less and you
get scrolling artifacts in countless games. For the SNES, it's only 16px
from the top and bottom. Main point is that most NTSC SNES games are
224-height games, so you'll have black borders. Oh well, hack the source
if you want. Game Boy overscan option does nothing.
Everything except for the cheats.xml file now uses BML markup. I need to
write a converter for cheats.xml still. Cut the SNES board parsing code
in half, 30KB->16KB. Much cleaner now.
Took the opportunity to fix a mistake I made back with the XML spec: all
numbers are integers, but can be prefixed with 0x to become hexadecimal.
Before, offset/size values defaulted to hex-mode even without a prefix,
unlike frequency/etc values.
The XML shaders have gone in their own direction anyway, with most being
multi-pass and incompatible with bsnes. So that said, please don't
extend the BML functionality from your end. But f eel free to add to the
XML spec, since other emulators now use that as well. And don't
misunderstand, I love the work that's being done there. It's pretty
awesome to see multi-pass shader capabilities, and the RAM watching
stuff is just amazing.
If there are any really awesome single-pass shaders that people would
like, I can convert it from XML and include it with future releases.
On that topic, I removed the watercolor/hdr-tv ones from the binary
packages (still in the source archive) ... they are neat, but not very
useful for actual gaming.
If we had more than one, I'd remove the Direct3D sepia one. Not going to
use shaders from a certain bipolar manic, because I'd never hear the end
of it if I did :/
Oh, one change I think people will like: MSU1 no longer requires
a memory map specification, so MSU1 authors won't have to keep updating
to my newer revisions of board markups. Basically, if there's not
a board with an msu1 section, it'll check if "gamename.msu" exists. If
it does, MSU1 gets mapped to 00-3f,80-bf:2000-2007. If all you want is
music, make a blank, zero-byte gamename.msu file.
2011-10-04 11:55:39 +00:00
|
|
|
shader_source_markup = source;
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto document = BML::unserialize(shader_source_markup);
|
|
|
|
bool is_hlsl = document["shader"]["language"].text() == "HLSL";
|
|
|
|
string shader_source = document["shader"]["source"].text();
|
2010-09-29 00:05:36 +00:00
|
|
|
if(shader_source == "") return;
|
2010-08-09 13:28:56 +00:00
|
|
|
|
|
|
|
HMODULE d3dx;
|
|
|
|
for(unsigned i = 0; i < 256; i++) {
|
|
|
|
char t[256];
|
|
|
|
sprintf(t, "d3dx9_%u.dll", i);
|
2010-09-26 05:09:31 +00:00
|
|
|
d3dx = LoadLibraryW(utf16_t(t));
|
2010-08-09 13:28:56 +00:00
|
|
|
if(d3dx) break;
|
|
|
|
}
|
2010-09-26 14:06:47 +00:00
|
|
|
if(!d3dx) d3dx = LoadLibraryW(L"d3dx9.dll");
|
2010-08-09 13:28:56 +00:00
|
|
|
if(!d3dx) return;
|
|
|
|
|
|
|
|
EffectProc effectProc = (EffectProc)GetProcAddress(d3dx, "D3DXCreateEffect");
|
|
|
|
TextureProc textureProc = (TextureProc)GetProcAddress(d3dx, "D3DXCreateTextureFromFileA");
|
|
|
|
|
|
|
|
LPD3DXBUFFER pBufferErrors = NULL;
|
2015-03-03 10:14:49 +00:00
|
|
|
effectProc(device, (const void*)shader_source.data(), lstrlenA(shader_source), NULL, NULL, 0, NULL, &effect, &pBufferErrors);
|
2010-08-09 13:28:56 +00:00
|
|
|
|
|
|
|
D3DXHANDLE hTech;
|
|
|
|
effect->FindNextValidTechnique(NULL, &hTech);
|
|
|
|
effect->SetTechnique(hTech);
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto init() -> bool {
|
2010-08-09 13:28:56 +00:00
|
|
|
RECT rd;
|
|
|
|
GetClientRect(settings.handle, &rd);
|
|
|
|
state.width = rd.right;
|
|
|
|
state.height = rd.bottom;
|
|
|
|
|
|
|
|
lpd3d = Direct3DCreate9(D3D_SDK_VERSION);
|
|
|
|
if(!lpd3d) return false;
|
|
|
|
|
|
|
|
memset(&presentation, 0, sizeof(presentation));
|
|
|
|
presentation.Flags = D3DPRESENTFLAG_VIDEO;
|
|
|
|
presentation.SwapEffect = D3DSWAPEFFECT_FLIP;
|
|
|
|
presentation.hDeviceWindow = settings.handle;
|
|
|
|
presentation.BackBufferCount = 1;
|
|
|
|
presentation.MultiSampleType = D3DMULTISAMPLE_NONE;
|
|
|
|
presentation.MultiSampleQuality = 0;
|
|
|
|
presentation.EnableAutoDepthStencil = false;
|
|
|
|
presentation.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
|
|
|
|
presentation.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
|
|
|
presentation.Windowed = true;
|
|
|
|
presentation.BackBufferFormat = D3DFMT_UNKNOWN;
|
|
|
|
presentation.BackBufferWidth = 0;
|
|
|
|
presentation.BackBufferHeight = 0;
|
|
|
|
|
|
|
|
if(lpd3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, settings.handle,
|
Update to v082r04 release.
byuu says:
So, here's the deal. I now have three emulators. I don't think the
NES/GB ones are at all useful, but I do want them to be eventually. And
having them have those pathetic little GUIs like ui-gameboy, and keeping
everything in separate project folders, just doesn't work well for me.
I kind of "got around" the issue with the Game Boy, by only allowing SGB
mode emulation. But there is no "Super Nintendo" ... er ... wait ...
uhmm ... well, you know what I mean anyway.
So, my idea is to write a multi-emulator GUI, and keep the projects
together. The GUI is not going to change much. The way I envision this
working:
At startup, you have a menubar with: "Cartridge, Settings, Tools, Help".
Cartridge has "Load NES Cartridge", "Load SNES Cartridge", etc.
When you load something, Cartridge is replaced with the appropriate
system menu, eg "SNES". Here you have all your regular items: "power,
reset, controller port selection, etc." There is also a new "Unload
Cartridge" option, which is how you restore the "Cartridge" menu again.
I have no plans to emulate any other systems, but if I ever do emulate
something that doesn't take cartridges, I'll change the name to just
"Load" or something.
The cheat editor / state manager will look and act exactly the same. The
settings panel will look exactly the same. I'll simply show/hide
system-specific options as needed, like NES/SNES aspect ratio
correction, etc. The input mapping window will just have settings for
the currently loaded system. Video and audio tweaking will apply
cross-system, as will hotkey mapping.
The GUI stuff is mostly copy-paste, so it should only take me a week to
get it 95% back to where it was, so don't worry, this isn't total GUI
rewrite #80.
I am, however, making all the objects pointers, so that I can destruct
them all prior to main() returning, which is certainly one way of fixing
that annoying Windows/Qt crash.
Please only test on Linux. The Windows port is broken to hell, and will
give you a bad impression of the idea:
- menu groups are not hiding for some reason (all groups are showing, it
looks hideous)
- Timer interval(0) is taking 16ms per call, capping the FPS to ~64 tops
[FWIW, bsnes/accuracy gets 130fps, bgameboy gets 450fps, bnes gets
800fps; all run at lowest possible granularity]
- the OS keeps beeping when you press keys (AGAIN)
Of course, Qt and GTK+ don't let you shrink a window from the requested
geometry size, because they suck. So the video scaling stuff doesn't
work all that great yet.
Man, a metric fuckton of things need to be fixed in phoenix, and
I really don't know how to fix any of them :/
2011-09-09 04:08:38 +00:00
|
|
|
D3DCREATE_FPU_PRESERVE | D3DCREATE_SOFTWARE_VERTEXPROCESSING, &presentation, &device) != D3D_OK) {
|
2010-08-09 13:28:56 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
device->GetDeviceCaps(&d3dcaps);
|
|
|
|
|
|
|
|
caps.dynamic = bool(d3dcaps.Caps2 & D3DCAPS2_DYNAMICTEXTURES);
|
|
|
|
caps.shader = d3dcaps.PixelShaderVersion > D3DPS_VERSION(1, 4);
|
|
|
|
|
|
|
|
if(caps.dynamic == true) {
|
|
|
|
flags.t_usage = D3DUSAGE_DYNAMIC;
|
|
|
|
flags.v_usage = D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC;
|
|
|
|
flags.t_pool = D3DPOOL_DEFAULT;
|
|
|
|
flags.v_pool = D3DPOOL_DEFAULT;
|
|
|
|
flags.lock = D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD;
|
|
|
|
} else {
|
|
|
|
flags.t_usage = 0;
|
|
|
|
flags.v_usage = D3DUSAGE_WRITEONLY;
|
|
|
|
flags.t_pool = D3DPOOL_MANAGED;
|
|
|
|
flags.v_pool = D3DPOOL_MANAGED;
|
|
|
|
flags.lock = D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD;
|
|
|
|
}
|
|
|
|
|
|
|
|
lost = false;
|
|
|
|
recover();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto release_resources() -> void {
|
2010-08-09 13:28:56 +00:00
|
|
|
if(effect) { effect->Release(); effect = 0; }
|
|
|
|
if(vertex_buffer) { vertex_buffer->Release(); vertex_buffer = 0; }
|
|
|
|
if(surface) { surface->Release(); surface = 0; }
|
|
|
|
if(texture) { texture->Release(); texture = 0; }
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto term() -> void {
|
2010-08-09 13:28:56 +00:00
|
|
|
release_resources();
|
|
|
|
if(device) { device->Release(); device = 0; }
|
|
|
|
if(lpd3d) { lpd3d->Release(); lpd3d = 0; }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#undef D3DVERTEX
|