mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-02 05:52:55 +02:00
Update to 20180728 release.
byuu says: Sigh, I seem to be spiraling a bit here ... but the work is very important. Hopefully I can get a solid WIP together soon. But for now... I've integrated dynamic rate control into ruby::Audio via setDynamic(bool) for now. It's very demanding, as you would expect. When it's not in use, I realized the OSS driver's performance was pretty bad due to calling write() for every sample for every channel. I implemented a tiny 256-sample buffer and bsnes went from 290fps to 330fps on my FreeBSD desktop. It may be possible to do the same buffering with DRC, but for now, I'm not doing so, and adjusting the audio input frequency on every sample. I also added ruby::Video::setFlush(bool), which is available only in the OpenGL drivers, and this causes glFinish() to be called after swapping display buffers. I really couldn't think of a good name for this, "hard GPU sync" sounds kind of silly. In my view, flush is what commits queued events. Eg fflush(). OpenGL of course treats glFlush differently (I really don't even know what the point of it is even after reading the manual ...), and then has glFinish ... meh, whatever. It's setFlush(bool) until I come up with something better. Also as expected, this one's a big hit to performance. To implement the DRC, I started putting helper functions into the ruby video/audio/input core classes. And then the XVideo driver started crashing. It took hours and hours and hours to track down the problem: you have to clear XSetWindowAttributes to zero before calling XCreateWindow. No amount of `--sync`, `gdb break gdk_x_error`, `-Og`, etc will make Xlib be even remotely helpful in debugging errors like this. The GLX, GLX2, and XVideo drivers basically worked by chance before. If the stack frame had the right memory cleared, it worked. Otherwise it'd crash with BadValue, and my changing things broke that condition on the XVideo driver. So this has been fixed in all three now. Once XVideo was running again, I realized that non-power of two video sizes were completely broken for the YUV formats. It took a while, but I managed to fix all of that as well. At this point, most of ruby is going to be broken outside of FreeBSD, as I still need to finish updating all the drivers.
This commit is contained in:
@@ -11,67 +11,88 @@ struct VideoGLX : Video, OpenGL {
|
||||
VideoGLX() { initialize(); }
|
||||
~VideoGLX() { terminate(); }
|
||||
|
||||
auto ready() -> bool { return _ready; }
|
||||
auto driver() -> string override { return "OpenGL"; }
|
||||
auto ready() -> bool override { return _ready; }
|
||||
|
||||
auto context() -> uintptr { return _context; }
|
||||
auto blocking() -> bool { return _blocking; }
|
||||
auto depth() -> uint { return _depth; }
|
||||
auto smooth() -> bool { return _smooth; }
|
||||
auto shader() -> string { return _shader; }
|
||||
auto availableFormats() -> vector<string> override {
|
||||
return {"R8G8B8", "R10G10B10"};
|
||||
}
|
||||
|
||||
auto setContext(uintptr context) -> bool {
|
||||
if(_context == context) return true;
|
||||
_context = context;
|
||||
auto hasContext() -> bool override { return true; }
|
||||
auto hasBlocking() -> bool override { return true; }
|
||||
auto hasFlush() -> bool override { return true; }
|
||||
auto hasFormat() -> bool override { return true; }
|
||||
auto hasSmooth() -> bool override { return true; }
|
||||
auto hasShader() -> bool override { return true; }
|
||||
|
||||
auto setContext(uintptr context) -> bool override {
|
||||
if(context == Video::context()) return true;
|
||||
if(!Video::setContext(context)) return false;
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto setBlocking(bool blocking) -> bool {
|
||||
if(_blocking == blocking) return true;
|
||||
_blocking = blocking;
|
||||
auto setBlocking(bool blocking) -> bool override {
|
||||
if(blocking == Video::blocking()) return true;
|
||||
if(!Video::setBlocking(blocking)) return false;
|
||||
if(glXSwapInterval) glXSwapInterval(_blocking);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto setDepth(uint depth) -> bool {
|
||||
if(_depth == depth) return true;
|
||||
switch(depth) {
|
||||
case 24: _depth = depth; OpenGL::inputFormat = GL_RGBA8; return true;
|
||||
case 30: _depth = depth; OpenGL::inputFormat = GL_RGB10_A2; return true;
|
||||
default: return false;
|
||||
auto setFlush(bool flush) -> bool override {
|
||||
if(flush == Video::flush()) return true;
|
||||
if(!Video::setFlush(flush)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto setFormat(string format) -> bool override {
|
||||
if(format == Video::format()) return true;
|
||||
if(!Video::setFormat(format)) return false;
|
||||
|
||||
if(format == "R8G8B8") {
|
||||
OpenGL::inputFormat = GL_RGBA8;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(format == "R10G10B10") {
|
||||
OpenGL::inputFormat = GL_RGB10_A2;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto setSmooth(bool smooth) -> bool {
|
||||
if(_smooth == smooth) return true;
|
||||
_smooth = smooth;
|
||||
if(!_shader) OpenGL::filter = _smooth ? GL_LINEAR : GL_NEAREST;
|
||||
auto setSmooth(bool smooth) -> bool override {
|
||||
if(smooth == Video::smooth()) return true;
|
||||
if(!Video::setSmooth(smooth)) return false;
|
||||
if(!shader()) OpenGL::filter = smooth ? GL_LINEAR : GL_NEAREST;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto setShader(string shader) -> bool {
|
||||
if(_shader == shader) return true;
|
||||
OpenGL::shader(_shader = shader);
|
||||
if(!_shader) OpenGL::filter = _smooth ? GL_LINEAR : GL_NEAREST;
|
||||
auto setShader(string shader) -> bool override {
|
||||
if(shader == Video::shader()) return true;
|
||||
if(!Video::setShader(shader)) return false;
|
||||
OpenGL::setShader(shader);
|
||||
if(!shader) OpenGL::filter = smooth() ? GL_LINEAR : GL_NEAREST;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto clear() -> void {
|
||||
auto clear() -> void override {
|
||||
if(!ready()) return;
|
||||
OpenGL::clear();
|
||||
if(_doubleBuffer) glXSwapBuffers(_display, _glXWindow);
|
||||
}
|
||||
|
||||
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
|
||||
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
|
||||
if(!ready()) return false;
|
||||
OpenGL::size(width, height);
|
||||
return OpenGL::lock(data, pitch);
|
||||
}
|
||||
|
||||
auto unlock() -> void {
|
||||
auto unlock() -> void override {
|
||||
if(!ready()) return;
|
||||
}
|
||||
|
||||
auto output() -> void {
|
||||
auto output() -> void override {
|
||||
if(!ready()) return;
|
||||
|
||||
//we must ensure that the child window is the same size as the parent window.
|
||||
@@ -89,9 +110,10 @@ struct VideoGLX : Video, OpenGL {
|
||||
OpenGL::outputHeight = parent.height;
|
||||
OpenGL::output();
|
||||
if(_doubleBuffer) glXSwapBuffers(_display, _glXWindow);
|
||||
if(flush()) glFinish();
|
||||
}
|
||||
|
||||
auto poll() -> void {
|
||||
auto poll() -> void override {
|
||||
while(XPending(_display)) {
|
||||
XEvent event;
|
||||
XNextEvent(_display, &event);
|
||||
@@ -118,15 +140,19 @@ private:
|
||||
XWindowAttributes windowAttributes;
|
||||
XGetWindowAttributes(_display, (Window)_context, &windowAttributes);
|
||||
|
||||
int redDepth = Video::format() == "R10G10B10" ? 10 : 8;
|
||||
int greenDepth = Video::format() == "R10G10B10" ? 10 : 8;
|
||||
int blueDepth = Video::format() == "R10G10B10" ? 10 : 8;
|
||||
|
||||
//let GLX determine the best Visual to use for GL output; provide a few hints
|
||||
//note: some video drivers will override double buffering attribute
|
||||
int attributeList[] = {
|
||||
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
|
||||
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
||||
GLX_DOUBLEBUFFER, True,
|
||||
GLX_RED_SIZE, (int)(_depth / 3),
|
||||
GLX_GREEN_SIZE, (int)(_depth / 3) + (int)(_depth % 3),
|
||||
GLX_BLUE_SIZE, (int)(_depth / 3),
|
||||
GLX_RED_SIZE, redDepth,
|
||||
GLX_GREEN_SIZE, greenDepth,
|
||||
GLX_BLUE_SIZE, blueDepth,
|
||||
None
|
||||
};
|
||||
|
||||
@@ -141,7 +167,7 @@ private:
|
||||
//it is not possible to change the Visual of an already realized (created) window.
|
||||
//therefore a new child window, using the same GLX Visual, must be created and binded to it.
|
||||
_colormap = XCreateColormap(_display, RootWindow(_display, vi->screen), vi->visual, AllocNone);
|
||||
XSetWindowAttributes attributes;
|
||||
XSetWindowAttributes attributes = {};
|
||||
attributes.colormap = _colormap;
|
||||
attributes.border_pixel = 0;
|
||||
_window = XCreateWindow(_display, /* parent = */ (Window)_context,
|
||||
@@ -230,11 +256,6 @@ private:
|
||||
}
|
||||
|
||||
bool _ready = false;
|
||||
uintptr _context = 0;
|
||||
bool _blocking = false;
|
||||
uint _depth = 24;
|
||||
bool _smooth = true;
|
||||
string _shader;
|
||||
|
||||
auto (*glXSwapInterval)(int) -> int = nullptr;
|
||||
|
||||
|
@@ -25,32 +25,40 @@ struct VideoGLX2 : Video {
|
||||
VideoGLX2() { initialize(); }
|
||||
~VideoGLX2() { terminate(); }
|
||||
|
||||
auto ready() -> bool { return _ready; }
|
||||
auto driver() -> string override { return "OpenGL2"; }
|
||||
auto ready() -> bool override { return _ready; }
|
||||
|
||||
auto context() -> uintptr { return _context; }
|
||||
auto blocking() -> bool { return _blocking; }
|
||||
auto smooth() -> bool { return _smooth; }
|
||||
auto hasContext() -> bool override { return true; }
|
||||
auto hasBlocking() -> bool override { return true; }
|
||||
auto hasFlush() -> bool override { return true; }
|
||||
auto hasSmooth() -> bool override { return true; }
|
||||
|
||||
auto setContext(uintptr context) -> bool {
|
||||
if(_context == context) return true;
|
||||
_context = context;
|
||||
auto setContext(uintptr context) -> bool override {
|
||||
if(context == this->context()) return true;
|
||||
if(!Video::setContext(context)) return false;
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto setBlocking(bool blocking) -> bool {
|
||||
if(_blocking == blocking) return true;
|
||||
_blocking = blocking;
|
||||
if(_ready && glXSwapInterval) glXSwapInterval(_blocking);
|
||||
auto setBlocking(bool blocking) -> bool override {
|
||||
if(blocking == this->blocking()) return true;
|
||||
if(!Video::setBlocking(blocking)) return false;
|
||||
if(ready() && glXSwapInterval) glXSwapInterval(_blocking);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto setSmooth(bool smooth) -> bool {
|
||||
if(_smooth == smooth) return true;
|
||||
_smooth = smooth;
|
||||
auto setFlush(bool flush) -> bool override {
|
||||
if(flush == this->flush()) return true;
|
||||
if(!Video::setFlush(flush)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto clear() -> void {
|
||||
auto setSmooth(bool smooth) -> bool override {
|
||||
if(smooth == this->smooth()) return true;
|
||||
if(!Video::setSmooth(smooth)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto clear() -> void override {
|
||||
if(!ready()) return;
|
||||
memory::fill<uint32_t>(_glBuffer, _glWidth * _glHeight);
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
@@ -59,18 +67,18 @@ struct VideoGLX2 : Video {
|
||||
if(_isDoubleBuffered) glXSwapBuffers(_display, _glXWindow);
|
||||
}
|
||||
|
||||
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
|
||||
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
|
||||
if(!ready()) return false;
|
||||
if(width != _width || height != _height) resize(width, height);
|
||||
pitch = _glWidth * sizeof(uint32_t);
|
||||
return data = _glBuffer;
|
||||
}
|
||||
|
||||
auto unlock() -> void {
|
||||
auto unlock() -> void override {
|
||||
if(!ready()) return;
|
||||
}
|
||||
|
||||
auto output() -> void {
|
||||
auto output() -> void override {
|
||||
if(!ready()) return;
|
||||
|
||||
XWindowAttributes parent, child;
|
||||
@@ -109,9 +117,10 @@ struct VideoGLX2 : Video {
|
||||
glFlush();
|
||||
|
||||
if(_isDoubleBuffered) glXSwapBuffers(_display, _glXWindow);
|
||||
if(flush()) glFinish();
|
||||
}
|
||||
|
||||
auto poll() -> void {
|
||||
auto poll() -> void override {
|
||||
while(XPending(_display)) {
|
||||
XEvent event;
|
||||
XNextEvent(_display, &event);
|
||||
@@ -154,7 +163,7 @@ private:
|
||||
|
||||
auto vi = glXGetVisualFromFBConfig(_display, fbConfig[0]);
|
||||
_colormap = XCreateColormap(_display, RootWindow(_display, vi->screen), vi->visual, AllocNone);
|
||||
XSetWindowAttributes attributes;
|
||||
XSetWindowAttributes attributes = {};
|
||||
attributes.colormap = _colormap;
|
||||
attributes.border_pixel = 0;
|
||||
_window = XCreateWindow(_display, (Window)_context, 0, 0, windowAttributes.width, windowAttributes.height,
|
||||
@@ -250,9 +259,6 @@ private:
|
||||
auto (*glXSwapInterval)(int) -> int = nullptr;
|
||||
|
||||
bool _ready = false;
|
||||
uintptr _context = 0;
|
||||
bool _blocking = false;
|
||||
bool _smooth = true;
|
||||
|
||||
Display* _display = nullptr;
|
||||
int _screen = 0;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
auto OpenGL::shader(const string& pathname) -> void {
|
||||
auto OpenGL::setShader(const string& pathname) -> void {
|
||||
for(auto& program : programs) program.release();
|
||||
programs.reset();
|
||||
|
||||
@@ -196,13 +196,13 @@ auto OpenGL::initialize() -> bool {
|
||||
OpenGLSurface::allocate();
|
||||
glrLinkProgram(program);
|
||||
|
||||
shader("");
|
||||
setShader("");
|
||||
return initialized = true;
|
||||
}
|
||||
|
||||
auto OpenGL::terminate() -> void {
|
||||
if(!initialized) return;
|
||||
shader(""); //release shader resources (eg frame[] history)
|
||||
setShader(""); //release shader resources (eg frame[] history)
|
||||
OpenGLSurface::release();
|
||||
if(buffer) { delete[] buffer; buffer = nullptr; }
|
||||
initialized = false;
|
||||
|
@@ -65,7 +65,7 @@ struct OpenGLProgram : OpenGLSurface {
|
||||
};
|
||||
|
||||
struct OpenGL : OpenGLProgram {
|
||||
auto shader(const string& pathname) -> void;
|
||||
auto setShader(const string& pathname) -> void;
|
||||
auto allocateHistory(uint size) -> void;
|
||||
auto clear() -> void;
|
||||
auto lock(uint32_t*& data, uint& pitch) -> bool;
|
||||
|
@@ -12,23 +12,25 @@ struct VideoXShm : Video {
|
||||
VideoXShm() { initialize(); }
|
||||
~VideoXShm() { terminate(); }
|
||||
|
||||
auto ready() -> bool { return _ready; }
|
||||
auto driver() -> string override { return "XShm"; }
|
||||
auto ready() -> bool override { return _ready; }
|
||||
|
||||
auto context() -> uintptr { return _context; }
|
||||
auto smooth() -> bool { return _smooth; }
|
||||
auto hasContext() -> bool override { return true; }
|
||||
auto hasSmooth() -> bool override { return true; }
|
||||
|
||||
auto setContext(uintptr context) -> bool {
|
||||
if(_context == context) return true;
|
||||
_context = context;
|
||||
auto setContext(uintptr context) -> bool override {
|
||||
if(context == this->context()) return true;
|
||||
if(!Video::setContext(context)) return false;
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto setSmooth(bool smooth) -> bool {
|
||||
_smooth = smooth;
|
||||
auto setSmooth(bool smooth) -> bool override {
|
||||
if(smooth == this->smooth()) return true;
|
||||
if(!Video::setSmooth(smooth)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto clear() -> void {
|
||||
auto clear() -> void override {
|
||||
if(!ready()) return;
|
||||
auto dp = _inputBuffer;
|
||||
uint length = _inputWidth * _inputHeight;
|
||||
@@ -36,7 +38,7 @@ struct VideoXShm : Video {
|
||||
output();
|
||||
}
|
||||
|
||||
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
|
||||
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
|
||||
if(!ready()) return false;
|
||||
if(!_inputBuffer || _inputWidth != width || _inputHeight != height) {
|
||||
if(_inputBuffer) delete[] _inputBuffer;
|
||||
@@ -50,11 +52,11 @@ struct VideoXShm : Video {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto unlock() -> void {
|
||||
auto unlock() -> void override {
|
||||
if(!ready()) return;
|
||||
}
|
||||
|
||||
auto output() -> void {
|
||||
auto output() -> void override {
|
||||
if(!ready()) return;
|
||||
size();
|
||||
|
||||
@@ -190,8 +192,6 @@ private:
|
||||
}
|
||||
|
||||
bool _ready = false;
|
||||
uintptr _context = 0;
|
||||
bool _smooth = true;
|
||||
|
||||
uint32_t* _inputBuffer = nullptr;
|
||||
uint _inputWidth = 0;
|
||||
|
@@ -10,20 +10,26 @@ struct VideoXVideo : Video {
|
||||
VideoXVideo() { initialize(); }
|
||||
~VideoXVideo() { terminate(); }
|
||||
|
||||
auto ready() -> bool { return _ready; }
|
||||
auto driver() -> string override { return "XVideo"; }
|
||||
auto ready() -> bool override { return _ready; }
|
||||
|
||||
auto context() -> uintptr { return _context; }
|
||||
auto blocking() -> bool { return _blocking; }
|
||||
auto availableFormats() -> vector<string> override {
|
||||
return _formatNames;
|
||||
}
|
||||
|
||||
auto setContext(uintptr context) -> bool {
|
||||
if(_context == context) return true;
|
||||
_context = context;
|
||||
auto hasContext() -> bool override { return true; }
|
||||
auto hasBlocking() -> bool override { return true; }
|
||||
auto hasFormat() -> bool override { return true; }
|
||||
|
||||
auto setContext(uintptr context) -> bool override {
|
||||
if(context == Video::context()) return true;
|
||||
if(!Video::setContext(context)) return false;
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto setBlocking(bool blocking) -> bool {
|
||||
if(_blocking == blocking) return true;
|
||||
_blocking = blocking;
|
||||
auto setBlocking(bool blocking) -> bool override {
|
||||
if(blocking == Video::blocking()) return true;
|
||||
if(!Video::setBlocking(blocking)) return false;
|
||||
|
||||
bool result = false;
|
||||
Display* display = XOpenDisplay(nullptr);
|
||||
@@ -36,7 +42,13 @@ struct VideoXVideo : Video {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto clear() -> void {
|
||||
auto setFormat(string format) -> bool override {
|
||||
if(format == Video::format()) return true;
|
||||
if(!Video::setFormat(format)) return false;
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto clear() -> void override {
|
||||
if(!ready()) return;
|
||||
memory::fill<uint32_t>(_buffer, _bufferWidth * _bufferHeight);
|
||||
//clear twice in case video is double buffered ...
|
||||
@@ -44,18 +56,18 @@ struct VideoXVideo : Video {
|
||||
output();
|
||||
}
|
||||
|
||||
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
|
||||
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
|
||||
if(!ready()) return false;
|
||||
if(width != _width || height != _height) resize(_width = width, _height = height);
|
||||
pitch = _bufferWidth * 4;
|
||||
return data = _buffer;
|
||||
}
|
||||
|
||||
auto unlock() -> void {
|
||||
auto unlock() -> void override {
|
||||
if(!ready()) return;
|
||||
}
|
||||
|
||||
auto output() -> void {
|
||||
auto output() -> void override {
|
||||
if(!ready()) return;
|
||||
|
||||
XWindowAttributes target;
|
||||
@@ -74,16 +86,15 @@ struct VideoXVideo : Video {
|
||||
//update target width and height attributes
|
||||
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 XvFormatUYVY: renderUYVY (_width, _height); break;
|
||||
case XvFormatYUY2: renderYUY2 (_width, _height); break;
|
||||
case XvFormatYV12: renderYV12 (_width, _height); break;
|
||||
case XvFormatI420: renderI420 (_width, _height); break;
|
||||
}
|
||||
auto& name = _formatName;
|
||||
if(name == "RGB32") renderRGB32(_width, _height);
|
||||
if(name == "RGB24") renderRGB24(_width, _height);
|
||||
if(name == "RGB16") renderRGB16(_width, _height);
|
||||
if(name == "RGB15") renderRGB15(_width, _height);
|
||||
if(name == "UYVY" ) renderUYVY (_width, _height);
|
||||
if(name == "YUY2" ) renderYUY2 (_width, _height);
|
||||
if(name == "YV12" ) renderYV12 (_width, _height);
|
||||
if(name == "I420" ) renderI420 (_width, _height);
|
||||
|
||||
XvShmPutImage(_display, _port, _window, _gc, _image,
|
||||
0, 0, _width, _height,
|
||||
@@ -91,7 +102,7 @@ struct VideoXVideo : Video {
|
||||
true);
|
||||
}
|
||||
|
||||
auto poll() -> void {
|
||||
auto poll() -> void override {
|
||||
while(XPending(_display)) {
|
||||
XEvent event;
|
||||
XNextEvent(_display, &event);
|
||||
@@ -111,12 +122,14 @@ private:
|
||||
_display = XOpenDisplay(nullptr);
|
||||
|
||||
if(!XShmQueryExtension(_display)) {
|
||||
print("VideoXv: XShm extension not found.\n");
|
||||
print("XVideo: XShm extension not found.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//find an appropriate Xv port
|
||||
_port = -1;
|
||||
int depth = 0;
|
||||
int visualID = 0;
|
||||
XvAdaptorInfo* adaptorInfo = nullptr;
|
||||
uint adaptorCount = 0;
|
||||
XvQueryAdaptors(_display, DefaultRootWindow(_display), &adaptorCount, &adaptorInfo);
|
||||
@@ -127,42 +140,42 @@ private:
|
||||
if(!(adaptorInfo[n].type & XvImageMask)) continue;
|
||||
|
||||
_port = adaptorInfo[n].base_id;
|
||||
_depth = adaptorInfo[n].formats->depth;
|
||||
_visualID = adaptorInfo[n].formats->visual_id;
|
||||
depth = adaptorInfo[n].formats->depth;
|
||||
visualID = adaptorInfo[n].formats->visual_id;
|
||||
break;
|
||||
}
|
||||
XvFreeAdaptorInfo(adaptorInfo);
|
||||
if(_port < 0) {
|
||||
print("VideoXv: failed to find valid XvPort.\n");
|
||||
print("XVideo: failed to find valid XvPort.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//create child window to attach to parent window.
|
||||
//this is so that even if parent window visual depth doesn't match Xv visual
|
||||
//(common with composited windows), Xv can still render to child window.
|
||||
XWindowAttributes window_attributes;
|
||||
XGetWindowAttributes(_display, (Window)_context, &window_attributes);
|
||||
XWindowAttributes windowAttributes;
|
||||
XGetWindowAttributes(_display, (Window)_context, &windowAttributes);
|
||||
|
||||
XVisualInfo visualTemplate;
|
||||
visualTemplate.visualid = _visualID;
|
||||
visualTemplate.visualid = visualID;
|
||||
visualTemplate.screen = DefaultScreen(_display);
|
||||
visualTemplate.depth = _depth;
|
||||
visualTemplate.depth = depth;
|
||||
visualTemplate.visual = 0;
|
||||
int visualMatches = 0;
|
||||
XVisualInfo* visualInfo = XGetVisualInfo(_display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualTemplate, &visualMatches);
|
||||
auto visualInfo = XGetVisualInfo(_display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualTemplate, &visualMatches);
|
||||
if(visualMatches < 1 || !visualInfo->visual) {
|
||||
if(visualInfo) XFree(visualInfo);
|
||||
print("VideoXv: unable to find Xv-compatible visual.\n");
|
||||
print("XVideo: unable to find Xv-compatible visual.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
_colormap = XCreateColormap(_display, (Window)_context, visualInfo->visual, AllocNone);
|
||||
XSetWindowAttributes attributes;
|
||||
XSetWindowAttributes attributes = {};
|
||||
attributes.colormap = _colormap;
|
||||
attributes.border_pixel = 0;
|
||||
_window = XCreateWindow(_display, /* parent = */ (Window)_context,
|
||||
/* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height,
|
||||
/* border_width = */ 0, _depth, InputOutput, visualInfo->visual,
|
||||
/* x = */ 0, /* y = */ 0, windowAttributes.width, windowAttributes.height,
|
||||
/* border_width = */ 0, depth, InputOutput, visualInfo->visual,
|
||||
CWColormap | CWBorderPixel | CWEventMask, &attributes);
|
||||
XSelectInput(_display, _window, ExposureMask);
|
||||
XFree(visualInfo);
|
||||
@@ -172,7 +185,7 @@ private:
|
||||
_gc = XCreateGC(_display, _window, 0, 0);
|
||||
|
||||
int attributeCount = 0;
|
||||
XvAttribute* attributeList = XvQueryPortAttributes(_display, _port, &attributeCount);
|
||||
auto 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
|
||||
@@ -182,96 +195,19 @@ private:
|
||||
}
|
||||
XFree(attributeList);
|
||||
|
||||
//find optimal rendering format
|
||||
_format = XvFormatUnknown;
|
||||
int formatCount = 0;
|
||||
XvImageFormatValues* format = XvListImageFormats(_display, _port, &formatCount);
|
||||
|
||||
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
|
||||
if(format[n].type == XvRGB && format[n].bits_per_pixel == 32) {
|
||||
_format = XvFormatRGB32;
|
||||
_fourCC = format[n].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
|
||||
if(format[n].type == XvRGB && format[n].bits_per_pixel == 24) {
|
||||
_format = XvFormatRGB24;
|
||||
_fourCC = format[n].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
|
||||
if(format[n].type == XvRGB && format[n].bits_per_pixel <= 16 && format[n].red_mask == 0xf800) {
|
||||
_format = XvFormatRGB16;
|
||||
_fourCC = format[n].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
|
||||
if(format[n].type == XvRGB && format[n].bits_per_pixel <= 16 && format[n].red_mask == 0x7c00) {
|
||||
_format = XvFormatRGB15;
|
||||
_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] == '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'
|
||||
&& format[n].component_order[2] == 'Y' && format[n].component_order[3] == 'V'
|
||||
) {
|
||||
_format = XvFormatYUY2;
|
||||
_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] == 'V'
|
||||
&& format[n].component_order[2] == 'U' && format[n].component_order[3] == '\x00'
|
||||
) {
|
||||
_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(format);
|
||||
if(_format == XvFormatUnknown) {
|
||||
print("VideoXv: unable to find a supported image format.\n");
|
||||
queryAvailableFormats();
|
||||
if(!_formatNames) {
|
||||
print("XVideo: unable to find a supported image format.\n");
|
||||
return false;
|
||||
}
|
||||
if(auto match = _formatNames.find(Video::format())) {
|
||||
_formatID = _formatIDs[match()];
|
||||
_formatName = _formatNames[match()];
|
||||
} else {
|
||||
_formatID = _formatIDs[0];
|
||||
_formatName = _formatNames[0];
|
||||
Video::setFormat(_formatName);
|
||||
}
|
||||
|
||||
_ready = true;
|
||||
initializeTables();
|
||||
@@ -291,6 +227,11 @@ private:
|
||||
_image = nullptr;
|
||||
}
|
||||
|
||||
if(_gc) {
|
||||
XFreeGC(_display, _gc);
|
||||
_gc = 0;
|
||||
}
|
||||
|
||||
if(_window) {
|
||||
XUnmapWindow(_display, _window);
|
||||
_window = 0;
|
||||
@@ -312,6 +253,49 @@ private:
|
||||
delete[] _vtable, _vtable = nullptr;
|
||||
}
|
||||
|
||||
auto queryAvailableFormats() -> void {
|
||||
auto& ids = _formatIDs;
|
||||
auto& names = _formatNames;
|
||||
|
||||
ids.reset();
|
||||
names.reset();
|
||||
|
||||
int count = 0;
|
||||
auto array = XvListImageFormats(_display, _port, &count);
|
||||
|
||||
for(uint sort : range(8)) {
|
||||
for(uint n : range(count)) {
|
||||
auto id = array[n].id;
|
||||
auto type = array[n].type;
|
||||
auto format = array[n].format;
|
||||
auto depth = array[n].bits_per_pixel;
|
||||
auto redMask = array[n].red_mask;
|
||||
auto order = array[n].component_order;
|
||||
string components;
|
||||
for(uint n : range(4)) if(char c = order[n]) components.append(c);
|
||||
|
||||
if(type == XvRGB) {
|
||||
if(sort == 0 && depth == 32) ids.append(id), names.append("RGB32");
|
||||
if(sort == 1 && depth == 24) ids.append(id), names.append("RGB24");
|
||||
if(sort == 2 && depth <= 16 && redMask == 0xf800) ids.append(id), names.append("RGB16");
|
||||
if(sort == 3 && depth <= 16 && redMask == 0x7c00) ids.append(id), names.append("RGB15");
|
||||
}
|
||||
|
||||
if(type == XvYUV && format == XvPacked) {
|
||||
if(sort == 4 && depth == 16 && components == "UYVY") ids.append(id), names.append("UYVY");
|
||||
if(sort == 5 && depth == 16 && components == "YUYV") ids.append(id), names.append("YUY2");
|
||||
}
|
||||
|
||||
if(type == XvYUV && format == XvPlanar) {
|
||||
if(sort == 6 && depth == 12 && components == "YVU" ) ids.append(id), names.append("YV12");
|
||||
if(sort == 7 && depth == 12 && components == "YUV" ) ids.append(id), names.append("I420");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(array);
|
||||
}
|
||||
|
||||
auto resize(uint width, uint height) -> void {
|
||||
if(_bufferWidth >= width && _bufferHeight >= height) return;
|
||||
_bufferWidth = max(width, _bufferWidth);
|
||||
@@ -331,7 +315,7 @@ private:
|
||||
XFree(_image);
|
||||
}
|
||||
|
||||
_image = XvShmCreateImage(_display, _port, _fourCC, 0, _bufferWidth, _bufferHeight, &_shmInfo);
|
||||
_image = XvShmCreateImage(_display, _port, _formatID, 0, _bufferWidth, _bufferHeight, &_shmInfo);
|
||||
|
||||
_shmInfo.shmid = shmget(IPC_PRIVATE, _image->data_size, IPC_CREAT | 0777);
|
||||
_shmInfo.shmaddr = _image->data = (char*)shmat(_shmInfo.shmid, 0, 0);
|
||||
@@ -343,38 +327,36 @@ private:
|
||||
}
|
||||
|
||||
auto renderRGB32(uint width, uint height) -> void {
|
||||
uint32_t* input = (uint32_t*)_buffer;
|
||||
uint32_t* output = (uint32_t*)_image->data;
|
||||
|
||||
for(uint y : range(height)) {
|
||||
memory::copy<uint32_t>(output, input, width);
|
||||
input += _bufferWidth;
|
||||
output += _bufferWidth;
|
||||
auto input = (const uint32_t*)_buffer + y * width;
|
||||
auto output = (uint32_t*)_image->data + y * (_image->pitches[0] >> 2);
|
||||
|
||||
for(uint x : range(width)) {
|
||||
uint32_t p = *input++;
|
||||
*output++ = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto renderRGB24(uint width, uint height) -> void {
|
||||
uint32_t* input = (uint32_t*)_buffer;
|
||||
uint8_t* output = (uint8_t*)_image->data;
|
||||
|
||||
for(uint y : range(height)) {
|
||||
auto input = (const uint32_t*)_buffer + y * width;
|
||||
auto output = (uint8_t*)_image->data + y * _image->pitches[0];
|
||||
|
||||
for(uint x : range(width)) {
|
||||
uint32_t p = *input++;
|
||||
*output++ = p >> 0;
|
||||
*output++ = p >> 8;
|
||||
*output++ = p >> 16;
|
||||
}
|
||||
|
||||
input += (_bufferWidth - width);
|
||||
output += (_bufferWidth - width) * 3;
|
||||
}
|
||||
}
|
||||
|
||||
auto renderRGB16(uint width, uint height) -> void {
|
||||
uint32_t* input = (uint32_t*)_buffer;
|
||||
uint16_t* output = (uint16_t*)_image->data;
|
||||
|
||||
for(uint y : range(height)) {
|
||||
auto input = (const uint32_t*)_buffer + y * width;
|
||||
auto output = (uint16_t*)_image->data + y * (_image->pitches[0] >> 1);
|
||||
|
||||
for(uint x : range(width)) {
|
||||
uint32_t p = toRGB16(*input++);
|
||||
*output++ = p;
|
||||
@@ -386,25 +368,22 @@ private:
|
||||
}
|
||||
|
||||
auto renderRGB15(uint width, uint height) -> void {
|
||||
uint32_t* input = (uint32_t*)_buffer;
|
||||
uint16_t* output = (uint16_t*)_image->data;
|
||||
|
||||
for(uint y : range(height)) {
|
||||
auto input = (const uint32_t*)_buffer + y * width;
|
||||
auto output = (uint16_t*)_image->data + y * (_image->pitches[0] >> 1);
|
||||
|
||||
for(uint x : range(width)) {
|
||||
uint32_t p = toRGB15(*input++);
|
||||
*output++ = p;
|
||||
}
|
||||
|
||||
input += _bufferWidth - width;
|
||||
output += _bufferWidth - width;
|
||||
}
|
||||
}
|
||||
|
||||
auto renderUYVY(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)) {
|
||||
auto input = (const uint32_t*)_buffer + y * width;
|
||||
auto output = (uint16_t*)_image->data + y * (_image->pitches[0] >> 1);
|
||||
|
||||
for(uint x : range(width >> 1)) {
|
||||
uint32_t p0 = toRGB16(*input++);
|
||||
uint32_t p1 = toRGB16(*input++);
|
||||
@@ -412,17 +391,14 @@ private:
|
||||
*output++ = _ytable[p0] << 8 | ((_utable[p0] + _utable[p1]) >> 1) << 0;
|
||||
*output++ = _ytable[p1] << 8 | ((_vtable[p0] + _vtable[p1]) >> 1) << 0;
|
||||
}
|
||||
|
||||
input += _bufferWidth - width;
|
||||
output += _bufferWidth - width;
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
auto input = (const uint32_t*)_buffer + y * width;
|
||||
auto output = (uint16_t*)_image->data + y * (_image->pitches[0] >> 1);
|
||||
|
||||
for(uint x : range(width >> 1)) {
|
||||
uint32_t p0 = toRGB16(*input++);
|
||||
uint32_t p1 = toRGB16(*input++);
|
||||
@@ -430,22 +406,17 @@ private:
|
||||
*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);
|
||||
auto input0 = (const uint32_t*)_buffer + (2 * y + 0) * width;
|
||||
auto input1 = (const uint32_t*)_buffer + (2 * y + 1) * width;
|
||||
auto youtput0 = (uint16_t*)_image->data + (_image->offsets[0] >> 1) + (2 * y + 0) * (_image->pitches[0] >> 1);
|
||||
auto youtput1 = (uint16_t*)_image->data + (_image->offsets[0] >> 1) + (2 * y + 1) * (_image->pitches[0] >> 1);
|
||||
auto voutput = (uint8_t*)_image->data + _image->offsets[1] + y * _image->pitches[1];
|
||||
auto uoutput = (uint8_t*)_image->data + _image->offsets[2] + y * _image->pitches[2];
|
||||
|
||||
for(uint x : range(width >> 1)) {
|
||||
uint16_t p0 = toRGB16(*input0++);
|
||||
@@ -462,15 +433,13 @@ private:
|
||||
}
|
||||
|
||||
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);
|
||||
auto input0 = (const uint32_t*)_buffer + (2 * y + 0) * width;
|
||||
auto input1 = (const uint32_t*)_buffer + (2 * y + 1) * width;
|
||||
auto youtput0 = (uint16_t*)_image->data + (_image->offsets[0] >> 1) + (2 * y + 0) * (_image->pitches[0] >> 1);
|
||||
auto youtput1 = (uint16_t*)_image->data + (_image->offsets[0] >> 1) + (2 * y + 1) * (_image->pitches[0] >> 1);
|
||||
auto uoutput = (uint8_t*)_image->data + _image->offsets[1] + y * _image->pitches[1];
|
||||
auto voutput = (uint8_t*)_image->data + _image->offsets[2] + y * _image->pitches[2];
|
||||
|
||||
for(uint x : range(width >> 1)) {
|
||||
uint16_t p0 = toRGB16(*input0++);
|
||||
@@ -525,8 +494,6 @@ private:
|
||||
}
|
||||
|
||||
bool _ready = false;
|
||||
uintptr _context = 0;
|
||||
bool _blocking = false;
|
||||
|
||||
uint _width = 0;
|
||||
uint _height = 0;
|
||||
@@ -539,18 +506,6 @@ private:
|
||||
uint8_t* _utable = nullptr;
|
||||
uint8_t* _vtable = nullptr;
|
||||
|
||||
enum XvFormat : uint {
|
||||
XvFormatRGB32,
|
||||
XvFormatRGB24,
|
||||
XvFormatRGB16,
|
||||
XvFormatRGB15,
|
||||
XvFormatUYVY,
|
||||
XvFormatYUY2,
|
||||
XvFormatYV12,
|
||||
XvFormatI420,
|
||||
XvFormatUnknown,
|
||||
};
|
||||
|
||||
Display* _display = nullptr;
|
||||
GC _gc = 0;
|
||||
Window _window = 0;
|
||||
@@ -558,10 +513,11 @@ private:
|
||||
XShmSegmentInfo _shmInfo;
|
||||
|
||||
int _port = -1;
|
||||
int _depth = 0;
|
||||
int _visualID = 0;
|
||||
|
||||
XvImage* _image = nullptr;
|
||||
XvFormat _format = XvFormatUnknown;
|
||||
uint32_t _fourCC = 0;
|
||||
|
||||
vector<int> _formatIDs;
|
||||
vector<string> _formatNames;
|
||||
|
||||
int _formatID = 0;
|
||||
string _formatName;
|
||||
};
|
||||
|
Reference in New Issue
Block a user