2011-11-06 14:41:41 +02:00
|
|
|
#include "core/command.hpp"
|
2011-11-08 21:22:30 +02:00
|
|
|
#include "core/dispatch.hpp"
|
2011-11-06 14:41:41 +02:00
|
|
|
#include "core/framebuffer.hpp"
|
2012-09-08 19:44:45 +03:00
|
|
|
#include "core/subtitles.hpp"
|
2012-02-06 04:38:53 +02:00
|
|
|
#include "lua/lua.hpp"
|
2011-11-06 14:41:41 +02:00
|
|
|
#include "core/misc.hpp"
|
|
|
|
#include "core/window.hpp"
|
2013-01-06 12:30:32 +02:00
|
|
|
#include "core/moviedata.hpp"
|
|
|
|
#include "core/moviefile.hpp"
|
2012-06-20 17:40:27 +03:00
|
|
|
#include "fonts/wrapper.hpp"
|
|
|
|
#include "library/framebuffer.hpp"
|
|
|
|
#include "library/pixfmt-lrgb.hpp"
|
2011-09-16 06:13:33 +03:00
|
|
|
|
2012-06-20 17:40:27 +03:00
|
|
|
framebuffer_raw screen_corrupt;
|
2011-09-16 06:13:33 +03:00
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
2012-01-06 17:28:01 +02:00
|
|
|
struct render_info
|
|
|
|
{
|
2012-06-20 17:40:27 +03:00
|
|
|
framebuffer_raw fbuf;
|
2012-01-06 17:28:01 +02:00
|
|
|
render_queue rq;
|
|
|
|
uint32_t hscl;
|
|
|
|
uint32_t vscl;
|
|
|
|
uint32_t lgap;
|
|
|
|
uint32_t rgap;
|
|
|
|
uint32_t tgap;
|
|
|
|
uint32_t bgap;
|
|
|
|
};
|
|
|
|
|
|
|
|
triplebuffer_logic buffering;
|
|
|
|
render_info buffer1;
|
|
|
|
render_info buffer2;
|
|
|
|
render_info buffer3;
|
|
|
|
|
|
|
|
render_info& get_write_buffer()
|
|
|
|
{
|
|
|
|
unsigned i = buffering.start_write();
|
|
|
|
switch(i) {
|
|
|
|
case 0:
|
|
|
|
return buffer1;
|
|
|
|
case 1:
|
|
|
|
return buffer2;
|
|
|
|
case 2:
|
|
|
|
return buffer3;
|
|
|
|
default:
|
|
|
|
return buffer1;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
render_info& get_read_buffer()
|
|
|
|
{
|
|
|
|
unsigned i = buffering.start_read();
|
|
|
|
switch(i) {
|
|
|
|
case 0:
|
|
|
|
return buffer1;
|
|
|
|
case 1:
|
|
|
|
return buffer2;
|
|
|
|
case 2:
|
|
|
|
return buffer3;
|
|
|
|
default:
|
|
|
|
return buffer1;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2011-09-16 06:13:33 +03:00
|
|
|
struct render_list_entry
|
|
|
|
{
|
|
|
|
uint32_t codepoint;
|
|
|
|
uint32_t x;
|
|
|
|
uint32_t y;
|
|
|
|
uint32_t scale;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct render_list_entry rl_corrupt[] = {
|
|
|
|
{'S', 88, 56, 7},
|
|
|
|
{'Y', 144, 56, 7},
|
|
|
|
{'S', 200, 56, 7},
|
|
|
|
{'T', 256, 56, 7},
|
|
|
|
{'E', 312, 56, 7},
|
|
|
|
{'M', 368, 56, 7},
|
|
|
|
{'S', 116, 168, 7},
|
|
|
|
{'T', 172, 168, 7},
|
|
|
|
{'A', 224, 168, 7},
|
|
|
|
{'T', 280, 168, 7},
|
|
|
|
{'E', 336, 168, 7},
|
|
|
|
{'C', 60, 280, 7},
|
|
|
|
{'O', 116, 280, 7},
|
|
|
|
{'R', 172, 280, 7},
|
|
|
|
{'R', 228, 280, 7},
|
|
|
|
{'U', 284, 280, 7},
|
|
|
|
{'P', 340, 280, 7},
|
|
|
|
{'T', 396, 280, 7},
|
|
|
|
{0, 0, 0, 0}
|
|
|
|
};
|
|
|
|
|
2011-10-19 13:59:54 +03:00
|
|
|
void draw_special_screen(uint32_t* target, struct render_list_entry* rlist)
|
2011-09-16 06:13:33 +03:00
|
|
|
{
|
|
|
|
while(rlist->scale) {
|
2012-06-20 17:40:27 +03:00
|
|
|
auto g = main_font.get_glyph(rlist->codepoint);
|
2011-09-16 06:13:33 +03:00
|
|
|
for(uint32_t j = 0; j < 16; j++) {
|
2012-06-20 17:40:27 +03:00
|
|
|
for(uint32_t i = 0; i < (g.wide ? 16 : 8); i++) {
|
|
|
|
uint32_t slice = g.data[j / (g.wide ? 2 : 4)];
|
|
|
|
uint32_t bit = 31 - ((j % (g.wide ? 2 : 4)) * (g.wide ? 16 : 8) + i);
|
2011-11-06 15:58:26 +02:00
|
|
|
uint32_t value = (slice >> bit) & 1;
|
2011-09-16 06:13:33 +03:00
|
|
|
if(value) {
|
|
|
|
uint32_t basex = rlist->x + rlist->scale * i;
|
|
|
|
uint32_t basey = rlist->y + rlist->scale * j;
|
|
|
|
for(uint32_t j2 = 0; j2 < rlist->scale; j2++)
|
|
|
|
for(uint32_t i2 = 0; i2 < rlist->scale; i2++)
|
2011-10-29 18:28:37 +03:00
|
|
|
target[(basey + j2) * 512 + (basex + i2)] = 0x7FFFF;
|
2011-09-16 06:13:33 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rlist++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-19 13:59:54 +03:00
|
|
|
void draw_corrupt(uint32_t* target)
|
2011-09-16 06:13:33 +03:00
|
|
|
{
|
|
|
|
for(unsigned i = 0; i < 512 * 448; i++)
|
2011-10-29 18:28:37 +03:00
|
|
|
target[i] = 0x7FC00;
|
2011-09-16 06:13:33 +03:00
|
|
|
draw_special_screen(target, rl_corrupt);
|
|
|
|
}
|
2011-09-17 11:33:35 +03:00
|
|
|
|
2012-10-13 11:13:17 +03:00
|
|
|
function_ptr_command<arg_filename> take_screenshot_cmd(lsnes_cmd, "take-screenshot", "Takes a screenshot",
|
2011-09-17 11:33:35 +03:00
|
|
|
"Syntax: take-screenshot <file>\nSaves screenshot to PNG file <file>\n",
|
2011-09-17 12:00:49 +03:00
|
|
|
[](arg_filename file) throw(std::bad_alloc, std::runtime_error) {
|
2012-01-06 17:28:01 +02:00
|
|
|
take_screenshot(file);
|
2011-09-17 11:33:35 +03:00
|
|
|
messages << "Saved PNG screenshot" << std::endl;
|
|
|
|
});
|
2012-01-06 17:28:01 +02:00
|
|
|
|
|
|
|
bool last_redraw_no_lua = true;
|
2011-09-16 06:13:33 +03:00
|
|
|
}
|
|
|
|
|
2012-06-20 17:40:27 +03:00
|
|
|
framebuffer<false> main_screen;
|
2011-09-16 06:13:33 +03:00
|
|
|
|
2012-01-06 17:28:01 +02:00
|
|
|
void take_screenshot(const std::string& file) throw(std::bad_alloc, std::runtime_error)
|
|
|
|
{
|
|
|
|
render_info& ri = get_read_buffer();
|
|
|
|
ri.fbuf.save_png(file);
|
|
|
|
buffering.end_read();
|
|
|
|
}
|
|
|
|
|
2011-09-16 06:13:33 +03:00
|
|
|
|
|
|
|
void init_special_screens() throw(std::bad_alloc)
|
|
|
|
{
|
2012-01-06 17:28:01 +02:00
|
|
|
std::vector<uint32_t> buf;
|
|
|
|
buf.resize(512*448);
|
2012-06-20 17:40:27 +03:00
|
|
|
|
|
|
|
framebuffer_info inf;
|
|
|
|
inf.type = &_pixel_format_lrgb;
|
|
|
|
inf.mem = reinterpret_cast<char*>(&buf[0]);
|
|
|
|
inf.physwidth = 512;
|
|
|
|
inf.physheight = 448;
|
|
|
|
inf.physstride = 2048;
|
|
|
|
inf.width = 512;
|
|
|
|
inf.height = 448;
|
|
|
|
inf.stride = 2048;
|
|
|
|
inf.offset_x = 0;
|
|
|
|
inf.offset_y = 0;
|
|
|
|
|
2012-01-06 17:28:01 +02:00
|
|
|
draw_corrupt(&buf[0]);
|
2012-06-20 17:40:27 +03:00
|
|
|
screen_corrupt = framebuffer_raw(inf);
|
2011-09-16 06:13:33 +03:00
|
|
|
}
|
|
|
|
|
2012-06-20 17:40:27 +03:00
|
|
|
void redraw_framebuffer(framebuffer_raw& todraw, bool no_lua, bool spontaneous)
|
2011-09-16 06:13:33 +03:00
|
|
|
{
|
2011-10-08 23:02:47 +03:00
|
|
|
uint32_t hscl, vscl;
|
2013-01-06 12:30:32 +02:00
|
|
|
auto g = our_rom->rtype->get_scale_factors(todraw.get_width(), todraw.get_height());
|
2011-10-08 23:02:47 +03:00
|
|
|
hscl = g.first;
|
|
|
|
vscl = g.second;
|
2012-01-06 17:28:01 +02:00
|
|
|
render_info& ri = get_write_buffer();
|
|
|
|
ri.rq.clear();
|
2011-09-16 06:13:33 +03:00
|
|
|
struct lua_render_context lrc;
|
|
|
|
lrc.left_gap = 0;
|
|
|
|
lrc.right_gap = 0;
|
|
|
|
lrc.bottom_gap = 0;
|
|
|
|
lrc.top_gap = 0;
|
2012-01-06 17:28:01 +02:00
|
|
|
lrc.queue = &ri.rq;
|
2012-06-20 17:40:27 +03:00
|
|
|
lrc.width = todraw.get_width() * hscl;
|
|
|
|
lrc.height = todraw.get_height() * vscl;
|
2012-09-08 19:44:45 +03:00
|
|
|
if(!no_lua) {
|
2012-03-06 05:10:54 +02:00
|
|
|
lua_callback_do_paint(&lrc, spontaneous);
|
2012-09-08 19:44:45 +03:00
|
|
|
render_subtitles(lrc);
|
|
|
|
}
|
2012-01-06 17:28:01 +02:00
|
|
|
ri.fbuf = todraw;
|
|
|
|
ri.hscl = hscl;
|
|
|
|
ri.vscl = vscl;
|
|
|
|
ri.lgap = lrc.left_gap;
|
|
|
|
ri.rgap = lrc.right_gap;
|
|
|
|
ri.tgap = lrc.top_gap;
|
|
|
|
ri.bgap = lrc.bottom_gap;
|
|
|
|
buffering.end_write();
|
2013-07-07 13:54:56 +03:00
|
|
|
notify_screen_update();
|
2012-01-06 17:28:01 +02:00
|
|
|
last_redraw_no_lua = no_lua;
|
|
|
|
}
|
|
|
|
|
|
|
|
void redraw_framebuffer()
|
|
|
|
{
|
|
|
|
render_info& ri = get_read_buffer();
|
2012-06-20 17:40:27 +03:00
|
|
|
framebuffer_raw copy = ri.fbuf;
|
2012-01-06 17:28:01 +02:00
|
|
|
buffering.end_read();
|
2012-03-06 05:10:54 +02:00
|
|
|
//Redraws are never spontaneous
|
|
|
|
redraw_framebuffer(copy, last_redraw_no_lua, false);
|
2012-01-06 17:28:01 +02:00
|
|
|
}
|
|
|
|
|
2011-11-15 05:10:12 +02:00
|
|
|
|
2012-01-06 17:28:01 +02:00
|
|
|
void render_framebuffer()
|
|
|
|
{
|
|
|
|
render_info& ri = get_read_buffer();
|
2012-06-20 17:40:27 +03:00
|
|
|
main_screen.reallocate(ri.fbuf.get_width() * ri.hscl + ri.lgap + ri.rgap, ri.fbuf.get_height() * ri.vscl +
|
|
|
|
ri.tgap + ri.bgap);
|
2012-01-06 17:28:01 +02:00
|
|
|
main_screen.set_origin(ri.lgap, ri.tgap);
|
|
|
|
main_screen.copy_from(ri.fbuf, ri.hscl, ri.vscl);
|
|
|
|
ri.rq.run(main_screen);
|
2013-07-07 13:54:56 +03:00
|
|
|
notify_set_screen(main_screen);
|
2011-09-16 06:13:33 +03:00
|
|
|
//We would want divide by 2, but we'll do it ourselves in order to do mouse.
|
2012-12-16 20:45:16 +02:00
|
|
|
keyboard_key* mouse_x = lsnes_kbd.try_lookup_key("mouse_x");
|
|
|
|
keyboard_key* mouse_y = lsnes_kbd.try_lookup_key("mouse_y");
|
|
|
|
keyboard_mouse_calibration xcal;
|
|
|
|
keyboard_mouse_calibration ycal;
|
|
|
|
xcal.offset = ri.lgap;
|
|
|
|
ycal.offset = ri.tgap;
|
|
|
|
if(mouse_x && mouse_x->get_type() == KBD_KEYTYPE_MOUSE)
|
|
|
|
mouse_x->cast_mouse()->set_calibration(xcal);
|
|
|
|
if(mouse_y && mouse_y->get_type() == KBD_KEYTYPE_MOUSE)
|
|
|
|
mouse_y->cast_mouse()->set_calibration(ycal);
|
2012-01-06 17:28:01 +02:00
|
|
|
buffering.end_read();
|
2011-09-16 06:13:33 +03:00
|
|
|
}
|
2011-10-08 23:02:47 +03:00
|
|
|
|
2012-01-06 17:28:01 +02:00
|
|
|
std::pair<uint32_t, uint32_t> get_framebuffer_size()
|
|
|
|
{
|
|
|
|
uint32_t v, h;
|
|
|
|
render_info& ri = get_read_buffer();
|
2012-06-20 17:40:27 +03:00
|
|
|
v = ri.fbuf.get_width();
|
|
|
|
h = ri.fbuf.get_height();
|
2012-01-06 17:28:01 +02:00
|
|
|
buffering.end_read();
|
|
|
|
return std::make_pair(h, v);
|
|
|
|
}
|
|
|
|
|
2012-06-20 17:40:27 +03:00
|
|
|
framebuffer_raw get_framebuffer() throw(std::bad_alloc)
|
2012-01-06 17:28:01 +02:00
|
|
|
{
|
|
|
|
render_info& ri = get_read_buffer();
|
2012-06-20 17:40:27 +03:00
|
|
|
framebuffer_raw copy = ri.fbuf;
|
2012-01-06 17:28:01 +02:00
|
|
|
buffering.end_read();
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-23 16:48:56 +02:00
|
|
|
triplebuffer_logic::triplebuffer_logic() throw(std::bad_alloc)
|
|
|
|
{
|
|
|
|
last_complete_slot = 0;
|
|
|
|
read_active = false;
|
|
|
|
write_active = false;
|
|
|
|
read_active_slot = 0;
|
|
|
|
write_active_slot = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
triplebuffer_logic::~triplebuffer_logic() throw()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned triplebuffer_logic::start_write() throw()
|
|
|
|
{
|
2013-01-21 09:24:46 +02:00
|
|
|
umutex_class h(mut);
|
2012-02-23 16:48:56 +02:00
|
|
|
if(!write_active) {
|
|
|
|
//We need to avoid hitting last complete slot or slot that is active for read.
|
|
|
|
if(last_complete_slot != 0 && read_active_slot != 0)
|
|
|
|
write_active_slot = 0;
|
|
|
|
else if(last_complete_slot != 1 && read_active_slot != 1)
|
|
|
|
write_active_slot = 1;
|
|
|
|
else
|
|
|
|
write_active_slot = 2;
|
|
|
|
}
|
|
|
|
write_active++;
|
|
|
|
return write_active_slot;
|
|
|
|
}
|
|
|
|
|
|
|
|
void triplebuffer_logic::end_write() throw()
|
|
|
|
{
|
2013-01-21 09:24:46 +02:00
|
|
|
umutex_class h(mut);
|
2012-02-23 16:48:56 +02:00
|
|
|
if(!--write_active)
|
|
|
|
last_complete_slot = write_active_slot;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned triplebuffer_logic::start_read() throw()
|
|
|
|
{
|
2013-01-21 09:24:46 +02:00
|
|
|
umutex_class h(mut);
|
2012-02-23 16:48:56 +02:00
|
|
|
if(!read_active)
|
|
|
|
read_active_slot = last_complete_slot;
|
|
|
|
read_active++;
|
|
|
|
return read_active_slot;
|
|
|
|
}
|
|
|
|
|
|
|
|
void triplebuffer_logic::end_read() throw()
|
|
|
|
{
|
2013-01-21 09:24:46 +02:00
|
|
|
umutex_class h(mut);
|
2012-02-23 16:48:56 +02:00
|
|
|
read_active--;
|
|
|
|
}
|
2013-02-28 04:03:01 +02:00
|
|
|
|
|
|
|
void render_kill_request(void* obj)
|
|
|
|
{
|
|
|
|
buffer1.rq.kill_request(obj);
|
|
|
|
buffer2.rq.kill_request(obj);
|
|
|
|
buffer3.rq.kill_request(obj);
|
2013-02-28 04:06:37 +02:00
|
|
|
}
|