mirror of https://github.com/bsnes-emu/bsnes.git
Update to bsnes v019r11 release.
Ok, I tried my best to add the audio synchronization method (drop video frames) yet again, and once again failed completely. The below WIP is completely unusuable as it stands, so please don't link to it, host it anywhere else, or even download it unless you can help with the programming. I'm not going to be able to fix this myself as I've tried countless times over the last two years in vain to fix it. http://byuu.cinnamonpirate.com/files/bsnes_v019_wip11.zip The included config file is important: it uses the DirectDraw renderer instead of the D3D renderer, and has triple buffering enabled. The relevant code is in src/ui/video/ddraw.cpp and src/ui/audio/dsound.cpp. The most important code is below, but obviously any tests would need the above WIP to build and try out. void AudioDS::run(uint32 sample) { uiVideo->tick(); data.buffer[data.buffer_pos++] = sample; if(data.buffer_pos < latency)return; uint32 ring_pos, pos, size; do { Sleep(1); uiVideo->tick(); dsb_b->GetCurrentPosition(&pos, 0); ring_pos = pos / data.ring_size; } while(config::system.regulate_speed == true && ring_pos == data.ring_pos); data.ring_pos = ring_pos; void *output; if(dsb_b->Lock(((data.ring_pos + 2) % 3) * data.ring_size, data.ring_size, &output, &size, 0, 0, 0) == DS_OK) { //Audio::resample_hermite((uint32*)output, data.buffer, latency, data.buffer_pos); memcpy(output, data.buffer, data.ring_size); dsb_b->Unlock(output, size, 0, 0); } data.buffer_pos = 0; } bool VideoDD::lock(uint16 *&data, uint &pitch) { if(video_buffer[video_active]->Lock(0, &ddsd, DDLOCK_WAIT, 0) != DD_OK) return false; video_valid[video_active] = false; pitch = ddsd.lPitch; data = (uint16*)ddsd.lpSurface; return data; } void VideoDD::unlock() { video_buffer[video_active]->Unlock(0); } void VideoDD::refresh() { video_valid[video_active] = true; video_active ^= 1; tick(); } void VideoDD::tick() { if(video_valid[0] == false && video_valid[1] == false) return; //nothing to render uint idx = video_valid[!video_active] == true ? !video_active : video_active; // if(video_valid[!video_active] == false) return; //uint idx = !video_active; if(settings.triple_buffering == true) { BOOL in_vblank; lpdd7->GetVerticalBlankStatus(&in_vblank); if(in_vblank == false) return; //DWORD scanline; // lpdd7->GetScanLine(&scanline); // if(scanline < screen_height()) return; // lpdd7->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0); } HRESULT hr; RECT rd, rs; snes.get_video_info(&vi); SetRect(&rs, 0, 0, vi.width, vi.height); POINT p = { 0, 0 }; ClientToScreen(hwnd, &p); GetClientRect(hwnd, &rd); OffsetRect(&rd, p.x, p.y); hr = screen->Blt(&rd, video_buffer[idx], &rs, DDBLT_WAIT, 0); video_valid[idx] = false; if(hr == DDERR_SURFACELOST) { screen->Restore(); video_buffer[0]->Restore(); video_buffer[1]->Restore(); } } What I'm basically doing is: Audio keeps a ring buffer, and waits until the temporary buffer fills up before forcing the emulator to sleep until the audio playback catches up. Every time an audio sample is generated, and every time the emulator sleeps for one millisecond, it gives Video a chance to run. Video has two backbuffers (a poor man's triple buffering, since that doesn't work in windowed mode for DDraw). The PPU renders the entire screen line by line, but it doesn't go from the PPU to the video card until Video::video_lock is called. At this time, the current buffer sets a flag to say it's contents are invalid, then it draws to the frame, then sets a flag saying the current contents are again valid. Finally, it calls the Video tick function to finish. Every time the Video tick function is called from Audio (well over 32,000 times a second, so it should have good precision for detecting vblank edges). First, it will see if any frames have completely rendered. If not, it will give up and return. Next, it will see if "triple buffering" (really a vsync now, but emulates triple buffering at least) is enabled. If so, it will return and do nothing if not in vblank. Otherwise, or if triple buffering is disabled, it will continue. Next, it finds the most recently rendered video frame that was valid and blits that to the screen, and then sets that frame to invalid, so that it is not rendered again (though it wouldn't hurt, it wastes CPU time to blit the same image twice). I've tried even adding in a 1ms interrupt timer to try and help with any emulation code that might be freezing the emulator for over an entire vblank (nothing in bsnes should be that intensive), and this did not help either. Basically, it's like I'm missing an unbelievable amount of frames, like five out of six end up never getting drawn at all, so the video is so choppy it's completely unusable. In reality, only one frame should be dropped every 11 seconds. And when I enable the resampler, that should change to only one frame every 66 seconds. As a side note, I added a four-tap hermite resampler in. It sounds good too, but I have no idea if it's better or worse than cubic. [No archive available]
This commit is contained in:
parent
6d66b1136d
commit
f9a8564af0