Skip to content

Commit

Permalink
Improvements to JS display implementation
Browse files Browse the repository at this point in the history
Fix red and green channels being swapped due to different assumptions
in channel ordering between DingusPPC and the JS side.

Add content hashing of the frame buffer so that we don't need to
blit to the JS side if the content hasn't changed. Uses xxHash3
since it has the best performance.

Updates mihaip/infinite-mac#219
Updates mihaip/infinite-mac#18
  • Loading branch information
mihaip committed Sep 16, 2023
1 parent f91122a commit 06e6eed
Show file tree
Hide file tree
Showing 3 changed files with 7,091 additions and 17 deletions.
61 changes: 44 additions & 17 deletions devices/video/display_js.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@
#include <loguru.hpp>
#include <emscripten.h>

// Use xxHash in inline mode to avoid having to include a separate .c file.
#define XXH_STATIC_LINKING_ONLY
#define XXH_INLINE_ALL
#include <thirdparty/xxHash/xxhash.h>

class Display::Impl
{
public:
std::unique_ptr<uint8_t[]> browser_framebuffer;
int browser_framebuffer_size;
int browser_framebuffer_pitch;
std::unique_ptr<uint8_t[]> framebuffer;
int framebuffer_size;
int framebuffer_pitch;
XXH64_hash_t last_update_hash;
};

Display::Display(): impl(std::make_unique<Impl>())
Expand All @@ -21,9 +27,10 @@ Display::~Display() {

bool Display::configure(int width, int height)
{
impl->browser_framebuffer_pitch = width * 4;
impl->browser_framebuffer_size = height * impl->browser_framebuffer_pitch;
impl->browser_framebuffer = std::make_unique<uint8_t[]>(impl->browser_framebuffer_size);
impl->framebuffer_pitch = width * 4;
impl->framebuffer_size = height * impl->framebuffer_pitch;
impl->framebuffer = std::make_unique<uint8_t[]>(impl->framebuffer_size);
impl->last_update_hash = 0;
EM_ASM_({ workerApi.didOpenVideo($0, $1); }, width, height);
return true;
}
Expand All @@ -36,22 +43,42 @@ void Display::handle_events(const WindowEvent& wnd_event)
void Display::blank()
{
// Replace contents with opaque black.
uint8_t *browser_framebuffer = impl->browser_framebuffer.get();
int browser_framebuffer_size = impl->browser_framebuffer_size;
for (int i = 0; i < browser_framebuffer_size; i += 4) {
browser_framebuffer[i] = 0x00;
browser_framebuffer[i + 1] = 0x00;
browser_framebuffer[i + 2] = 0x00;
browser_framebuffer[i + 3] = 0xff;
uint8_t *framebuffer = impl->framebuffer.get();
int framebuffer_size = impl->framebuffer_size;
for (int i = 0; i < framebuffer_size; i += 4) {
framebuffer[i] = 0x00;
framebuffer[i + 1] = 0x00;
framebuffer[i + 2] = 0x00;
framebuffer[i + 3] = 0xff;
}
EM_ASM_({ workerApi.blit($0, $1); }, browser_framebuffer, browser_framebuffer_size);
impl->last_update_hash = XXH3_64bits(framebuffer, framebuffer_size);
EM_ASM_({ workerApi.blit($0, $1); }, framebuffer, framebuffer_size);
}

void Display::update(std::function<void(uint8_t *dst_buf, int dst_pitch)> convert_fb_cb, bool draw_hw_cursor, int cursor_x, int cursor_y)
{
convert_fb_cb(impl->browser_framebuffer.get(), impl->browser_framebuffer_pitch);
// TODO: has contents and avoid sending framebuffer if unchanged
EM_ASM_({ workerApi.blit($0, $1); }, impl->browser_framebuffer.get(), impl->browser_framebuffer_size);
uint8_t *framebuffer = impl->framebuffer.get();
size_t framebuffer_size = impl->framebuffer_size;
convert_fb_cb(framebuffer, impl->framebuffer_pitch);
// DingusPPC generates ARGB but in little endian order within a 32-bit word. s
// It ends up as BGRA in memory, so we need to swap red and blue channels
// to generate the RGBA that the JS side expects.
for (size_t i = 0; i < framebuffer_size; i += 4) {
std::swap(framebuffer[i], framebuffer[i + 2]);
}

XXH64_hash_t update_hash = XXH3_64bits(framebuffer, framebuffer_size);

if (update_hash == impl->last_update_hash) {
// Screen has not changed, but we still let the JS know so that it can
// keep track of screen refreshes when deciding how long to idle for.
EM_ASM({ workerApi.blit(0, 0); });
return;
}

impl->last_update_hash = update_hash;

EM_ASM_({ workerApi.blit($0, $1); }, framebuffer, framebuffer_size);
}

void Display::setup_hw_cursor(std::function<void(uint8_t *dst_buf, int dst_pitch)> draw_hw_cursor, int cursor_width, int cursor_height)
Expand Down
3 changes: 3 additions & 0 deletions thirdparty/xxHash/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# xxHash

Import of xxhash.h from https://github.com/Cyan4973/xxHash as of Cyan4973/xxHash@675eda553733666329e1052f8fd97ef599fdca63
Loading

0 comments on commit 06e6eed

Please sign in to comment.