Update to v103r19 release.

byuu says:

Changelog:

  - tomoko: Application::onMain assigned at end of Program::Program()
    [Screwtape]¹
  - libco: add `#define _XOPEN_SOURCE 500` to fix compilation of sjlj.c
    [Screwtape]
  - ruby/audio/openal: fixed device driver string list enumeration
  - ruby/audio/wasapi: changing device re-initializes the driver now
  - ruby/audio/wasapi: probably a pointless change, but don't fill the
    buffer beyond the queue size with silence
  - ruby/video/xvideo: renamed from ruby/video/xv
  - ruby/video/xvideo: check to see if `XV_AUTOPAINT_COLORKEY` exists
    before setting it [SuperMikeMan]
  - ruby/video/xvideo: align buffer sizes to be evenly divisible by four
    [SuperMikeMan]
  - ruby/video/xvideo: fail nicely without crashing (hopefully)
  - ruby/video/xvideo: add support for YV12 and I420 12-bit planar YUV
    formats²

¹: prevents crashes when drivers fail to initialize from running the
main loop that polls input drivers before the input driver is
initialized (or fails to initialize itself.) Some drivers still don't
block their main functions when initialization fails, so they will still
crash, but I'll work to fix them.

²: this was a **major** pain in the ass, heh. You only get one chroma
sample for every four luma samples, so the color reproduction is even
worse than UYVY and YUYV (which is two to four chroma to luma.) Further,
the planar format took forever to figure out. Apparently it doesn't care
what portion of the image you specify in XvShmPutImage, it expects you
to use the buffer dimensions to locate the U and V portions of the data.

This is probably the most thorough X-Video driver in existence now.

Notes:

  - forgot to rename the configuration settings dialog window title to
    just "Settings"
This commit is contained in:
Tim Allen 2017-07-23 19:18:16 +10:00
parent 284e4c043e
commit 8be474b0ac
9 changed files with 184 additions and 107 deletions

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "103.18";
static const string Version = "103.19";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

@ -26,11 +26,11 @@ else ifeq ($(platform),macosx)
ruby += audio.openal
ruby += #input.quartz input.carbon
else ifeq ($(platform),linux)
ruby += video.glx2 video.xv video.xshm video.sdl #video.glx
ruby += video.glx2 video.xvideo video.xshm video.sdl #video.glx
ruby += audio.oss audio.openal #audio.alsa audio.pulseaudio audio.pulseaudiosimple audio.ao
ruby += input.sdl input.xlib #input.udev
else ifeq ($(platform),bsd)
ruby += video.glx2 video.xv video.xshm video.sdl #video.glx
ruby += video.glx2 video.xvideo video.xshm video.sdl #video.glx
ruby += audio.oss audio.openal
ruby += input.sdl input.xlib
endif

View File

@ -15,7 +15,6 @@ unique_pointer<Program> program;
Program::Program(string_vector args) {
program = this;
Application::onMain({&Program::main, this});
Emulator::platform = this;
emulators.append(new Famicom::Interface);
@ -77,6 +76,8 @@ Program::Program(string_vector args) {
}
}
loadMedium();
Application::onMain({&Program::main, this});
}
auto Program::main() -> void {

View File

@ -12,6 +12,7 @@
#define LIBCO_C
#include "libco.h"
#define _XOPEN_SOURCE 500
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>

View File

@ -14,7 +14,7 @@ rubylink += $(if $(findstring video.direct3d,$(ruby)),-ld3d9)
rubylink += $(if $(findstring video.directdraw,$(ruby)),-lddraw)
rubylink += $(if $(findstring video.glx,$(ruby)),-lGL)
rubylink += $(if $(findstring video.wgl,$(ruby)),-lopengl32)
rubylink += $(if $(findstring video.xv,$(ruby)),-lXv)
rubylink += $(if $(findstring video.xvideo,$(ruby)),-lXv)
rubylink += $(if $(findstring audio.alsa,$(ruby)),-lasound)
rubylink += $(if $(findstring audio.ao,$(ruby)),-lao)

View File

@ -161,9 +161,9 @@ private:
const char* list = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
if(!list) return result;
while(list[0] || list[1]) {
while(list && *list) {
result.append(list);
while(list[0]) list++;
list += strlen(list) + 1;
}
return result;

View File

@ -37,7 +37,7 @@ struct AudioWASAPI : Audio {
auto setDevice(string device) -> bool {
if(_device == device) return true;
_device = device;
return true;
return initialize();
}
auto setBlocking(bool blocking) -> bool {
@ -177,12 +177,12 @@ private:
_audioClient->GetCurrentPadding(&padding);
available = _bufferSize - padding;
}
uint32_t length = min(available, _queue.count);
uint8_t* buffer = nullptr;
if(_renderClient->GetBuffer(available, &buffer) == S_OK) {
if(_renderClient->GetBuffer(length, &buffer) == S_OK) {
uint bufferFlags = 0;
for(uint _ : range(available)) {
//if more samples are available than we have queued, fill remainder with silence
for(uint _ : range(length)) {
double samples[8] = {};
if(_queue.count) {
for(uint n : range(_channels)) {
@ -210,7 +210,7 @@ private:
break;
}
}
_renderClient->ReleaseBuffer(available, bufferFlags);
_renderClient->ReleaseBuffer(length, bufferFlags);
}
}

View File

@ -66,8 +66,8 @@ using namespace ruby;
#include <ruby/video/xshm.cpp>
#endif
#if defined(VIDEO_XV)
#include <ruby/video/xv.cpp>
#if defined(VIDEO_XVIDEO)
#include <ruby/video/xvideo.cpp>
#endif
namespace ruby {
@ -111,8 +111,8 @@ auto Video::create(const string& driver) -> Video* {
if(driver == "XShm") return new VideoXShm;
#endif
#if defined(VIDEO_XV)
if(driver == "X-Video") return new VideoXv;
#if defined(VIDEO_XVIDEO)
if(driver == "XVideo") return new VideoXVideo;
#endif
return new Video;
@ -133,8 +133,8 @@ auto Video::optimalDriver() -> string {
return "OpenGL";
#elif defined(VIDEO_GLX2)
return "OpenGL2";
#elif defined(VIDEO_XV)
return "X-Video";
#elif defined(VIDEO_XVIDEO)
return "XVideo";
#elif defined(VIDEO_XSHM)
return "XShm";
#elif defined(VIDEO_SDL)
@ -159,8 +159,8 @@ auto Video::safestDriver() -> string {
return "XShm";
#elif defined(VIDEO_SDL)
return "SDL";
#elif defined(VIDEO_XV)
return "X-Video";
#elif defined(VIDEO_XVIDEO)
return "XVideo";
#elif defined(VIDEO_GLX2)
return "OpenGL2";
#elif defined(VIDEO_GLX)
@ -201,8 +201,8 @@ auto Video::availableDrivers() -> string_vector {
"OpenGL2",
#endif
#if defined(VIDEO_XV)
"X-Video",
#if defined(VIDEO_XVIDEO)
"XVideo",
#endif
#if defined(VIDEO_XSHM)

View File

@ -6,9 +6,9 @@
extern "C" auto XvShmCreateImage(Display*, XvPortID, int, char*, int, int, XShmSegmentInfo*) -> XvImage*;
struct VideoXv : Video {
VideoXv() { initialize(); }
~VideoXv() { terminate(); }
struct VideoXVideo : Video {
VideoXVideo() { initialize(); }
~VideoXVideo() { terminate(); }
auto ready() -> bool { return _ready; }
@ -44,6 +44,8 @@ struct VideoXv : Video {
}
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
if(!_ready) return false;
if(width != _width || height != _height) {
resize(_width = width, _height = height);
}
@ -56,8 +58,7 @@ struct VideoXv : Video {
}
auto output() -> void {
uint width = _width;
uint height = _height;
if(!_ready) return;
XWindowAttributes target;
XGetWindowAttributes(_display, _window, &target);
@ -76,16 +77,18 @@ struct VideoXv : Video {
XGetWindowAttributes(_display, _window, &target);
switch(_format) {
case XvFormatRGB32: renderRGB32(width, height); break;
case XvFormatRGB24: renderRGB24(width, height); break;
case XvFormatRGB16: renderRGB16(width, height); break;
case XvFormatRGB15: renderRGB15(width, height); break;
case XvFormatYUY2: renderYUY2 (width, height); break;
case XvFormatUYVY: renderUYVY (width, height); break;
case XvFormatRGB32: renderRGB32(_width, _height); break;
case XvFormatRGB24: renderRGB24(_width, _height); break;
case XvFormatRGB16: renderRGB16(_width, _height); break;
case XvFormatRGB15: renderRGB15(_width, _height); break;
case XvFormatUYVY: renderUYVY (_width, _height); break;
case XvFormatYUY2: renderYUY2 (_width, _height); break;
case XvFormatYV12: renderYV12 (_width, _height); break;
case XvFormatI420: renderI420 (_width, _height); break;
}
XvShmPutImage(_display, _port, _window, _gc, _image,
0, 0, width, height,
0, 0, _width, _height,
0, 0, target.width, target.height,
true);
}
@ -157,9 +160,16 @@ struct VideoXv : Video {
_gc = XCreateGC(_display, _window, 0, 0);
//set colorkey to auto paint, so that Xv video output is always visible
Atom atom = XInternAtom(_display, "XV_AUTOPAINT_COLORKEY", true);
if(atom != None) XvSetPortAttribute(_display, _port, atom, 1);
int attributeCount = 0;
XvAttribute* attributeList = XvQueryPortAttributes(_display, _port, &attributeCount);
for(auto n : range(attributeCount)) {
if(string{attributeList[n].name} == "XV_AUTOPAINT_COLORKEY") {
//set colorkey to auto paint, so that Xv video output is always visible
Atom atom = XInternAtom(_display, "XV_AUTOPAINT_COLORKEY", true);
if(atom != None) XvSetPortAttribute(_display, _port, atom, 1);
}
}
XFree(attributeList);
//find optimal rendering format
_format = XvFormatUnknown;
@ -198,6 +208,18 @@ struct VideoXv : Video {
}
}
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
if(format[n].type == XvYUV && format[n].bits_per_pixel == 16 && format[n].format == XvPacked) {
if(format[n].component_order[0] == 'U' && format[n].component_order[1] == 'Y'
&& format[n].component_order[2] == 'V' && format[n].component_order[3] == 'Y'
) {
_format = XvFormatUYVY;
_fourCC = format[n].id;
break;
}
}
}
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
if(format[n].type == XvYUV && format[n].bits_per_pixel == 16 && format[n].format == XvPacked) {
if(format[n].component_order[0] == 'Y' && format[n].component_order[1] == 'U'
@ -211,11 +233,23 @@ struct VideoXv : Video {
}
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
if(format[n].type == XvYUV && format[n].bits_per_pixel == 16 && format[n].format == XvPacked) {
if(format[n].component_order[0] == 'U' && format[n].component_order[1] == 'Y'
&& format[n].component_order[2] == 'V' && format[n].component_order[3] == 'Y'
if(format[n].type == XvYUV && format[n].bits_per_pixel == 12 && format[n].format == XvPlanar) {
if(format[n].component_order[0] == 'Y' && format[n].component_order[1] == 'V'
&& format[n].component_order[2] == 'U' && format[n].component_order[3] == '\x00'
) {
_format = XvFormatUYVY;
_format = XvFormatYV12;
_fourCC = format[n].id;
break;
}
}
}
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
if(format[n].type == XvYUV && format[n].bits_per_pixel == 12 && format[n].format == XvPlanar) {
if(format[n].component_order[0] == 'Y' && format[n].component_order[1] == 'U'
&& format[n].component_order[2] == 'V' && format[n].component_order[3] == '\x00'
) {
_format = XvFormatI420;
_fourCC = format[n].id;
break;
}
@ -228,29 +262,11 @@ struct VideoXv : Video {
return false;
}
_bufferWidth = 256;
_bufferHeight = 256;
_image = XvShmCreateImage(_display, _port, _fourCC, 0, _bufferWidth, _bufferHeight, &_shmInfo);
if(!_image) {
print("VideoXv: XShmCreateImage failed.\n");
return false;
}
_shmInfo.shmid = shmget(IPC_PRIVATE, _image->data_size, IPC_CREAT | 0777);
_shmInfo.shmaddr = _image->data = (char*)shmat(_shmInfo.shmid, 0, 0);
_shmInfo.readOnly = false;
if(!XShmAttach(_display, &_shmInfo)) {
print("VideoXv: XShmAttach failed.\n");
return false;
}
_buffer = new uint32_t[_bufferWidth * _bufferHeight];
_width = 256;
_height = 256;
_ready = true;
initializeTables();
resize(_width = 256, _height = 256);
clear();
return _ready = true;
return true;
}
auto terminate() -> void {
@ -279,7 +295,7 @@ struct VideoXv : Video {
_display = nullptr;
}
delete[] _buffer, _buffer = nullptr;
delete[] _buffer, _buffer = nullptr, _bufferWidth = 0, _bufferHeight = 0;
delete[] _ytable, _ytable = nullptr;
delete[] _utable, _utable = nullptr;
delete[] _vtable, _vtable = nullptr;
@ -290,10 +306,19 @@ struct VideoXv : Video {
_bufferWidth = max(width, _bufferWidth);
_bufferHeight = max(height, _bufferHeight);
XShmDetach(_display, &_shmInfo);
shmdt(_shmInfo.shmaddr);
shmctl(_shmInfo.shmid, IPC_RMID, nullptr);
XFree(_image);
//must round to be evenly divisible by 4
if(uint round = _bufferWidth & 3) _bufferWidth += 4 - round;
if(uint round = _bufferHeight & 3) _bufferHeight += 4 - round;
_bufferWidth = bit::round(_bufferWidth);
_bufferHeight = bit::round(_bufferHeight);
if(_image) {
XShmDetach(_display, &_shmInfo);
shmdt(_shmInfo.shmaddr);
shmctl(_shmInfo.shmid, IPC_RMID, nullptr);
XFree(_image);
}
_image = XvShmCreateImage(_display, _port, _fourCC, 0, _bufferWidth, _bufferHeight, &_shmInfo);
@ -324,8 +349,8 @@ struct VideoXv : Video {
for(uint y : range(height)) {
for(uint x : range(width)) {
uint32_t p = *input++;
*output++ = p;
*output++ = p >> 8;
*output++ = p >> 0;
*output++ = p >> 8;
*output++ = p >> 16;
}
@ -340,8 +365,8 @@ struct VideoXv : Video {
for(uint y : range(height)) {
for(uint x : range(width)) {
uint32_t p = *input++;
*output++ = ((p >> 8) & 0xf800) | ((p >> 5) & 0x07e0) | ((p >> 3) & 0x001f); //RGB32->RGB16
uint32_t p = toRGB16(*input++);
*output++ = p;
}
input += _bufferWidth - width;
@ -355,31 +380,8 @@ struct VideoXv : Video {
for(uint y : range(height)) {
for(uint x : range(width)) {
uint32_t p = *input++;
*output++ = ((p >> 9) & 0x7c00) | ((p >> 6) & 0x03e0) | ((p >> 3) & 0x001f); //RGB32->RGB15
}
input += _bufferWidth - width;
output += _bufferWidth - width;
}
}
auto renderYUY2(uint width, uint height) -> void {
uint32_t* input = (uint32_t*)_buffer;
uint16_t* output = (uint16_t*)_image->data;
for(uint y : range(height)) {
for(uint x : range(width >> 1)) {
uint32_t p0 = *input++;
uint32_t p1 = *input++;
p0 = ((p0 >> 8) & 0xf800) + ((p0 >> 5) & 0x07e0) + ((p0 >> 3) & 0x001f); //RGB32->RGB16
p1 = ((p1 >> 8) & 0xf800) + ((p1 >> 5) & 0x07e0) + ((p1 >> 3) & 0x001f); //RGB32->RGB16
uint8_t u = (_utable[p0] + _utable[p1]) >> 1;
uint8_t v = (_vtable[p0] + _vtable[p1]) >> 1;
*output++ = (u << 8) | _ytable[p0];
*output++ = (v << 8) | _ytable[p1];
uint32_t p = toRGB15(*input++);
*output++ = p;
}
input += _bufferWidth - width;
@ -388,21 +390,16 @@ struct VideoXv : Video {
}
auto renderUYVY(uint width, uint height) -> void {
uint32_t* input = (uint32_t*)_buffer;
const uint32_t* input = (const uint32_t*)_buffer;
uint16_t* output = (uint16_t*)_image->data;
for(uint y : range(height)) {
for(uint x : range(width >> 1)) {
uint32_t p0 = *input++;
uint32_t p1 = *input++;
p0 = ((p0 >> 8) & 0xf800) + ((p0 >> 5) & 0x07e0) + ((p0 >> 3) & 0x001f);
p1 = ((p1 >> 8) & 0xf800) + ((p1 >> 5) & 0x07e0) + ((p1 >> 3) & 0x001f);
uint32_t p0 = toRGB16(*input++);
uint32_t p1 = toRGB16(*input++);
uint8_t u = (_utable[p0] + _utable[p1]) >> 1;
uint8_t v = (_vtable[p0] + _vtable[p1]) >> 1;
*output++ = (_ytable[p0] << 8) | u;
*output++ = (_ytable[p1] << 8) | v;
*output++ = _ytable[p0] << 8 | ((_utable[p0] + _utable[p1]) >> 1) << 0;
*output++ = _ytable[p1] << 8 | ((_vtable[p0] + _vtable[p1]) >> 1) << 0;
}
input += _bufferWidth - width;
@ -410,6 +407,82 @@ struct VideoXv : Video {
}
}
auto renderYUY2(uint width, uint height) -> void {
const uint32_t* input = (const uint32_t*)_buffer;
uint16_t* output = (uint16_t*)_image->data;
for(uint y : range(height)) {
for(uint x : range(width >> 1)) {
uint32_t p0 = toRGB16(*input++);
uint32_t p1 = toRGB16(*input++);
*output++ = ((_utable[p0] + _utable[p1]) >> 1) << 8 | _ytable[p0] << 0;
*output++ = ((_vtable[p0] + _vtable[p1]) >> 1) << 8 | _ytable[p1] << 0;
}
input += _bufferWidth - width;
output += _bufferWidth - width;
}
}
auto renderYV12(uint width, uint height) -> void {
const uint w = _bufferWidth, h = _bufferHeight;
for(uint y : range(height >> 1)) {
const uint32_t* input0 = (const uint32_t*)_buffer + (2 * y * w);
const uint32_t* input1 = input0 + w;
uint16_t* youtput0 = (uint16_t*)_image->data + ((2 * y * w) >> 1);
uint16_t* youtput1 = youtput0 + (w >> 1);
uint8_t* voutput = (uint8_t*)_image->data + (w * h) + ((2 * y * w) >> 2);
uint8_t* uoutput = (uint8_t*)_image->data + (w * h) + ((w * h) >> 2) + ((2 * y * w) >> 2);
for(uint x : range(width >> 1)) {
uint16_t p0 = toRGB16(*input0++);
uint16_t p1 = toRGB16(*input0++);
uint16_t p2 = toRGB16(*input1++);
uint16_t p3 = toRGB16(*input1++);
*youtput0++ = _ytable[p0] << 0 | _ytable[p1] << 8;
*youtput1++ = _ytable[p2] << 0 | _ytable[p3] << 8;
*voutput++ = (_vtable[p0] + _vtable[p1] + _vtable[p2] + _vtable[p3]) >> 2;
*uoutput++ = (_utable[p0] + _utable[p1] + _utable[p2] + _utable[p3]) >> 2;
}
}
}
auto renderI420(uint width, uint height) -> void {
const uint w = _bufferWidth, h = _bufferHeight;
for(uint y : range(height >> 1)) {
const uint32_t* input0 = (const uint32_t*)_buffer + (2 * y * w);
const uint32_t* input1 = input0 + w;
uint16_t* youtput0 = (uint16_t*)_image->data + ((2 * y * w) >> 1);
uint16_t* youtput1 = youtput0 + (w >> 1);
uint8_t* uoutput = (uint8_t*)_image->data + (w * h) + ((2 * y * w) >> 2);
uint8_t* voutput = (uint8_t*)_image->data + (w * h) + ((w * h) >> 2) + ((2 * y * w) >> 2);
for(uint x : range(width >> 1)) {
uint16_t p0 = toRGB16(*input0++);
uint16_t p1 = toRGB16(*input0++);
uint16_t p2 = toRGB16(*input1++);
uint16_t p3 = toRGB16(*input1++);
*youtput0++ = _ytable[p0] << 0 | _ytable[p1] << 8;
*youtput1++ = _ytable[p2] << 0 | _ytable[p3] << 8;
*uoutput++ = (_utable[p0] + _utable[p1] + _utable[p2] + _utable[p3]) >> 2;
*voutput++ = (_vtable[p0] + _vtable[p1] + _vtable[p2] + _vtable[p3]) >> 2;
}
}
}
inline auto toRGB15(uint32_t rgb32) const -> uint16_t {
return ((rgb32 >> 9) & 0x7c00) + ((rgb32 >> 6) & 0x03e0) + ((rgb32 >> 3) & 0x001f);
}
inline auto toRGB16(uint32_t rgb32) const -> uint16_t {
return ((rgb32 >> 8) & 0xf800) + ((rgb32 >> 5) & 0x07e0) + ((rgb32 >> 3) & 0x001f);
}
auto initializeTables() -> void {
_ytable = new uint8_t[65536];
_utable = new uint8_t[65536];
@ -460,8 +533,10 @@ struct VideoXv : Video {
XvFormatRGB24,
XvFormatRGB16,
XvFormatRGB15,
XvFormatYUY2,
XvFormatUYVY,
XvFormatYUY2,
XvFormatYV12,
XvFormatI420,
XvFormatUnknown,
};