diff options
80 files changed, 17772 insertions, 0 deletions
@@ -0,0 +1 @@ +Gwenole Beauchesne <gbeauchesne@splitted-desktop.com> - Primary author @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..cd643f9 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,57 @@ +AUTOMAKE_OPTIONS = foreign + +SUBDIRS = debian.upstream src + +DOCS = \ + AUTHORS \ + COPYING \ + NEWS \ + README + +# Extra clean files so that maintainer-clean removes *everything* +MAINTAINERCLEANFILES = \ + aclocal.m4 compile config.guess config.sub \ + configure depcomp install-sh ltmain.sh \ + Makefile.in missing + +DEB_BUILDDIR = debian.build + +deb: + @[ -d debian ] || ln -s debian.upstream debian + dpkg-buildpackage -rfakeroot -uc -us + +deb.upstream: dist + -mkdir -p $(DEB_BUILDDIR) + cd $(DEB_BUILDDIR) && \ + rm -rf $(PACKAGE)-$(VERSION) && \ + tar zxvf ../$(PACKAGE)-$(VERSION).tar.gz && \ + cd $(PACKAGE)-$(VERSION) && \ + $(MAKE) deb -f Makefile.am + +bindistdir = $(distdir).$(TARGET_ARCH) + +bindist: bindist-gzip + +bindist_conf_flags = +bindist_conf_flags += --enable-debug +bindist_conf_flags += --enable-tracer + +bindistdir: dist-gzip + -rm -rf bindist-build; mkdir bindist-build + -cd bindist-build && tar zxf ../$(distdir).tar.gz + -cd bindist-build/$(distdir) && \ + ./configure --prefix=/usr $(bindist_conf_flags) && \ + $(MAKE) + + -rm -rf $(bindistdir); mkdir $(bindistdir) + $(MAKE) install-strip \ + DESTDIR=$(PWD)/$(bindistdir) \ + -C bindist-build/$(distdir) + find $(bindistdir)/ -name "*.la" -exec rm -f {} \; + -rm -rf bindist-build + + cp $(DOCS) $(bindistdir)/ + +bindist-gzip: bindistdir + tar zcvf $(bindistdir).tar.gz $(bindistdir) + rm -rf $(bindistdir) @@ -0,0 +1,201 @@ +xvba-video NEWS -- summary of changes. 2011-06-14 +Copyright (C) 2009-2011 Splitted-Desktop Systems + +Version 0.8.0 - 14.Jun.2011 +* Relicense code to GPLv2 +* Require fglrx >= 8.80.5 (Catalyst 10.12) + +Version 0.7.8 - 24.Feb.2011 +* Add compatibility glue with upstream libva >= 1.0.8 + +Version 0.7.7 - 15.Dec.2010 +* Fix Evergreen workaround for newer fglrx versions +* Fix vaQueryConfigProfiles() & vaQueryConfigEntrypoints() duplicates + +Version 0.7.6 - 04.Nov.2010 +* Fix Evergreen workaround for fglrx >= 8.79.4 +* Add vaPutSurface() high-quality scaling flag (VA_FILTER_SCALING_HQ) + +Version 0.7.5 - 05.Oct.2010 +* Add support for GL_TEXTURE_RECTANGLE_ARB textures +* Add workaround for GLX rendering on Evergreen chips +* Add vaPutSurface() low-quality scaling flag (VA_FILTER_SCALING_FAST) + +Version 0.7.4 - 21.Sep.2010 +* Check UVD is really enabled prior to using it +* Add debug info through XVBA_VIDEO_DEBUG=<level> +* Fix regression when decoding multiple slices per frame +* Fix system crash with H.264 videos encoded over HP @ L4.1 + +Version 0.7.3 - 05.Aug.2010 +* Add compatibility glue with original VA-API 0.31.1 +* Fix vaCopySurfaceGLX() to a GLX texture of different size + +Version 0.7.2 - 13.Jul.2010 +* Require fglrx >= 8.73.2 (Catalyst 10.5) +* Fix vaInitialize() return status if an error occurred +* Fix regression when rendering subpictures in VA/GLX mode +* Set VADisplayAttribDirectSurface to 1 in VA/GLX mode too + +Version 0.7.1 - 09.Jul.2010 +* Drop explicit link against libva +* Add compatibility glue with original VA-API 0.31.0 +* Fix regression when rendering to a GL_RGBA texture +* Fix rendering of subpictures with size mod 16 != 0 + +Version 0.7.0 - 08.Jul.2010 +* Add support for VA-API 0.31.1-sds1 +* Requires fglrx driver version >= 8.69.2 +* Fix VA/GLX to preserve caller's GL context +* Fix vaCopySurfaceGLX() to handle GL_RGBA8 textures +* Fix output surface creation code to detect errors + +Version 0.6.11 - 18.Apr.2010 +* Fix VA context destruction +* Fix rendering of empty surfaces +* Fix vaGetImage() in I420 format +* Fix vaCreateConfig() to validate profile & entrypoint + +Version 0.6.10 - 18.Mar.2010 +* Add I420 image format +* Add support for VA-API 0.31.0-sds6 +* Fix destruction of child windows used by vaPutSurface() + +Version 0.6.9 - 26.Feb.2010 +* Build against VA-API 0.31.0-sds5 +* Optimize rendering of VA images +* Fix detection of window size changes +* Fix rendering of multiple surfaces per window +* Add support for VA_CLEAR_DRAWABLE to vaPutSurface() + +Version 0.6.8 - 22.Feb.2010 +* Fix rendering of VA images not a multiple of 16 +* Add support for GL_RGBA textures in vaCreateSurfaceGLX() +* Optimize rendering of multiple subpictures from a single image + +Version 0.6.7 - 18.Feb.2010 +* Use fail-safe values for H.264 videos encoded over HP@L4.1 +* Fix hue rotation to preserve luminance +* Fix internal contrast range to [ 0.0f .. 10.0f ] +* Fix rendering of multiple subpictures per surface +* Fix vaCopySurfaceGLX() for surfaces with dimensions not a multiple of 16 + +Version 0.6.6 - 11.Feb.2010 +* Fix XvBA objects destruction for fglrx >= 8.70.3 +* Fix vaPutImage() to a surface used for decoding +* Fix vaGetImage()/vaPutSurface() with surface dimensions not a multiple of 16 +* Fix rendering of VA subpictures that were previously deassociated + +Version 0.6.5 - 08.Feb.2010 +* Add brightness/contrast/hue/saturation display attributes +* Fix vaPutSurface() window resize. e.g. when switching to full-screen mode +* Allow vaPutSurface() to render to multiple drawables from a single surface + +Version 0.6.4 - 20.Jan.2010 +* Fix vaGetImage() with YV12 format +* Fix vaPutSurface() to only draw the requested source region +* Fix rendering of subpictures with size different from parent surface's + +Version 0.6.3 - 18.Jan.2010 +* Add background-color display attribute +* Fix output surface allocation logic +* Fix subwindow stacking order in vaPutSurface() +* Fix vaGetImage() to wait for decoding to complete + +Version 0.6.2 - 14.Jan.2010 +* Fix VA buffer leaks when decoded surfaces are not used +* Add support for RGBA, NV12 and YV12 image formats for vaPutImage() +* Don't capture keyboard and mouse events in windowed rendering mode with OpenGL + +Version 0.6.1 - 11.Jan.2010 +* Add support for surfaces not bound to any VA context for plain rendering +* Avoid flickering in windowed rendering mode with OpenGL (vaPutSurface()) +* Fix windowed rendering with OpenGL for clips larger than the visible area + +Version 0.6.0 - 30.Dec.2009 +* Add vaPutSurface() implementation with OpenGL, if PCOM is disabled +* Add support for bob deinterlacing (PCOM only) + +Version 0.5.4 - 04.Dec.2009 +* Fix check for RGB subpicture format +* Fix support for VA_SUBPICTURE_GLOBAL_ALPHA flag +* Add YUY2 subpicture format (PCOM only) +* Add RGBA subpicture format (OpenGL only) + +Version 0.5.3 - 28.Nov.2009 +* Handle up to 16 subpictures in OpenGL rendering mode too + +Version 0.5.2 - 27.Nov.2009 +* Add ARGB subpictures to OpenGL renderer +* Fix rendering and destruction of subpictures +* Drop create/destroy context workarounds for fglrx >= 8.69.2 + +Version 0.5.1 - 12.Oct.2009 +* Fix ARGB image format +* Fix H.264 level_idc reconstruction +* Fix vaCreateImage() when output VAImage points to stack +* Add vaPutImage() for full BGRA and NV12 image uploads + +Version 0.5.0 - 06.Oct.2009 +* Fix vaGetImage() with YV12 pixels +* Add support for subpictures (PCOM only) + +Version 0.4.4 - 24.Sep.2009 +* Fix vaQueryConfigProfiles() +* Fix vaQueryConfigEntrypoints() + +Version 0.4.3 - 22.Sep.2009 +* Fix vaQuerySurfaceStatus() +* Fix various bugs in vaGetImage() +* Fix support for multiple VA/GLX surfaces per context + +Version 0.4.2 - 10.Sep.2009 +* Add VADisplayAttributes +* Add support for VA-API 0.31 +* Fix one minor memory leak in vaDestroySurfaces() +* Make vaInitialize() fail if PCOM is disabled and fglrx < 8.66 + +Version 0.4.1 - 01.Sep.2009 +* Fix vaQueryImageFormats() +* Add support for OpenGL extensions (v3) to VA-API +* Disable support for PCOM rendering in public releases + +Version 0.4.0 - 21.Aug.2009 +* Add support for OpenGL extensions to VA-API +* Fix VAImageID pool memory leak in vaTerminate() + +Version 0.3.0 - 12.Aug.2009 +* Add support for XvBA 0.74 +* Add support for VAImage related functions: + - vaCreateImage(), vaDestroyImage() + - vaGetImage() for full surface readback only (i.e. no partial reads) + +Version 0.2.3 - 04.Aug.2009 +* Add support for VA-API 0.30 +* Add support for vaSyncSurface() and vaQuerySurfaceStatus() +* Add debugging tools (XVBA_VIDEO_TRACE environment variable) +* Fix vaPutSurface() in asynchronous mode under some rare conditions + +Version 0.2.2 - 17.Jun.2009 +* Use asynchronous present model by default +* Fix video playback when window size changes +* Fix memory leak in presence of multiple slices per frame + +Version 0.2.1 - 16.Jun.2009 +* Add make bindist rule +* Fix fullscreen mode with some video clips +* Fix windowed mode to use the requested display bounds +* Fix unimplemented hooks to return an error (VA_STATUS_ERROR_OPERATION_FAILED) + +Version 0.2.0 - 11.Jun.2009 +* Fix memory leaks +* Fix VC-1 decoding +* Fix minor H.264 bugs + +Version 0.1.1 - 10.Jun.2009 +* Allow window size changes +* Fix VC-1 LEVEL reconstruction +* Fix H.264 level_idc reconstruction + +Version 0.1.0 - 09.Jun.2009 +* Initial public (binary) release @@ -0,0 +1,32 @@ + + xvba-video + An XvBA-based backend for VA-API + + Copyright (C) 2009-2011 Splitted-Desktop Systems + + +License +------- + +xvba-video is available under the terms of the GNU General Public +License. + + +Overview +-------- + +xvba-video consists in an XvBA-based backend for VA-API. + + +Requirements +------------ + +libVA API version (not package) GLX support +0.29-branch >= 0.29.0-sds8 No +0.30-branch >= 0.30.0-sds1 No +0.30-branch >= 0.30.4-sds5 Yes +upstream 0.31-branch No +0.31-branch >= 0.31.0-sds1 Yes + +mplayer-vaapi >= 20090320 patchset is also needed if you intend to use +MPlayer. diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..9c2f4f6 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,3 @@ +#! /bin/sh +autoreconf -v --install +./configure "$@" diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..72c6a4f --- /dev/null +++ b/configure.ac @@ -0,0 +1,326 @@ +# xvba_video package version number +m4_define([xvba_video_major_version], [0]) +m4_define([xvba_video_minor_version], [8]) +m4_define([xvba_video_micro_version], [0]) +m4_define([xvba_video_pre_version], [0]) +m4_define([xvba_video_version], + [xvba_video_major_version.xvba_video_minor_version.xvba_video_micro_version]) +m4_if(xvba_video_pre_version, [0], [], [ +m4_append([xvba_video_version], xvba_video_pre_version, [.pre]) +]) + +# libva package version number +m4_define([libva_sds_version_0_29], [8]) +m4_define([libva_sds_package_version_0_29], [0.29-2+sds11]) +m4_define([libva_sds_version_0_30], [1]) +m4_define([libva_sds_package_version_0_30], [0.30-1+sds1]) +m4_define([libva_glx_sds_version_0_30], [5]) +m4_define([libva_glx_sds_package_version_0_30], [0.30.4-1+sds6]) +m4_define([libva_glx_sds_version_0_31], [1]) +m4_define([libva_glx_sds_package_version_0_31], [0.31.0-1+sds1]) +m4_define([libva_sds_version], [libva_glx_sds_version_0_31]) +m4_define([libva_sds_package_version], [libva_glx_sds_package_version_0_31]) + +AC_PREREQ([2.57]) +AC_INIT([xvba_video], [xvba_video_version], [gbeauchesne@splitted-desktop.com], [xvba-video]) +AC_CONFIG_SRCDIR([Makefile.am]) +AC_CANONICAL_TARGET +AM_INIT_AUTOMAKE +AM_CONFIG_HEADER([src/config.h]) + +LIBVA_SDS_VERSION_0_29=libva_sds_version_0_29 +LIBVA_SDS_VERSION_0_30=libva_sds_version_0_30 +LIBVA_GLX_SDS_VERSION_0_30=libva_glx_sds_version_0_30 +LIBVA_GLX_SDS_VERSION_0_31=libva_glx_sds_version_0_31 +LIBVA_SDS_VERSION=libva_sds_version +LIBVA_SDS_PACKAGE_VERSION=libva_sds_package_version +AC_SUBST(LIBVA_SDS_PACKAGE_VERSION) + +XVBA_VIDEO_MAJOR_VERSION=xvba_video_major_version +XVBA_VIDEO_MINOR_VERSION=xvba_video_minor_version +XVBA_VIDEO_MICRO_VERSION=xvba_video_micro_version +AC_DEFINE([XVBA_VIDEO_MAJOR_VERSION], [xvba_video_major_version], [Major version of the driver]) +AC_DEFINE([XVBA_VIDEO_MINOR_VERSION], [xvba_video_minor_version], [Minor version of the driver]) +AC_DEFINE([XVBA_VIDEO_MICRO_VERSION], [xvba_video_micro_version], [Micro version of the driver]) +AC_DEFINE([XVBA_VIDEO_PRE_VERSION], [xvba_video_pre_version], [Preversion of the driver]) + +XVBA_VIDEO_LT_LDFLAGS="-avoid-version" +AC_SUBST(XVBA_VIDEO_LT_LDFLAGS) + +AC_DISABLE_STATIC +AC_PROG_LIBTOOL +AC_PROG_CC + +AC_CHECK_LIB(m, sqrt) + +AC_HEADER_STDC +AC_SYS_LARGEFILE + +dnl We want pthreads. Try libpthread first, then libc_r (FreeBSD), then PTL. +HAVE_PTHREADS=yes +AC_CHECK_LIB(pthread, pthread_create, , [ + AC_CHECK_LIB(c_r, pthread_create, , [ + AC_CHECK_LIB(PTL, pthread_create, , [ + HAVE_PTHREADS=no + ]) + ]) +]) +if test "x$HAVE_PTHREADS" = "xyes"; then + AC_DEFINE(HAVE_PTHREADS, 1, [Defined if pthreads are available]) +fi + +dnl Checks for libraries. +AC_CHECK_LIB(rt, timer_create) + +dnl Checks for library functions. +AC_CHECK_FUNCS(clock_gettime) + +dnl Check for Cg compiler +AC_CHECK_PROG(CGC, [cgc], [cgc], [no]) +have_cgc="no" +if test "x$CGC" != "xno"; then + have_cgc="yes" +fi +AM_CONDITIONAL(HAVE_CGC, [test "x$have_cgc" = "xyes"]) + +dnl Check for python (pso2h.py needs it) +AC_CHECK_PROG(PYTHON, [python], [python], [no]) +have_python="no" +if test "x$PYTHON" != "xno"; then + have_python="yes" +fi +AM_CONDITIONAL(HAVE_PYTHON, [test "x$have_python" = "xyes"]) + +TARGET_ARCH="$target_cpu" +AC_SUBST(TARGET_ARCH) + +AC_DEFUN([AC_DEFINE_01], [ + if test "$2" = "yes" -o "$2" = "guessing yes"; then + AC_DEFINE($1, 1, $3) + else + AC_DEFINE($1, 0, $3) + fi +]) + +AC_ARG_ENABLE(glx, + AC_HELP_STRING([--enable-glx], + [enable OpenGL/X11 @<:@default=yes@:>@]), + [], [enable_glx="yes"]) + +AC_ARG_ENABLE(libxvba-dlopen, + AC_HELP_STRING([--enable-libxvba-dlopen], + [enable libXvBAW.so.1 to be dlopen()'ed]), + [], [enable_libxvba_dlopen="yes"]) + +AC_DEFINE_01(USE_DLOPEN, "$enable_libxvba_dlopen", + [Define to enable libXvBAW.so.1 to be dlopen()'ed]) + +AC_ARG_ENABLE(valgrind, + AC_HELP_STRING([--enable-valgrind], + [enable runtime detection of valgrind]), + [], [PKG_CHECK_EXISTS(valgrind, [enable_valgrind="yes"], [enable_valgrind="no"])]) + +AC_DEFINE_01(USE_VALGRIND, "$enable_valgrind", + [Define to enable run-time detection of Valgrind]) + +AC_ARG_ENABLE(debug, + AC_HELP_STRING([--enable-debug], + [enable debugging information]), + [], [enable_debug="yes"]) + +AC_DEFINE_01(USE_DEBUG, "$enable_debug", + [Enable debugging information]) + +AC_ARG_ENABLE(tracer, + AC_HELP_STRING([--enable-tracer], + [enable XvBA tracer]), + [], [enable_tracer="yes"]) + +AC_DEFINE_01(USE_TRACER, "$enable_tracer", + [Enable XvBA tracer]) + +dnl Check for __attribute__((visibility())) +AC_CACHE_CHECK([whether __attribute__((visibility())) is supported], + xvba_cv_visibility_attribute, + [cat > conftest.c <<EOF +int foo __attribute__ ((visibility ("hidden"))) = 1; +int bar __attribute__ ((visibility ("protected"))) = 1; +EOF + xvba_cv_visibility_attribute=no + if ${CC-cc} -Werror -S conftest.c -o conftest.s >/dev/null 2>&1; then + if grep '\.hidden.*foo' conftest.s >/dev/null; then + if grep '\.protected.*bar' conftest.s >/dev/null; then + xvba_cv_visibility_attribute=yes + fi + fi + fi + rm -f conftest.[cs] +]) +if test $xvba_cv_visibility_attribute = yes; then + AC_DEFINE([HAVE_VISIBILITY_ATTRIBUTE], 1, [Define to use the __attribute__((visibility())) extension]) +fi + +dnl Check for OpenGL +USE_GLX=1 +if test "$enable_glx" != "yes"; then + USE_GLX=0 +fi +GL_DEPS_CFLAGS="" +GL_DEPS_LIBS="" +AC_CHECK_HEADERS([GL/gl.h GL/glext.h GL/glx.h], [], [USE_GLX=0], [ +#ifdef HAVE_GL_GL_H +# include <GL/gl.h> +#endif +]) +AC_CHECK_LIB(GL, glXCreateContext, [GL_DEPS_LIBS="-lGL -ldl"], [USE_GLX=0]) +AC_SUBST(GL_DEPS_CFLAGS) +AC_SUBST(GL_DEPS_LIBS) + +dnl Check for VA-API +PKG_CHECK_MODULES(LIBVA_DEPS, [libva]) +PKG_CHECK_MODULES(LIBVA_X11_DEPS, [libva-x11]) +PKG_CHECK_MODULES(LIBVA_GLX_DEPS, [libva-glx], [], [USE_GLX=0]) + +dnl Check for SDS extensions to VA-API +AC_CACHE_CHECK([for VA-API], + ac_cv_libva_sds_extensions, [ + saved_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $LIBVA_DEPS_CFLAGS" + AC_TRY_COMPILE([ + #include <va/va_version.h> + #if VA_MAJOR_VERSION == 0 && VA_MINOR_VERSION == 29 + # if !defined(VA_SDS_VERSION) || (VA_SDS_VERSION < $LIBVA_SDS_VERSION_0_29) + # error "VA-API version >= 0.29.0-sds$LIBVA_SDS_VERSION_0_29 is required" + # endif + #elif VA_MAJOR_VERSION == 0 && VA_MINOR_VERSION == 30 + # if !defined(VA_SDS_VERSION) || (VA_SDS_VERSION < $LIBVA_SDS_VERSION_0_30) + # error "VA-API version >= 0.30.0-sds$LIBVA_SDS_VERSION_0_30 is required" + # endif + #elif !VA_CHECK_VERSION(0,31,0) + # error "VA-API version >= 0.31 is required" + #endif + ], [], + [ac_cv_libva_sds_extensions="yes"], + [ac_cv_libva_sds_extensions="no"]) + CFLAGS="$saved_CFLAGS" +]) +VA_VERSION=`$PKG_CONFIG --modversion libva` +VA_MAJOR_VERSION=`echo "$VA_VERSION" | cut -d'.' -f1` +VA_MINOR_VERSION=`echo "$VA_VERSION" | cut -d'.' -f2` +VA_MICRO_VERSION=`echo "$VA_VERSION" | cut -d'.' -f3` +VA_SDS_VERSION=`$PKG_CONFIG libva --variable sdsversion` + +VA_VERSION_STR="$VA_VERSION" +if test -n "$VA_SDS_VERSION"; then + VA_VERSION_STR="$VA_VERSION_STR-sds$VA_SDS_VERSION" +fi + +va_full_version_int=`expr ${VA_MAJOR_VERSION:-0} "*" 1000000 + \ + ${VA_MINOR_VERSION:-0} "*" 10000 + \ + ${VA_MICRO_VERSION:-0} "*" 100 + \ + ${VA_SDS_VERSION:-0}` +VA_DRIVER_INIT_FUNC="__vaDriverInit_${VA_MAJOR_VERSION}_${VA_MINOR_VERSION}" +if test $va_full_version_int -ge 00310005; then + VA_DRIVER_INIT_FUNC="${VA_DRIVER_INIT_FUNC}_${VA_MICRO_VERSION}_sds${VA_SDS_VERSION}" +else + VA_DRIVER_INIT_FUNC="${VA_DRIVER_INIT_FUNC}_sds" +fi +if test "$ac_cv_libva_sds_extensions" = "yes"; then + AC_DEFINE_UNQUOTED([VA_DRIVER_INIT_FUNC], [$VA_DRIVER_INIT_FUNC], [Define driver entry-point]) +else + AC_MSG_ERROR([Your VA-API SDK does not include SDS extensions]) +fi + +dnl Check for OpenGL rendering extensions to VA-API +AC_CACHE_CHECK([for VA-API (GLX extensions)], + ac_cv_libva_glx_extensions, [ + saved_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $LIBVA_DEPS_CFLAGS" + AC_TRY_COMPILE([ + #include <va/va_version.h> + #if VA_MAJOR_VERSION == 0 && VA_MINOR_VERSION == 30 + # if !defined(VA_SDS_VERSION) || (VA_SDS_VERSION < $LIBVA_GLX_SDS_VERSION_0_30) + # error "VA-API version >= 0.30.0-sds$LIBVA_GLX_SDS_VERSION_0_30 is required" + # endif + #elif VA_MAJOR_VERSION == 0 && VA_MINOR_VERSION == 31 + # if !defined(VA_SDS_VERSION) || (VA_SDS_VERSION < $LIBVA_GLX_SDS_VERSION_0_31) + # error "VA-API version >= 0.31.0-sds$LIBVA_GLX_SDS_VERSION_0_31 is required" + # endif + #elif VA_MAJOR_VERSION == 0 && VA_MINOR_VERSION >= 32 + #else + # error "This version of VA-API is not supported for OpenGL rendering" + #endif + ], [], + [ac_cv_libva_glx_extensions="yes"], + [ac_cv_libva_glx_extensions="no" USE_GLX=0]) + CFLAGS="$saved_CFLAGS" +]) + +dnl Check for VA-API drivers path +AC_MSG_CHECKING([for VA drivers path]) +LIBVA_DRIVERS_PATH=`$PKG_CONFIG libva --variable driverdir` +if test -z "$LIBVA_DRIVERS_PATH"; then + LIBVA_DRIVERS_PATH="/usr/lib/xorg/modules/drivers" +fi +AC_MSG_RESULT([$LIBVA_DRIVERS_PATH]) +AC_SUBST(LIBVA_DRIVERS_PATH) + +dnl Check for XvBA +AC_CACHE_CHECK([for XvBA], + ac_cv_have_xvba, [ + saved_LIBS="$LIBS" + LIBS="$LIBS -lXvBAW" + LIBS="$LIBS -lX11 -lXext -lGL -ldl" # XXX: help broken XvBA libraries... + AC_TRY_LINK( + [ +#include <X11/Xlib.h> +#include <amdxvba.h> + ], + [XVBAQueryExtension(0,0)], + [ac_cv_have_xvba="yes" HAVE_XVBA=1], + [ac_cv_have_xvba="no" HAVE_XVBA=0]) + LIBS="$saved_LIBS" +]) +if test "$ac_cv_have_xvba" = "no"; then + AC_MSG_ERROR([you need XvBA to build this package]) +fi + +AC_DEFINE_UNQUOTED(USE_GLX, $USE_GLX, [Defined to 1 if GLX is enabled]) +AM_CONDITIONAL(USE_GLX, test $USE_GLX -eq 1) + +PKG_CHECK_MODULES(DRM_DEPS, [libdrm]) + +XVBA_VIDEO_CFLAGS="$LIBVA_DEPS_CFLAGS $DRM_DEPS_CFLAGS" +XVBA_VIDEO_LIBS="" +if test "$enable_libxvba_dlopen" = "yes"; then + XVBA_VIDEO_LIBS="$XVBA_VIDEO_LIBS -ldl" +else + XVBA_VIDEO_LIBS="$XVBA_VIDEO_LIBS -lXvBAW -lXext -lGL" +fi +if test $USE_GLX -eq 1; then + XVBA_VIDEO_CFLAGS="$XVBA_VIDEO_CFLAGS $GL_DEPS_CFLAGS" + XVBA_VIDEO_LIBS="$XVBA_VIDEO_LIBS $GL_DEPS_LIBS" +fi +if test "$enable_valgrind" = "yes"; then + PKG_CHECK_MODULES(VALGRIND_DEPS, [valgrind]) + XVBA_VIDEO_CFLAGS="$XVBA_VIDEO_CFLAGS $VALGRIND_DEPS_CFLAGS" +fi +AC_SUBST(XVBA_VIDEO_CFLAGS) +AC_SUBST(XVBA_VIDEO_LIBS) + +AC_OUTPUT([ + Makefile + debian.upstream/Makefile + src/Makefile + src/shaders/Makefile +]) + +dnl Print summary +echo +echo xvba-video configuration summary: +echo +echo VA-API version ................... : $VA_VERSION_STR +echo GLX support ...................... : $(test $USE_GLX -eq 1 && echo yes || echo no) +echo Cg compiler ...................... : $CGC +echo Enable debugging information ..... : $enable_debug +echo Enable XvBA tracer ............... : $enable_tracer diff --git a/debian.upstream/Makefile.am b/debian.upstream/Makefile.am new file mode 100644 index 0000000..0816444 --- /dev/null +++ b/debian.upstream/Makefile.am @@ -0,0 +1,27 @@ +DEBIANFILES = \ + changelog.in \ + compat \ + control.in \ + copyright \ + rules \ + xvba-video.install \ + $(NULL) + +DEBIANGENFILES = \ + changelog \ + control \ + $(NULL) + +EXTRA_DIST = $(DEBIANFILES) + +dist_noinst_DATA = $(DEBIANGENFILES) + +# Extra clean files so that maintainer-clean removes *everything* +MAINTAINERCLEANFILES = Makefile.in $(DEBIANGENFILES) + +$(DEBIANGENFILES): %: %.in Makefile + -sed \ + -e 's|\@PACKAGE_VERSION\@|$(PACKAGE_VERSION)|' \ + -e 's|\@LIBVA_SDS_PACKAGE_VERSION\@|$(LIBVA_SDS_PACKAGE_VERSION)|' \ + -e 's|\@DATE\@|'"`LC_ALL=C date +'%a, %d %b %Y %X %z'`"'|' \ + $< > $@ diff --git a/debian.upstream/changelog.in b/debian.upstream/changelog.in new file mode 100644 index 0000000..c2b20f3 --- /dev/null +++ b/debian.upstream/changelog.in @@ -0,0 +1,5 @@ +xvba-video (@PACKAGE_VERSION@-1) unstable; urgency=low + + * Autogenerated package, see NEWS file for ChangeLog. + + -- Gwenole Beauchesne <gbeauchesne@splitted-desktop.com> @DATE@ diff --git a/debian.upstream/compat b/debian.upstream/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian.upstream/compat @@ -0,0 +1 @@ +5 diff --git a/debian.upstream/control.in b/debian.upstream/control.in new file mode 100644 index 0000000..2e6db32 --- /dev/null +++ b/debian.upstream/control.in @@ -0,0 +1,27 @@ +Source: xvba-video +Section: libs +Priority: optional +Maintainer: Gwenole Beauchesne <gbeauchesne@splitted-desktop.com> +Build-Depends: debhelper (>= 5), + cdbs, + libdrm-dev, + libva-dev (>= @LIBVA_SDS_PACKAGE_VERSION@), + python +Standards-Version: 3.7.2 + +Package: xvba-video +Section: libs +Architecture: any +Depends: libva1 (>= @LIBVA_SDS_PACKAGE_VERSION@), + ${shlibs:Depends}, ${misc:Depends} +Description: XvBA-based backend for VA-API + Video decode driver for ATI/AMD chipsets (XvBA implementation). + +Package: xvba-video-dbg +Section: libdevel +Architecture: any +Depends: xvba-video (= ${Source-Version}) +Description: XvBA-based backend for VA-API + Video decode driver for ATI/AMD chipsets (XvBA implementation). + . + This package contains the debug files. diff --git a/debian.upstream/copyright b/debian.upstream/copyright new file mode 100644 index 0000000..63b3d55 --- /dev/null +++ b/debian.upstream/copyright @@ -0,0 +1,24 @@ +This package is maintained by: +Gwenole Beauchesne <gbeauchesne@splitted-desktop.com> + + +License: + + Copyright (C) 2009-2011 Splitted-Desktop Systems + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +On Debian GNU/Linux systems, the complete text of the GNU General +Public License is in `/usr/share/common-licenses/GPL'. diff --git a/debian.upstream/rules b/debian.upstream/rules new file mode 100755 index 0000000..a6286f6 --- /dev/null +++ b/debian.upstream/rules @@ -0,0 +1,18 @@ +#!/usr/bin/make -f + +include /usr/share/cdbs/1/rules/debhelper.mk +include /usr/share/cdbs/1/class/autotools.mk +include /usr/share/cdbs/1/rules/simple-patchsys.mk +include /usr/share/cdbs/1/rules/utils.mk + +# Allow SMP build +ifeq ($(DEBIAN_BUILD_NCPUS),) + DEBIAN_BUILD_NCPUS = $(shell /usr/bin/getconf _NPROCESSORS_ONLN) +endif +ifneq ($(DEBIAN_BUILD_NCPUS),) + EXTRA_MAKE_FLAGS += -j$(DEBIAN_BUILD_NCPUS) +endif +MAKE += $(EXTRA_MAKE_FLAGS) + +DEB_CONFIGURE_USER_FLAGS += --enable-debug +DEB_CONFIGURE_USER_FLAGS += --enable-tracer diff --git a/debian.upstream/xvba-video.install b/debian.upstream/xvba-video.install new file mode 100644 index 0000000..8fbbd2b --- /dev/null +++ b/debian.upstream/xvba-video.install @@ -0,0 +1 @@ +debian/tmp/usr/lib/va/drivers/*.so diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..02b9430 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,82 @@ +DRIVERS = fglrx + +SUBDIRS = shaders + +INCLUDES = \ + $(XVBA_VIDEO_CFLAGS) + +LDADD = \ + $(XVBA_VIDEO_LT_LDFLAGS) -module \ + -no-undefined -module -Wl,--no-undefined + +if USE_GLX +source_glx_h = xvba_video_glx.h utils_glx.h +source_glx_c = xvba_video_glx.c utils_glx.c +endif + +source_x11_h = xvba_video_x11.h utils_x11.h +source_x11_c = xvba_video_x11.c utils_x11.c + +source_h = \ + color_matrix.h \ + debug.h \ + fglrxinfo.h \ + object_heap.h \ + sysdeps.h \ + utils.h \ + uarray.h \ + uasyncqueue.h \ + ulist.h \ + uqueue.h \ + vaapi_compat.h \ + xvba_buffer.h \ + xvba_decode.h \ + xvba_driver.h \ + xvba_driver_template.h \ + xvba_dump.h \ + xvba_gate.h \ + xvba_image.h \ + xvba_subpic.h \ + xvba_video.h \ + $(source_glx_h) \ + $(source_x11_h) \ + $(NULL) + +source_c = \ + color_matrix.c \ + debug.c \ + fglrxinfo.c \ + object_heap.c \ + utils.c \ + uarray.c \ + uasyncqueue.c \ + ulist.c \ + uqueue.c \ + xvba_buffer.c \ + xvba_decode.c \ + xvba_driver.c \ + xvba_dump.c \ + xvba_gate.c \ + xvba_image.c \ + xvba_subpic.c \ + xvba_video.c \ + $(source_glx_c) \ + $(source_x11_c) \ + $(NULL) + +xvba_drv_video_la_LTLIBRARIES = xvba_drv_video.la +xvba_drv_video_ladir = @LIBVA_DRIVERS_PATH@ +xvba_drv_video_la_SOURCES = $(source_c) +xvba_drv_video_la_LIBADD = $(XVBA_VIDEO_LIBS) -lX11 -lXext +xvba_drv_video_la_LDFLAGS = $(LDADD) + +noinst_HEADERS = $(source_h) + +install-data-hook: + cd $(DESTDIR)$(LIBVA_DRIVERS_PATH) ; \ + for drv in $(DRIVERS); do \ + ln -s xvba_drv_video.so $${drv}_drv_video.so; \ + done + +# Extra clean files so that maintainer-clean removes *everything* +MAINTAINERCLEANFILES = Makefile.in config.h.in diff --git a/src/color_matrix.c b/src/color_matrix.c new file mode 100644 index 0000000..62020d7 --- /dev/null +++ b/src/color_matrix.c @@ -0,0 +1,469 @@ +/* + * color_matrix.c - Color matrix utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "color_matrix.h" +#include <math.h> + +// Default weight values +#define Rw (0.3086) +#define Gw (0.6094) +#define Bw (0.0820) + +// 3D point +typedef struct { + float x; + float y; + float z; +} Point3D; + +// Print color matrix M with NAME +#ifdef TEST +static void cm_print(ColorMatrix m, const char *name) +{ + int x, y; + + printf("%s = {\n", name); + for (y = 0; y < 4; y++) { + putchar(' '); + for (x = 0; x < 4; x++) + printf(" %f", m[y][x]); + putchar('\n'); + } + printf("}\n"); +} +#endif + +// Assign color matrix A to M +void cm_copy(ColorMatrix m, ColorMatrix a) +{ +#if 1 + int x, y; + + for (y = 0; y < 4; y++) + for (x = 0; x < 4; x++) + m[y][x] = a[y][x]; +#else + m[0][0] = a[0][0]; + m[0][1] = a[0][1]; + m[0][2] = a[0][2]; + m[0][3] = a[0][3]; + + m[1][0] = a[1][0]; + m[1][1] = a[1][1]; + m[1][2] = a[1][2]; + m[1][3] = a[1][3]; + + m[2][0] = a[2][0]; + m[2][1] = a[2][1]; + m[2][2] = a[2][2]; + m[2][3] = a[2][3]; + + m[3][0] = a[3][0]; + m[3][1] = a[3][1]; + m[3][2] = a[3][2]; + m[3][3] = a[3][3]; +#endif +} + +// Multiply color matrices A and B and set the result to M +void cm_multiply(ColorMatrix m, ColorMatrix a, ColorMatrix b) +{ + ColorMatrix t; + int x, y; + + for (y = 0; y < 4; y++) + for (x = 0; x < 4; x++) + t[y][x] = (b[y][0] * a[0][x] + + b[y][1] * a[1][x] + + b[y][2] * a[2][x] + + b[y][3] * a[3][x]); + + cm_copy(m, t); +} + +// Transform a 3D point using the color matrix +static void cm_transform(ColorMatrix m, const Point3D *p, Point3D *tp) +{ + tp->x = p->x * m[0][0] + p->y * m[1][0] + p->z * m[2][0] + m[3][0]; + tp->y = p->x * m[0][1] + p->y * m[1][1] + p->z * m[2][1] + m[3][1]; + tp->z = p->x * m[0][2] + p->y * m[1][2] + p->z * m[2][2] + m[3][2]; +} + +// Create a new color matrix with a SCALE factor +static inline void cm_set_scale(ColorMatrix m, float scale) +{ + m[0][0] = scale; + m[0][1] = 0.0f; + m[0][2] = 0.0f; + m[0][3] = 0.0f; + + m[1][0] = 0.0f; + m[1][1] = scale; + m[1][2] = 0.0f; + m[1][3] = 0.0f; + + m[2][0] = 0.0f; + m[2][1] = 0.0f; + m[2][2] = scale; + m[2][3] = 0.0f; + + m[3][0] = 0.0f; + m[3][1] = 0.0f; + m[3][2] = 0.0f; + m[3][3] = 1.0f; +} + +// Create a new identity matrix +void cm_set_identity(ColorMatrix m) +{ + cm_set_scale(m, 1.0f); +} + +// Create a new color matrix with offset R/G/B components +static void cm_set_offset(ColorMatrix m, float r, float g, float b) +{ + cm_set_identity(m); + + m[3][0] = r; + m[3][1] = g; + m[3][2] = b; +} + +// Set color matrix components for a rotation around the x (red) axis +static void cm_set_rotation_x(ColorMatrix m, float rs, float rc) +{ + m[0][0] = 1.0f; + m[0][1] = 0.0f; + m[0][2] = 0.0f; + m[0][3] = 0.0f; + + m[1][0] = 0.0f; + m[1][1] = rc; + m[1][2] = rs; + m[1][3] = 0.0f; + + m[2][0] = 0.0f; + m[2][1] = -rs; + m[2][2] = rc; + m[2][3] = 0.0f; + + m[3][0] = 0.0f; + m[3][1] = 0.0f; + m[3][2] = 0.0f; + m[3][3] = 1.0f; +} + +// Set color matrix components for a rotation around the y (green) axis +static void cm_set_rotation_y(ColorMatrix m, float rs, float rc) +{ + m[0][0] = rc; + m[0][1] = 0.0f; + m[0][2] = -rs; + m[0][3] = 0.0f; + + m[1][0] = 0.0f; + m[1][1] = 1.0f; + m[1][2] = 0.0f; + m[1][3] = 0.0f; + + m[2][0] = rs; + m[2][1] = 0.0f; + m[2][2] = rc; + m[2][3] = 0.0f; + + m[3][0] = 0.0f; + m[3][1] = 0.0f; + m[3][2] = 0.0f; + m[3][3] = 1.0f; +} + +// Set color matrix components for a rotation around the z (blue) axis +static void cm_set_rotation_z(ColorMatrix m, float rs, float rc) +{ + m[0][0] = rc; + m[0][1] = rs; + m[0][2] = 0.0f; + m[0][3] = 0.0f; + + m[1][0] = -rs; + m[1][1] = rc; + m[1][2] = 0.0f; + m[1][3] = 0.0f; + + m[2][0] = 0.0f; + m[2][1] = 0.0f; + m[2][2] = 1.0f; + m[2][3] = 0.0f; + + m[3][0] = 0.0f; + m[3][1] = 0.0f; + m[3][2] = 0.0f; + m[3][3] = 1.0f; +} + +// Shear z using x and y +static void cm_shear_z(ColorMatrix m, float dx, float dy) +{ + ColorMatrix t; + + t[0][0] = 1.0f; + t[0][1] = 0.0f; + t[0][2] = dx; + t[0][3] = 0.0f; + + t[1][0] = 0.0f; + t[1][1] = 1.0f; + t[1][2] = dy; + t[1][3] = 0.0f; + + t[2][0] = 0.0f; + t[2][1] = 0.0f; + t[2][2] = 1.0f; + t[2][3] = 0.0f; + + t[3][0] = 0.0f; + t[3][1] = 0.0f; + t[3][2] = 0.0f; + t[3][3] = 1.0f; + + cm_multiply(m, t, m); +} + +// Return BRIGHTNESS value range +void cm_get_brightness_range(float *vstart, float *vend, float *vdefault) +{ + /* Brightness value ranges from 0.0 to 10.0. A value of 1.0 + represents no modification */ + if (vstart) + *vstart = 0.0f; + if (vend) + *vend = 10.0f; + if (vdefault) + *vdefault = 1.0f; +} + +// Create a new color matrix with a specific BRIGHTNESS value +void cm_set_brightness(ColorMatrix m, float brightness) +{ + cm_set_scale(m, brightness); +} + +// Return CONTRAST value range +void cm_get_contrast_range(float *vstart, float *vend, float *vdefault) +{ + /* Contrast value ranges from 0 to 10.0. A value of 1.0 + represents no modification */ + if (vstart) + *vstart = 0.0f; + if (vend) + *vend = 10.0f; + if (vdefault) + *vdefault = 1.0f; +} + +// Create a new color matrix with a specific CONTRAST value +void cm_set_contrast(ColorMatrix m, float contrast) +{ + ColorMatrix n; /* offset by -0.5f */ + ColorMatrix p; /* offset by +0.5f */ + ColorMatrix s; /* scaled by contrast */ + ColorMatrix t; + + cm_set_scale(s, contrast); + cm_set_offset(n, -0.5f, -0.5f, -0.5f); + cm_set_offset(p, +0.5f, +0.5f, +0.5f); + cm_multiply(t, s, n); + cm_multiply(m, p, t); +} + +// Return SATURATION value range +void cm_get_saturation_range(float *vstart, float *vend, float *vdefault) +{ + /* Saturation value ranges from 0.0 to 10.0. A value of 1.0 + represents no modification */ + if (vstart) + *vstart = 0.0f; + if (vend) + *vend = 10.0f; + if (vdefault) + *vdefault = 1.0f; +} + +// Create a new color matrix used for SATURATION +void cm_set_saturation(ColorMatrix m, float saturation) +{ + /* + * +- -+ + * | a d g 0 | + * | b e h 0 | + * | c f i 0 | + * | 0 0 0 1 | + * +- -+ + * + * where + * + * a = (1 - s) * Rw + s + * b = (1 - s) * Rw + * c = (1 - s) * Rw + * d = (1 - s) * Gw + * e = (1 - s) * Gw + s + * f = (1 - s) * Gw + * g = (1 - s) * Bw + * h = (1 - s) * Bw + * i = (1 - s) * Bw + s + */ + m[0][0] = (1.0f - saturation) * Rw + saturation; + m[0][1] = (1.0f - saturation) * Rw; + m[0][2] = (1.0f - saturation) * Rw; + m[0][3] = 0.0f; + + m[1][0] = (1.0f - saturation) * Gw; + m[1][1] = (1.0f - saturation) * Gw + saturation; + m[1][2] = (1.0f - saturation) * Gw; + m[1][3] = 0.0f; + + m[2][0] = (1.0f - saturation) * Bw; + m[2][1] = (1.0f - saturation) * Bw; + m[2][2] = (1.0f - saturation) * Bw + saturation; + m[2][3] = 0.0f; + + m[3][0] = 0.0f; + m[3][1] = 0.0f; + m[3][2] = 0.0f; + m[3][3] = 1.0f; +} + +// Return the HUE value range +void cm_get_hue_range(float *vstart, float *vend, float *vdefault) +{ + /* Hue value ranges from -PI to +PI. A value of 0.0 represents no + modification */ + if (vstart) + *vstart = -M_PI; + if (vend) + *vend = M_PI; + if (vdefault) + *vdefault = 0.0f; +} + +// Create a new color matrix used for HUE rotation +void cm_set_hue(ColorMatrix m, float hue) +{ + cm_set_rotation_z(m, sin(hue), cos(hue)); +} + +// Rotate hue while preserving the luminance +// From <http://www.graficaobscura.com/matrix/index.html> (Paul Haeberli, 1993) +static void cm_rotate_hue(ColorMatrix m, ColorMatrix h) +{ + ColorMatrix t, xr, yr; + float mag; + Point3D lum, tlum; + float xrs, xrc; + float yrs, yrc; + float zsx, zsy; + + /* Rotate the grey vector into positive Z */ + mag = sqrtf(2.0f); + xrs = 1.0f / mag; + xrc = 1.0f / mag; + cm_set_rotation_x(xr, xrs, xrc); + + mag = sqrtf(3.0f); + yrs = -1.0f / mag; + yrc = sqrtf(2.0f) / mag; + cm_set_rotation_y(yr, yrs, yrc); + + cm_set_identity(t); + cm_multiply(t, xr, t); + cm_multiply(t, yr, t); + + /* Shear the space to make the luminance plane horizontal */ + lum.x = Rw; + lum.y = Gw; + lum.z = Bw; + cm_transform(t, &lum, &tlum); + zsx = tlum.x / tlum.z; + zsy = tlum.y / tlum.z; + cm_shear_z(t, zsx, zsy); + + /* Rotate the hue */ + cm_multiply(t, h, t); + + /* Unshear the space to put the luminance plane back */ + cm_shear_z(t, -zsx, -zsy); + + /* Rotate the grey vector back into place */ + cm_set_rotation_y(yr, -yrs, yrc); + cm_set_rotation_x(xr, -xrs, xrc); + + cm_multiply(t, yr, t); + cm_multiply(t, xr, t); + cm_multiply(m, t, m); +} + +// Create composite matrix from brightness/contrast/saturation/hue matrices +void cm_composite( + ColorMatrix m, + ColorMatrix b, + ColorMatrix c, + ColorMatrix s, + ColorMatrix h +) +{ + ColorMatrix t; + + cm_multiply(t, c, b); + cm_multiply(m, t, s); + cm_rotate_hue(m, h); +} + +#ifdef TEST +int main(void) +{ + ColorMatrix brightness_matrix; + cm_set_brightness(brightness_matrix, 1.0f); + cm_print(brightness_matrix, "brightness matrix"); + + ColorMatrix contrast_matrix; + cm_set_contrast(contrast_matrix, 1.0f); + cm_print(contrast_matrix, "contrast_matrix"); + + ColorMatrix saturation_matrix; + cm_set_saturation(saturation_matrix, 1.0f); + cm_print(saturation_matrix, "saturation matrix"); + + ColorMatrix hue_matrix; + cm_set_hue(hue_matrix, 0.0f); + cm_print(hue_matrix, "hue matrix"); + + ColorMatrix color_matrix; + cm_composite( + color_matrix, + brightness_matrix, + contrast_matrix, + saturation_matrix, + hue_matrix + ); + cm_print(color_matrix, "composite matrix"); + return 0; +} +#endif diff --git a/src/color_matrix.h b/src/color_matrix.h new file mode 100644 index 0000000..dacbe75 --- /dev/null +++ b/src/color_matrix.h @@ -0,0 +1,79 @@ +/* + * color_matrix.h - Color matrix utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef COLOR_MATRIX_H +#define COLOR_MATRIX_H + +typedef float ColorMatrix[4][4]; + +// Assign color matrix A to M +void cm_copy(ColorMatrix m, ColorMatrix a) + attribute_hidden; + +// Multiply color matrices A and B and set the result to M +void cm_multiply(ColorMatrix m, ColorMatrix a, ColorMatrix b) + attribute_hidden; + +// Create a new identity matrix +void cm_set_identity(ColorMatrix m) + attribute_hidden; + +// Return CONTRAST value range +void cm_get_brightness_range(float *vstart, float *vend, float *vdefault) + attribute_hidden; + +// Create a new color matrix with a specific BRIGHTNESS value +void cm_set_brightness(ColorMatrix m, float brightness) + attribute_hidden; + +// Return CONTRAST value range +void cm_get_contrast_range(float *vstart, float *vend, float *vdefault) + attribute_hidden; + +// Create a new color matrix with a specific CONTRAST value +void cm_set_contrast(ColorMatrix m, float contrast) + attribute_hidden; + +// Return SATURATION value range +void cm_get_saturation_range(float *vstart, float *vend, float *vdefault) + attribute_hidden; + +// Create a new color matrix used for SATURATION +void cm_set_saturation(ColorMatrix m, float saturation) + attribute_hidden; + +// Return the HUE value range +void cm_get_hue_range(float *vstart, float *vend, float *vdefault) + attribute_hidden; + +// Create a new color matrix used for HUE rotation +void cm_set_hue(ColorMatrix m, float hue) + attribute_hidden; + +// Create composite matrix from brightness/contrast/saturation/hue matrices +void cm_composite( + ColorMatrix m, + ColorMatrix b, + ColorMatrix c, + ColorMatrix s, + ColorMatrix h +) attribute_hidden; + +#endif /* COLOR_MATRIX_H */ diff --git a/src/debug.c b/src/debug.c new file mode 100644 index 0000000..b65e334 --- /dev/null +++ b/src/debug.c @@ -0,0 +1,158 @@ +/* + * debug.c - Debugging utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "debug.h" +#include <stdarg.h> +#include "utils.h" + +#if USE_VALGRIND +# include <valgrind.h> +#endif + +static void do_vprintf(const char *msg, va_list args) +{ +#if USE_VALGRIND + if (RUNNING_ON_VALGRIND) { + static char *fmt = NULL; + static int fmtlen = 0; + + int xmsglen = vsnprintf(NULL, 0, msg, args); + if ((fmt = realloc(fmt, fmtlen + xmsglen + 1)) != NULL) { + vsnprintf(fmt + fmtlen, fmtlen + xmsglen + 1, msg, args); + fmt[fmtlen += xmsglen] = '\0'; + if (fmt[fmtlen - 1] == '\n') { + fmt[fmtlen - 1] = '\0'; + VALGRIND_PRINTF(fmt); /* this already adds a terminal '\n' */ + free(fmt); + fmt = NULL; + fmtlen = 0; + } + } + return; + } +#endif + vprintf(msg, args); +} + +static void do_printf(const char *msg, ...) +{ + va_list args; + va_start(args, msg); + do_vprintf(msg, args); + va_end(args); +} + +void xvba_error_message(const char *msg, ...) +{ + va_list args; + + fprintf(stderr, "%s: error: ", PACKAGE_NAME); + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); +} + +void xvba_information_message(const char *msg, ...) +{ + va_list args; + + do_printf("%s: ", PACKAGE_NAME); + va_start(args, msg); + do_vprintf(msg, args); + va_end(args); +} + +static int debug_enabled(void) +{ + static int g_debug_enabled = -1; + if (g_debug_enabled < 0) { + if (getenv_yesno("XVBA_VIDEO_DEBUG", &g_debug_enabled) < 0) + g_debug_enabled = 0; + } + return g_debug_enabled; +} + +void debug_message(const char *msg, ...) +{ + va_list args; + + if (!debug_enabled()) + return; + + do_printf("%s: ", PACKAGE_NAME); + va_start(args, msg); + do_vprintf(msg, args); + va_end(args); +} + +static int g_trace_is_new_line = 1; +static int g_trace_indent = 0; + +int trace_enabled(void) +{ + static int g_trace_enabled = -1; + if (g_trace_enabled < 0) { + if (getenv_yesno("XVBA_VIDEO_TRACE", &g_trace_enabled) < 0) + g_trace_enabled = 0; + } + return g_trace_enabled; +} + +static int trace_indent_width(void) +{ + static int g_indent_width = -1; + if (g_indent_width < 0) { + if (getenv_int("XVBA_VIDEO_TRACE_INDENT_WIDTH", &g_indent_width) < 0) + g_indent_width = 4; + } + return g_indent_width; +} + +void trace_indent(int inc) +{ + g_trace_indent += inc; +} + +void trace_print(const char *format, ...) +{ + va_list args; + + if (g_trace_is_new_line) { + int i, j, n; + printf("%s: ", PACKAGE_NAME); + n = trace_indent_width(); + for (i = 0; i < g_trace_indent; i++) { + for (j = 0; j < n / 4; j++) + printf(" "); + for (j = 0; j < n % 4; j++) + printf(" "); + } + } + + va_start(args, format); + vfprintf(stdout, format, args); + va_end(args); + + g_trace_is_new_line = (strchr(format, '\n') != NULL); + + if (g_trace_is_new_line) + fflush(stdout); +} diff --git a/src/debug.h b/src/debug.h new file mode 100644 index 0000000..550b96c --- /dev/null +++ b/src/debug.h @@ -0,0 +1,52 @@ +/* + * debug.h - Debugging utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef DEBUG_H +#define DEBUG_H + +void xvba_error_message(const char *msg, ...) + attribute_hidden; + +void xvba_information_message(const char *msg, ...) + attribute_hidden; + +void debug_message(const char *msg, ...) + attribute_hidden; + +#if DEBUG && USE_DEBUG +# define D(x) x +# define bug debug_message +#else +# define D(x) +#endif + +// Returns TRUE if debug trace is enabled +int trace_enabled(void) + attribute_hidden; + +// Increase or decrease indentation level +void trace_indent(int inc) + attribute_hidden; + +// Print message on the debug trace output +void trace_print(const char *format, ...) + attribute_hidden; + +#endif /* DEBUG_H */ diff --git a/src/fglrxinfo.c b/src/fglrxinfo.c new file mode 100644 index 0000000..953069b --- /dev/null +++ b/src/fglrxinfo.c @@ -0,0 +1,221 @@ +/* + * fglrxinfo.c - FGLRX driver info + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#define NEED_REPLIES +#include <X11/Xlibint.h> +#include <X11/Xutil.h> +#include <X11/extensions/Xext.h> +#include <X11/extensions/extutil.h> +#include "fglrxinfo.h" + +#define ATIFGL_EXTENSION_NAME "ATIFGLEXTENSION" +#define ATIFGL_EXTENSION_EVENTS 0 + +typedef struct _FGLGetDriverData { + CARD8 reqType; + CARD8 fireglReqType; + CARD16 length B16; + CARD32 screen B32; + CARD16 size B16; + CARD16 pad1; +} xFGLGetDriverDataReq; +#define sz_xFGLGetDriverDataReq sizeof(xFGLGetDriverDataReq) + +typedef struct { + BYTE type; + BYTE pad1; + CARD16 sequenceNumber B16; + CARD32 length B32; + CARD8 majorVersion; + CARD8 minorVersion; + CARD8 patchlevel B16; + CARD8 BIOSVersionMajor; + CARD8 BIOSVersionMinor; + CARD8 HasSecondary; + CARD16 pad3 B16; + CARD16 pad4 B16; + CARD16 deviceID B16; + CARD32 pad5 B32; + CARD32 pad6 B32; + CARD32 pad7 B32; + // ... there are more fields +} xFGLGetDriverDataReply; +#define sz_xFGLGetDriverDataReply sizeof(xFGLGetDriverDataReply) + +#define X_FGLGetDriverData 0 + +static XExtensionInfo _fglext_ext_info_data; +static XExtensionInfo *fglext_ext_info = &_fglext_ext_info_data; +static /* const */ char *fglext_extension_name = ATIFGL_EXTENSION_NAME; + +#define xFGLCheckExtension(dpy,i,val) \ + XextCheckExtension (dpy, i, fglext_extension_name, val) + +static int close_display(); +static /* const */ XExtensionHooks fglext_extension_hooks = { + NULL, /* create_gc */ + NULL, /* copy_gc */ + NULL, /* flush_gc */ + NULL, /* free_gc */ + NULL, /* create_font */ + NULL, /* free_font */ + close_display, /* close_display */ + NULL, /* wire_to_event */ + NULL, /* event_to_wire */ + NULL, /* error */ + NULL, /* error_string */ +}; + +static XEXT_GENERATE_FIND_DISPLAY (find_display, fglext_ext_info, + fglext_extension_name, + &fglext_extension_hooks, + ATIFGL_EXTENSION_EVENTS, NULL) + +static XEXT_GENERATE_CLOSE_DISPLAY (close_display, fglext_ext_info) + +Bool fglrx_is_dri_capable(Display *dpy, int screen) +{ + char **extensions; + int i, n_extensions, has_fglext = 0, has_fglrxdri = 0; + + extensions = XListExtensions(dpy, &n_extensions); + if (!extensions) + return False; + + for (i = 0; i < n_extensions; i++) { + if (strcmp(extensions[i], ATIFGL_EXTENSION_NAME) == 0) + has_fglext = 1; + if (strcmp(extensions[i], "ATIFGLRXDRI") == 0) + has_fglrxdri = 1; + } + XFreeExtensionList(extensions); + return has_fglext && has_fglrxdri; +} + +Bool fglrx_get_version( + Display *dpy, + int screen, + int *ddxDriverMajorVersion, + int *ddxDriverMinorVersion, + int *ddxDriverPatchVersion +) +{ + XExtDisplayInfo *info = find_display (dpy); + xFGLGetDriverDataReply rep; + xFGLGetDriverDataReq *req; + + if (ddxDriverMajorVersion) + *ddxDriverMajorVersion = 0; + if (ddxDriverMinorVersion) + *ddxDriverMinorVersion = 0; + if (ddxDriverPatchVersion) + *ddxDriverPatchVersion = 0; + + if(!XextHasExtension(info)) + return False; + + xFGLCheckExtension (dpy, info, False); + + LockDisplay (dpy); + GetReq (FGLGetDriverData, req); + req->reqType = info->codes->major_opcode; + req->fireglReqType = X_FGLGetDriverData; + req->screen = screen; + if (!_XReply (dpy, (xReply *) &rep, 0, xTrue)) { + UnlockDisplay (dpy); + SyncHandle (); + return False; + } + UnlockDisplay (dpy); + SyncHandle (); + + if (ddxDriverMajorVersion) + *ddxDriverMajorVersion = rep.majorVersion; + if (ddxDriverMinorVersion) + *ddxDriverMinorVersion = rep.minorVersion; + if (ddxDriverPatchVersion) + *ddxDriverPatchVersion = rep.patchlevel; + + return True; +} + +int fglrx_check_version(int major, int minor, int micro) +{ + static int is_initialized = -1; + static int fglrx_version_major, fglrx_version_minor, fglrx_version_micro; + + if (is_initialized < 0) { + Display *dpy = XOpenDisplay(NULL); + if (!dpy) + is_initialized = 0; + else { + is_initialized = fglrx_get_version(dpy, DefaultScreen(dpy), + &fglrx_version_major, + &fglrx_version_minor, + &fglrx_version_micro); + XCloseDisplay(dpy); + } + } + + return (is_initialized && + ((fglrx_version_major > major) || + (fglrx_version_major == major && + fglrx_version_minor > minor) || + (fglrx_version_major == major && + fglrx_version_minor == minor && + fglrx_version_micro >= micro))); +} + +Bool fglrx_get_device_id( + Display *dpy, + int screen, + unsigned int *deviceID +) +{ + XExtDisplayInfo *info = find_display(dpy); + xFGLGetDriverDataReply rep; + xFGLGetDriverDataReq *req; + + if (deviceID) + *deviceID = 0; + + if(!XextHasExtension(info)) + return False; + + xFGLCheckExtension(dpy, info, False); + + LockDisplay(dpy); + GetReq(FGLGetDriverData, req); + req->reqType = info->codes->major_opcode; + req->fireglReqType = X_FGLGetDriverData; + req->screen = screen; + if (!_XReply(dpy, (xReply *) &rep, 0, xTrue)) { + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + UnlockDisplay(dpy); + SyncHandle(); + + if (deviceID) + *deviceID = rep.deviceID; + return True; +} diff --git a/src/fglrxinfo.h b/src/fglrxinfo.h new file mode 100644 index 0000000..d654e6b --- /dev/null +++ b/src/fglrxinfo.h @@ -0,0 +1,46 @@ +/* + * fglrxinfo.h - FGLRX driver info + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef FGLRXINFO_H +#define FGLRXINFO_H + +#include <X11/Xlib.h> + +Bool fglrx_is_dri_capable(Display *dpy, int screen) + attribute_hidden; + +Bool fglrx_get_version( + Display *dpy, + int screen, + int *ddxDriverMajorVersion, + int *ddxDriverMinorVersion, + int *ddxDriverPatchVersion +) attribute_hidden; + +int fglrx_check_version(int major, int minor, int micro) + attribute_hidden; + +Bool fglrx_get_device_id( + Display *dpy, + int screen, + unsigned int *deviceID +) attribute_hidden; + +#endif /* FGLRXINFO_H */ diff --git a/src/object_heap.c b/src/object_heap.c new file mode 100644 index 0000000..6306179 --- /dev/null +++ b/src/object_heap.c @@ -0,0 +1,210 @@ +/* + * object_heap.c - Management of VA-API resources + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "object_heap.h" + +#define DEBUG 0 +#include "debug.h" + +/* This code is: + * Copyright (c) 2007 Intel Corporation. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#define LAST_FREE -1 +#define ALLOCATED -2 + +/* + * Expands the heap + * Return 0 on success, -1 on error + */ +static int object_heap_expand( object_heap_p heap ) +{ + int i; + void *new_heap_index; + int next_free; + int new_heap_size = heap->heap_size + heap->heap_increment; + + new_heap_index = (void *) realloc( heap->heap_index, new_heap_size * heap->object_size ); + if ( NULL == new_heap_index ) + { + return -1; /* Out of memory */ + } + heap->heap_index = new_heap_index; + next_free = heap->next_free; + for(i = new_heap_size; i-- > heap->heap_size; ) + { + object_base_p obj = (object_base_p) (heap->heap_index + i * heap->object_size); + obj->id = i + heap->id_offset; + obj->next_free = next_free; + next_free = i; + } + heap->next_free = next_free; + heap->heap_size = new_heap_size; + return 0; /* Success */ +} + +/* + * Return 0 on success, -1 on error + */ +int object_heap_init( object_heap_p heap, int object_size, int id_offset) +{ + heap->object_size = object_size; + heap->id_offset = id_offset & OBJECT_HEAP_OFFSET_MASK; + heap->heap_size = 0; + heap->heap_increment = 16; + heap->heap_index = NULL; + heap->next_free = LAST_FREE; + return object_heap_expand(heap); +} + +/* + * Allocates an object + * Returns the object ID on success, returns -1 on error + */ +int object_heap_allocate( object_heap_p heap ) +{ + object_base_p obj; + if ( LAST_FREE == heap->next_free ) + { + if( -1 == object_heap_expand( heap ) ) + { + return -1; /* Out of memory */ + } + } + ASSERT( heap->next_free >= 0 ); + + obj = (object_base_p) (heap->heap_index + heap->next_free * heap->object_size); + heap->next_free = obj->next_free; + obj->next_free = ALLOCATED; + return obj->id; +} + +/* + * Lookup an object by object ID + * Returns a pointer to the object on success, returns NULL on error + */ +object_base_p object_heap_lookup( object_heap_p heap, int id ) +{ + object_base_p obj; + if ( (id < heap->id_offset) || (id > (heap->heap_size+heap->id_offset)) ) + { + return NULL; + } + id &= OBJECT_HEAP_ID_MASK; + obj = (object_base_p) (heap->heap_index + id * heap->object_size); + + /* Check if the object has in fact been allocated */ + if ( obj->next_free != ALLOCATED ) + { + return NULL; + } + return obj; +} + +/* + * Iterate over all objects in the heap. + * Returns a pointer to the first object on the heap, returns NULL if heap is empty. + */ +object_base_p object_heap_first( object_heap_p heap, object_heap_iterator *iter ) +{ + *iter = -1; + return object_heap_next( heap, iter ); +} + +/* + * Iterate over all objects in the heap. + * Returns a pointer to the next object on the heap, returns NULL if heap is empty. + */ +object_base_p object_heap_next( object_heap_p heap, object_heap_iterator *iter ) +{ + object_base_p obj; + int i = *iter + 1; + while ( i < heap->heap_size) + { + obj = (object_base_p) (heap->heap_index + i * heap->object_size); + if (obj->next_free == ALLOCATED) + { + *iter = i; + return obj; + } + i++; + } + *iter = i; + return NULL; +} + + + +/* + * Frees an object + */ +void object_heap_free( object_heap_p heap, object_base_p obj ) +{ + /* Don't complain about NULL pointers */ + if (NULL != obj) + { + /* Check if the object has in fact been allocated */ + ASSERT( obj->next_free == ALLOCATED ); + + obj->next_free = heap->next_free; + heap->next_free = obj->id & OBJECT_HEAP_ID_MASK; + } +} + +/* + * Destroys a heap, the heap must be empty. + */ +void object_heap_destroy( object_heap_p heap ) +{ + object_base_p obj; + int i; + /* Check if heap is empty */ + for (i = 0; i < heap->heap_size; i++) + { + /* Check if object is not still allocated */ + obj = (object_base_p) (heap->heap_index + i * heap->object_size); + ASSERT( obj->next_free != ALLOCATED ); + } + free(heap->heap_index); + heap->heap_size = 0; + heap->heap_index = NULL; + heap->next_free = LAST_FREE; +} diff --git a/src/object_heap.h b/src/object_heap.h new file mode 100644 index 0000000..25aefb4 --- /dev/null +++ b/src/object_heap.h @@ -0,0 +1,116 @@ +/* + * object_heap.h - Management of VA-API resources + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* This code is: + * Copyright (c) 2007 Intel Corporation. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef VA_OBJECT_HEAP_H +#define VA_OBJECT_HEAP_H + +#define OBJECT_HEAP_OFFSET_MASK 0x7f000000 +#define OBJECT_HEAP_ID_MASK 0x00ffffff + +typedef struct object_base *object_base_p; +typedef struct object_heap *object_heap_p; + +struct object_base { + int id; + int next_free; +}; + +struct object_heap { + int object_size; + int id_offset; + void *heap_index; + int next_free; + int heap_size; + int heap_increment; +}; + +typedef int object_heap_iterator; + +/* + * Return 0 on success, -1 on error + */ +int object_heap_init(object_heap_p heap, int object_size, int id_offset) + attribute_hidden; + +/* + * Allocates an object + * Returns the object ID on success, returns -1 on error + */ +int object_heap_allocate(object_heap_p heap) + attribute_hidden; + +/* + * Lookup an allocated object by object ID + * Returns a pointer to the object on success, returns NULL on error + */ +object_base_p object_heap_lookup(object_heap_p heap, int id) + attribute_hidden; + +/* + * Iterate over all objects in the heap. + * Returns a pointer to the first object on the heap, returns NULL if heap is empty. + */ +object_base_p object_heap_first(object_heap_p heap, object_heap_iterator *iter) + attribute_hidden; + +/* + * Iterate over all objects in the heap. + * Returns a pointer to the next object on the heap, returns NULL if heap is empty. + */ +object_base_p object_heap_next(object_heap_p heap, object_heap_iterator *iter) + attribute_hidden; + +/* + * Frees an object + */ +void object_heap_free(object_heap_p heap, object_base_p obj) + attribute_hidden; + +/* + * Destroys a heap, the heap must be empty. + */ +void object_heap_destroy(object_heap_p heap) + attribute_hidden; + +#endif /* VA_OBJECT_HEAP_H */ diff --git a/src/shaders/Bicubic.cg b/src/shaders/Bicubic.cg new file mode 100644 index 0000000..92fe67b --- /dev/null +++ b/src/shaders/Bicubic.cg @@ -0,0 +1,179 @@ +/* + * Bicubic.cg - XvBA backend for VA-API (Bicubic filtering) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* + * GPU Gems - Chapter 20. Fast Third-Order Texture Filtering: + * <http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter20.html> + * + * 1 + * w0(a) = - * (-a^3 + 3*a^2 - 3*a + 1) + * 6 + * + * 1 + * w1(a) = - * (3*a^3 - 6*a^2 + 4) + * 6 + * + * 1 + * w2(a) = - * (-3*a^3 + 3*a^2 + 3*a + 1) + * 6 + * + * 1 + * w3(a) = - * a^3 + * 6 + * + * w1(x) + * g0(x) = w0(x) + w1(x) ; h0(x) = 1 - ------------- + x + * w0(x) + w1(x) + * + * w3(x) + * g1(x) = w2(x) + w3(x) ; h1(x) = 1 + ------------- - x + * w2(x) + w3(x) + */ + +#ifdef USE_TEXTURE_RECTANGLE +#define Sampler samplerRECT +#define loadTexture texRECT +#else +#define Sampler sampler2D +#define loadTexture tex2D +#endif + +float2 unpackCoord(float2 coord, float4 textureSize) +{ +#ifndef USE_TEXTURE_RECTANGLE + coord *= textureSize.xy; +#endif + return coord; +} + +float2 packCoord(float2 coord, float4 textureSize) +{ +#ifndef USE_TEXTURE_RECTANGLE + coord *= textureSize.zw; +#endif + return coord; +} + +struct Bicubic_input { + float2 coord : TEXCOORD0; + Sampler tex : TEXUNIT0; +#ifdef USE_TEXTURE_FLOAT + sampler1D tex_hg : TEXUNIT1; +#endif +}; + +struct Bicubic_output { + float4 color : COLOR; +}; + +#ifndef USE_TEXTURE_FLOAT +float2 e2(float2 a) +{ + return a * a; +} + +float2 e3(float2 a) +{ + return e2(a) * a; +} + +float2 w0(float2 a) +{ + return (1.0f/6.0f) * (-e3(a) + 3.0f*e2(a) - 3.0f*a + 1.0f); +} + +float2 w1(float2 a) +{ + return (1.0f/6.0f) * (3.0f*e3(a) - 6.0f*e2(a) + 4.0f); +} + +float2 w2(float2 a) +{ + return (1.0f/6.0f) * (-3.0f*e3(a) + 3.0f*e2(a) + 3.0f*a + 1.0f); +} + +float2 w3(float2 a) +{ + return (1.0f/6.0f) * (e3(a)); +} + +float2 g0(float2 x) +{ + return w0(x) + w1(x); +} + +float2 h0(float2 x) +{ + //return 1.0f - w1(x) / (w0(x) + w1(x)) + x; + return -1.0f + w1(x) / g0(x) + 0.5f; +} + +float2 g1(float2 x) +{ + return w2(x) + w3(x); +} + +float2 h1(float2 x) +{ + //return 1.0f + w3(x) / (w2(x) + w3(x)) - x; + return 1.0f + w3(x) / g1(x) + 0.5f; +} +#endif + +float4 cubicTex2D(Bicubic_input IN, float4 textureSize) +{ + const float2 coord = unpackCoord(IN.coord, textureSize) - 0.5f; + const float2 int_coord = floor(coord); +#ifdef USE_TEXTURE_FLOAT + const float4 hg_x = tex1D(IN.tex_hg, coord.x); + const float4 hg_y = tex1D(IN.tex_hg, coord.y); + const float2 g[2] = { float2(hg_x.z, hg_y.z), float2(hg_x.w, hg_y.w) }; + const float2 h[2] = { float2(hg_x.x, hg_y.x)+int_coord, float2(hg_x.y, hg_y.y)+int_coord }; +#else + const float2 frc_coord = coord - int_coord; + const float2 g[2] = { g0(frc_coord), g1(frc_coord) }; + const float2 h[2] = { h0(frc_coord)+int_coord, h1(frc_coord)+int_coord }; +#endif + + const float2 coord00 = packCoord(float2(h[0].x, h[0].y), textureSize); + const float2 coord10 = packCoord(float2(h[1].x, h[0].y), textureSize); + const float2 coord01 = packCoord(float2(h[0].x, h[1].y), textureSize); + const float2 coord11 = packCoord(float2(h[1].x, h[1].y), textureSize); + + float4 tex00 = loadTexture(IN.tex, coord00); + float4 tex10 = loadTexture(IN.tex, coord10); + float4 tex01 = loadTexture(IN.tex, coord01); + float4 tex11 = loadTexture(IN.tex, coord11); + + tex00 = g[0].y * tex00 + g[1].y * tex01; + tex10 = g[0].y * tex10 + g[1].y * tex11; + return (g[0].x * tex00 + g[1].x * tex10); +} + +Bicubic_output Bicubic_main( + Bicubic_input IN, + uniform float4 textureSize +) +{ + Bicubic_output OUT; + + OUT.color = cubicTex2D(IN, textureSize); + return OUT; +} diff --git a/src/shaders/Bicubic.pso b/src/shaders/Bicubic.pso new file mode 100644 index 0000000..35d0693 --- /dev/null +++ b/src/shaders/Bicubic.pso @@ -0,0 +1,74 @@ +!!ARBfp1.0 +# cgc version 3.0.0007, build date Jul 22 2010 +# command line args: -profile arbfp1 +# source file: Bicubic.cg +#vendor NVIDIA Corporation +#version 3.0.0.07 +#profile arbfp1 +#program Bicubic_main +#semantic Bicubic_main.IN +#semantic Bicubic_main.textureSize +#var float2 IN.coord : $vin.TEXCOORD0 : TEX0 : 0 : 1 +#var sampler2D IN.tex : TEXUNIT0 : texunit 0 : 0 : 1 +#var float4 textureSize : : c[0] : 1 : 1 +#var float4 Bicubic_main.color : $vout.COLOR : COL : -1 : 1 +#const c[1] = 0.5 3 1 6 +#const c[2] = 4 0.16666667 -0.5 0 +#const c[3] = -3 1.5 0 +PARAM c[4] = { program.local[0], + { 0.5, 3, 1, 6 }, + { 4, 0.16666667, -0.5, 0 }, + { -3, 1.5, 0 } }; +TEMP R0; +TEMP R1; +TEMP R2; +TEMP R3; +MUL R0.xy, fragment.texcoord[0], c[0]; +ADD R0.xy, R0, -c[1].x; +FLR R1.xy, R0; +ADD R2.zw, R0.xyxy, -R1.xyxy; +MUL R3.xy, R2.zwzw, R2.zwzw; +MUL R2.xy, R2.zwzw, R3; +MUL R0.xy, R3, c[1].y; +MAD R0.zw, R2.xyxy, c[3].x, R0.xyxy; +MAD R0.xy, R2.zwzw, -R3, R0; +MAD R0.zw, R2, c[1].y, R0; +MUL R1.zw, -R3.xyxy, c[1].w; +MAD R0.xy, -R2.zwzw, c[1].y, R0; +ADD R0.zw, R0, c[1].z; +MAD R0.zw, R2, R3.xyxy, R0; +MAD R1.zw, R2.xyxy, c[1].y, R1; +MUL R0.zw, R0, c[2].y; +ADD R0.xy, R0, c[1].z; +ADD R2.zw, R1, c[2].x; +ADD R1.zw, R0.xyxy, R2; +RCP R0.y, R0.w; +RCP R0.x, R0.z; +MUL R0.xy, R2, R0; +MUL R2.xy, R1.zwzw, c[2].y; +MAD R1.zw, R0.xyxy, c[2].y, R1.xyxy; +RCP R0.y, R2.y; +RCP R0.x, R2.x; +MUL R0.xy, R2.zwzw, R0; +MAD R0.xy, R0, c[2].y, R1; +ADD R2.zw, R1, c[3].y; +MUL R1.xy, R2.zwzw, c[0].zwzw; +TEX R3, R1, texture[0], 2D; +ADD R0.xy, R0, c[2].z; +MOV R1.x, R2.z; +MOV R1.y, R0; +MOV R2.z, R0.x; +MUL R1.xy, R1, c[0].zwzw; +MUL R3, R0.w, R3; +TEX R1, R1, texture[0], 2D; +MAD R1, R2.y, R1, R3; +MUL R2.zw, R2, c[0]; +TEX R3, R2.zwzw, texture[0], 2D; +MUL R1, R0.z, R1; +MUL R3, R3, R0.w; +MUL R0.xy, R0, c[0].zwzw; +TEX R0, R0, texture[0], 2D; +MAD R0, R0, R2.y, R3; +MAD result.color, R0, R2.x, R1; +END +# 47 instructions, 4 R-regs diff --git a/src/shaders/Bicubic_FLOAT.pso b/src/shaders/Bicubic_FLOAT.pso new file mode 100644 index 0000000..1e083bc --- /dev/null +++ b/src/shaders/Bicubic_FLOAT.pso @@ -0,0 +1,53 @@ +!!ARBfp1.0 +# cgc version 3.0.0007, build date Jul 22 2010 +# command line args: -profile arbfp1 -DUSE_TEXTURE_FLOAT +# source file: Bicubic.cg +#vendor NVIDIA Corporation +#version 3.0.0.07 +#profile arbfp1 +#program Bicubic_main +#semantic Bicubic_main.IN +#semantic Bicubic_main.textureSize +#var float2 IN.coord : $vin.TEXCOORD0 : TEX0 : 0 : 1 +#var sampler2D IN.tex : TEXUNIT0 : texunit 0 : 0 : 1 +#var sampler1D IN.tex_hg : TEXUNIT1 : texunit 1 : 0 : 1 +#var float4 textureSize : : c[0] : 1 : 1 +#var float4 Bicubic_main.color : $vout.COLOR : COL : -1 : 1 +#const c[1] = 0.5 +PARAM c[2] = { program.local[0], + { 0.5 } }; +TEMP R0; +TEMP R1; +TEMP R2; +TEMP R3; +TEMP R4; +MUL R0.xy, fragment.texcoord[0], c[0]; +ADD R0.zw, R0.xyxy, -c[1].x; +TEX R3, R0.w, texture[1], 1D; +TEX R4, R0.z, texture[1], 1D; +MOV R0.y, R3; +FLR R0.zw, R0; +MOV R0.x, R4.y; +ADD R1.zw, R0, R0.xyxy; +MUL R0.xy, R1.zwzw, c[0].zwzw; +MOV R2.x, R1.z; +MOV R1.y, R3.x; +MOV R1.x, R4; +ADD R1.xy, R1, R0.zwzw; +MOV R2.y, R1; +MOV R1.z, R1.x; +MUL R3.xy, R2, c[0].zwzw; +TEX R0, R0, texture[0], 2D; +MUL R2, R3.w, R0; +TEX R0, R3, texture[0], 2D; +MAD R0, R3.z, R0, R2; +MUL R1.zw, R1, c[0]; +MUL R2, R4.w, R0; +TEX R0, R1.zwzw, texture[0], 2D; +MUL R3.xy, R1, c[0].zwzw; +MUL R1, R3.w, R0; +TEX R0, R3, texture[0], 2D; +MAD R0, R3.z, R0, R1; +MAD result.color, R4.z, R0, R2; +END +# 28 instructions, 5 R-regs diff --git a/src/shaders/Evergreen.cg b/src/shaders/Evergreen.cg new file mode 100644 index 0000000..9f89a46 --- /dev/null +++ b/src/shaders/Evergreen.cg @@ -0,0 +1,65 @@ +/* + * Evergreen.cg - XvBA backend for VA-API (workaround for Evergreen rendering) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +struct Evergreen_input { + float2 coord : TEXCOORD0; + sampler2D tex : TEXUNIT0; +}; + +struct Evergreen_output { + float4 color : COLOR; +}; + +/* + * params.xy = 1/(2*n); + * params.zw = (n); + */ +float2 swap_l(float2 v, float4 params) +{ + return 2.0 * params.zw * floor(v * params.xy); +} + +float2 swap_r(float2 v, float4 params) +{ + return (v + params.zw) - 2.0 * params.zw * floor((v + params.zw) * params.xy); +} + +float2 swap(float2 v, float4 params) +{ + return swap_l(v, params) + swap_r(v, params); +} + +Evergreen_output Evergreen_main( + Evergreen_input IN, + uniform float4 textureSize, + uniform float4 mix_params[MIX_PARAMS] +) +{ + Evergreen_output OUT; + + float2 coord = IN.coord; + coord *= textureSize.xy; + for (int i = 0; i < MIX_PARAMS; i++) + coord = swap(coord, mix_params[i]); + coord *= textureSize.zw; + + OUT.color = tex2D(IN.tex, coord); + return OUT; +} diff --git a/src/shaders/Evergreen_mix_1.cg b/src/shaders/Evergreen_mix_1.cg new file mode 100644 index 0000000..23d688c --- /dev/null +++ b/src/shaders/Evergreen_mix_1.cg @@ -0,0 +1,22 @@ +/* + * Evergreen.cg - XvBA backend for VA-API (workaround for Evergreen rendering) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define MIX_PARAMS 1 +#include "Evergreen.cg" diff --git a/src/shaders/Evergreen_mix_1.pso b/src/shaders/Evergreen_mix_1.pso new file mode 100644 index 0000000..4fc472b --- /dev/null +++ b/src/shaders/Evergreen_mix_1.pso @@ -0,0 +1,35 @@ +!!ARBfp1.0 +# cgc version 3.0.0007, build date Jul 22 2010 +# command line args: -profile arbfp1 +# source file: Evergreen_mix_1.cg +#vendor NVIDIA Corporation +#version 3.0.0.07 +#profile arbfp1 +#program Evergreen_main +#semantic Evergreen_main.IN +#semantic Evergreen_main.textureSize +#semantic Evergreen_main.mix_params +#var float2 IN.coord : $vin.TEXCOORD0 : TEX0 : 0 : 1 +#var sampler2D IN.tex : TEXUNIT0 : texunit 0 : 0 : 1 +#var float4 textureSize : : c[0] : 1 : 1 +#var float4 mix_params[0] : : c[1] : 2 : 1 +#var float4 Evergreen_main.color : $vout.COLOR : COL : -1 : 1 +#const c[2] = 2 +PARAM c[3] = { program.local[0..1], + { 2 } }; +TEMP R0; +TEMP R1; +MUL R0.zw, fragment.texcoord[0].xyxy, c[0].xyxy; +ADD R0.xy, R0.zwzw, c[1].zwzw; +MUL R1.xy, R0, c[1]; +FLR R1.xy, R1; +MUL R0.zw, R0, c[1].xyxy; +MUL R1.xy, R1, c[1].zwzw; +FLR R0.zw, R0; +MAD R0.xy, -R1, c[2].x, R0; +MUL R0.zw, R0, c[1]; +MAD R0.xy, R0.zwzw, c[2].x, R0; +MUL R0.xy, R0, c[0].zwzw; +TEX result.color, R0, texture[0], 2D; +END +# 12 instructions, 2 R-regs diff --git a/src/shaders/Evergreen_mix_2.cg b/src/shaders/Evergreen_mix_2.cg new file mode 100644 index 0000000..48183ee --- /dev/null +++ b/src/shaders/Evergreen_mix_2.cg @@ -0,0 +1,22 @@ +/* + * Evergreen.cg - XvBA backend for VA-API (workaround for Evergreen rendering) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define MIX_PARAMS 2 +#include "Evergreen.cg" diff --git a/src/shaders/Evergreen_mix_2.pso b/src/shaders/Evergreen_mix_2.pso new file mode 100644 index 0000000..9bc5dcc --- /dev/null +++ b/src/shaders/Evergreen_mix_2.pso @@ -0,0 +1,45 @@ +!!ARBfp1.0 +# cgc version 3.0.0007, build date Jul 22 2010 +# command line args: -profile arbfp1 +# source file: Evergreen_mix_2.cg +#vendor NVIDIA Corporation +#version 3.0.0.07 +#profile arbfp1 +#program Evergreen_main +#semantic Evergreen_main.IN +#semantic Evergreen_main.textureSize +#semantic Evergreen_main.mix_params +#var float2 IN.coord : $vin.TEXCOORD0 : TEX0 : 0 : 1 +#var sampler2D IN.tex : TEXUNIT0 : texunit 0 : 0 : 1 +#var float4 textureSize : : c[0] : 1 : 1 +#var float4 mix_params[0] : : c[1] : 2 : 1 +#var float4 mix_params[1] : : c[2] : 2 : 1 +#var float4 Evergreen_main.color : $vout.COLOR : COL : -1 : 1 +#const c[3] = 2 +PARAM c[4] = { program.local[0..2], + { 2 } }; +TEMP R0; +TEMP R1; +MUL R0.zw, fragment.texcoord[0].xyxy, c[0].xyxy; +ADD R0.xy, R0.zwzw, c[1].zwzw; +MUL R1.xy, R0, c[1]; +FLR R1.xy, R1; +MUL R1.xy, R1, c[1].zwzw; +MUL R0.zw, R0, c[1].xyxy; +FLR R0.zw, R0; +MAD R0.xy, -R1, c[3].x, R0; +MUL R0.zw, R0, c[1]; +MAD R0.zw, R0, c[3].x, R0.xyxy; +ADD R0.xy, R0.zwzw, c[2].zwzw; +MUL R1.xy, R0, c[2]; +FLR R1.xy, R1; +MUL R0.zw, R0, c[2].xyxy; +MUL R1.xy, R1, c[2].zwzw; +FLR R0.zw, R0; +MAD R0.xy, -R1, c[3].x, R0; +MUL R0.zw, R0, c[2]; +MAD R0.xy, R0.zwzw, c[3].x, R0; +MUL R0.xy, R0, c[0].zwzw; +TEX result.color, R0, texture[0], 2D; +END +# 21 instructions, 2 R-regs diff --git a/src/shaders/Evergreen_mix_3.cg b/src/shaders/Evergreen_mix_3.cg new file mode 100644 index 0000000..7626301 --- /dev/null +++ b/src/shaders/Evergreen_mix_3.cg @@ -0,0 +1,22 @@ +/* + * Evergreen.cg - XvBA backend for VA-API (workaround for Evergreen rendering) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define MIX_PARAMS 3 +#include "Evergreen.cg" diff --git a/src/shaders/Evergreen_mix_3.pso b/src/shaders/Evergreen_mix_3.pso new file mode 100644 index 0000000..e165bd5 --- /dev/null +++ b/src/shaders/Evergreen_mix_3.pso @@ -0,0 +1,55 @@ +!!ARBfp1.0 +# cgc version 3.0.0007, build date Jul 22 2010 +# command line args: -profile arbfp1 +# source file: Evergreen_mix_3.cg +#vendor NVIDIA Corporation +#version 3.0.0.07 +#profile arbfp1 +#program Evergreen_main +#semantic Evergreen_main.IN +#semantic Evergreen_main.textureSize +#semantic Evergreen_main.mix_params +#var float2 IN.coord : $vin.TEXCOORD0 : TEX0 : 0 : 1 +#var sampler2D IN.tex : TEXUNIT0 : texunit 0 : 0 : 1 +#var float4 textureSize : : c[0] : 1 : 1 +#var float4 mix_params[0] : : c[1] : 2 : 1 +#var float4 mix_params[1] : : c[2] : 2 : 1 +#var float4 mix_params[2] : : c[3] : 2 : 1 +#var float4 Evergreen_main.color : $vout.COLOR : COL : -1 : 1 +#const c[4] = 2 +PARAM c[5] = { program.local[0..3], + { 2 } }; +TEMP R0; +TEMP R1; +MUL R0.zw, fragment.texcoord[0].xyxy, c[0].xyxy; +ADD R0.xy, R0.zwzw, c[1].zwzw; +MUL R1.xy, R0, c[1]; +FLR R1.xy, R1; +MUL R1.xy, R1, c[1].zwzw; +MUL R0.zw, R0, c[1].xyxy; +FLR R0.zw, R0; +MAD R0.xy, -R1, c[4].x, R0; +MUL R0.zw, R0, c[1]; +MAD R0.zw, R0, c[4].x, R0.xyxy; +ADD R0.xy, R0.zwzw, c[2].zwzw; +MUL R1.xy, R0, c[2]; +FLR R1.xy, R1; +MUL R1.xy, R1, c[2].zwzw; +MUL R0.zw, R0, c[2].xyxy; +FLR R0.zw, R0; +MAD R0.xy, -R1, c[4].x, R0; +MUL R0.zw, R0, c[2]; +MAD R0.zw, R0, c[4].x, R0.xyxy; +ADD R0.xy, R0.zwzw, c[3].zwzw; +MUL R1.xy, R0, c[3]; +FLR R1.xy, R1; +MUL R0.zw, R0, c[3].xyxy; +MUL R1.xy, R1, c[3].zwzw; +FLR R0.zw, R0; +MAD R0.xy, -R1, c[4].x, R0; +MUL R0.zw, R0, c[3]; +MAD R0.xy, R0.zwzw, c[4].x, R0; +MUL R0.xy, R0, c[0].zwzw; +TEX result.color, R0, texture[0], 2D; +END +# 30 instructions, 2 R-regs diff --git a/src/shaders/Evergreen_mix_4.cg b/src/shaders/Evergreen_mix_4.cg new file mode 100644 index 0000000..f584ab4 --- /dev/null +++ b/src/shaders/Evergreen_mix_4.cg @@ -0,0 +1,22 @@ +/* + * Evergreen.cg - XvBA backend for VA-API (workaround for Evergreen rendering) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define MIX_PARAMS 4 +#include "Evergreen.cg" diff --git a/src/shaders/Evergreen_mix_4.pso b/src/shaders/Evergreen_mix_4.pso new file mode 100644 index 0000000..bd78fb1 --- /dev/null +++ b/src/shaders/Evergreen_mix_4.pso @@ -0,0 +1,65 @@ +!!ARBfp1.0 +# cgc version 3.0.0007, build date Jul 22 2010 +# command line args: -profile arbfp1 +# source file: Evergreen_mix_4.cg +#vendor NVIDIA Corporation +#version 3.0.0.07 +#profile arbfp1 +#program Evergreen_main +#semantic Evergreen_main.IN +#semantic Evergreen_main.textureSize +#semantic Evergreen_main.mix_params +#var float2 IN.coord : $vin.TEXCOORD0 : TEX0 : 0 : 1 +#var sampler2D IN.tex : TEXUNIT0 : texunit 0 : 0 : 1 +#var float4 textureSize : : c[0] : 1 : 1 +#var float4 mix_params[0] : : c[1] : 2 : 1 +#var float4 mix_params[1] : : c[2] : 2 : 1 +#var float4 mix_params[2] : : c[3] : 2 : 1 +#var float4 mix_params[3] : : c[4] : 2 : 1 +#var float4 Evergreen_main.color : $vout.COLOR : COL : -1 : 1 +#const c[5] = 2 +PARAM c[6] = { program.local[0..4], + { 2 } }; +TEMP R0; +TEMP R1; +MUL R0.zw, fragment.texcoord[0].xyxy, c[0].xyxy; +ADD R0.xy, R0.zwzw, c[1].zwzw; +MUL R1.xy, R0, c[1]; +FLR R1.xy, R1; +MUL R1.xy, R1, c[1].zwzw; +MUL R0.zw, R0, c[1].xyxy; +FLR R0.zw, R0; +MAD R0.xy, -R1, c[5].x, R0; +MUL R0.zw, R0, c[1]; +MAD R0.zw, R0, c[5].x, R0.xyxy; +ADD R0.xy, R0.zwzw, c[2].zwzw; +MUL R1.xy, R0, c[2]; +FLR R1.xy, R1; +MUL R1.xy, R1, c[2].zwzw; +MUL R0.zw, R0, c[2].xyxy; +FLR R0.zw, R0; +MAD R0.xy, -R1, c[5].x, R0; +MUL R0.zw, R0, c[2]; +MAD R0.zw, R0, c[5].x, R0.xyxy; +ADD R0.xy, R0.zwzw, c[3].zwzw; +MUL R1.xy, R0, c[3]; +FLR R1.xy, R1; +MUL R1.xy, R1, c[3].zwzw; +MUL R0.zw, R0, c[3].xyxy; +FLR R0.zw, R0; +MAD R0.xy, -R1, c[5].x, R0; +MUL R0.zw, R0, c[3]; +MAD R0.zw, R0, c[5].x, R0.xyxy; +ADD R0.xy, R0.zwzw, c[4].zwzw; +MUL R1.xy, R0, c[4]; +FLR R1.xy, R1; +MUL R0.zw, R0, c[4].xyxy; +MUL R1.xy, R1, c[4].zwzw; +FLR R0.zw, R0; +MAD R0.xy, -R1, c[5].x, R0; +MUL R0.zw, R0, c[4]; +MAD R0.xy, R0.zwzw, c[5].x, R0; +MUL R0.xy, R0, c[0].zwzw; +TEX result.color, R0, texture[0], 2D; +END +# 39 instructions, 2 R-regs diff --git a/src/shaders/Makefile.am b/src/shaders/Makefile.am new file mode 100644 index 0000000..3408283 --- /dev/null +++ b/src/shaders/Makefile.am @@ -0,0 +1,46 @@ +shaders_c = \ + Bicubic.cg \ + NV12.cg \ + YV12.cg \ + ProcAmp.cg \ + $(NULL) + +shaders_c += Evergreen_mix_1.cg +shaders_c += Evergreen_mix_2.cg +shaders_c += Evergreen_mix_3.cg +shaders_c += Evergreen_mix_4.cg + +shaders_o = $(shaders_c:%.cg=%.pso) +shaders_o += Bicubic_FLOAT.pso + +shaders_h = $(shaders_o:%.pso=%.h) + +BUILT_SOURCES = +CLEANFILES = +EXTRA_DIST = pso2h.py Evergreen.cg $(shaders_c) $(shaders_o) $(shaders_h) + +# Only add those targets if python is available +if HAVE_PYTHON +BUILT_SOURCES += $(shaders_h) +CLEANFILES += $(shaders_h) + +%.h: %.pso pso2h.py + $(PYTHON) pso2h.py -n $*_fp -o $@ $< +endif + +# Only add those targets if the Cg compiler is available +if HAVE_CGC +BUILT_SOURCES += $(shaders_o) + +Evergreen_%.pso: Evergreen_%.cg Evergreen.cg + $(CGC) -entry Evergreen_main -profile arbfp1 -o $@ $< + +%.pso: %.cg + $(CGC) -entry $*_main -profile arbfp1 -o $@ $< + +%_FLOAT.pso: %.cg + $(CGC) -entry $*_main -profile arbfp1 -o $@ -DUSE_TEXTURE_FLOAT $< +endif + +# Extra clean files so that maintainer-clean removes *everything* +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/shaders/NV12.cg b/src/shaders/NV12.cg new file mode 100644 index 0000000..47cba47 --- /dev/null +++ b/src/shaders/NV12.cg @@ -0,0 +1,46 @@ +/* + * NV12.cg - XvBA backend for VA-API (NV12 -> RGB colorspace conversion) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +struct NV12_input { + float2 Y : TEXCOORD0; + sampler2D Y_tex : TEXUNIT0; + sampler2D UV_tex : TEXUNIT1; +}; + +struct NV12_output { + float4 color : COLOR; +}; + +NV12_output NV12_main(NV12_input IN) +{ + NV12_output OUT; + + /* See YV12.cg for explanations on the color conversion matrix */ + float y = 1.16438356 * (tex2D(IN.Y_tex, IN.Y).x - 0.0625); + float u = tex2D(IN.UV_tex, IN.Y).x - 0.5; + float v = tex2D(IN.UV_tex, IN.Y).w - 0.5; + + OUT.color.r = y + 1.59602678 * v; + OUT.color.g = y - 0.39176229 * u - 0.81296764 * v; + OUT.color.b = y + 2.01723214 * u; + OUT.color.a = 1.0; + + return OUT; +} diff --git a/src/shaders/NV12.pso b/src/shaders/NV12.pso new file mode 100644 index 0000000..b747ae2 --- /dev/null +++ b/src/shaders/NV12.pso @@ -0,0 +1,31 @@ +!!ARBfp1.0 +# cgc version 3.0.0007, build date Jul 22 2010 +# command line args: -profile arbfp1 +# source file: NV12.cg +#vendor NVIDIA Corporation +#version 3.0.0.07 +#profile arbfp1 +#program NV12_main +#semantic NV12_main.IN +#var float2 IN.Y : $vin.TEXCOORD0 : TEX0 : 0 : 1 +#var sampler2D IN.Y_tex : TEXUNIT0 : texunit 0 : 0 : 1 +#var sampler2D IN.UV_tex : TEXUNIT1 : texunit 1 : 0 : 1 +#var float4 NV12_main.color : $vout.COLOR : COL : -1 : 1 +#const c[0] = 1 1.1643835 0.0625 2.0172322 +#const c[1] = 0.5 0.39176229 0.81296766 1.5960268 +PARAM c[2] = { { 1, 1.1643835, 0.0625, 2.0172322 }, + { 0.5, 0.39176229, 0.81296766, 1.5960268 } }; +TEMP R0; +TEX R0.x, fragment.texcoord[0], texture[0], 2D; +ADD R0.y, R0.x, -c[0].z; +TEX R0.xw, fragment.texcoord[0], texture[1], 2D; +MUL R0.y, R0, c[0]; +ADD R0.x, R0, -c[1]; +ADD R0.w, R0, -c[1].x; +MAD R0.z, -R0.x, c[1].y, R0.y; +MAD result.color.y, -R0.w, c[1].z, R0.z; +MAD result.color.z, R0.x, c[0].w, R0.y; +MAD result.color.x, R0.w, c[1].w, R0.y; +MOV result.color.w, c[0].x; +END +# 11 instructions, 1 R-regs diff --git a/src/shaders/ProcAmp.cg b/src/shaders/ProcAmp.cg new file mode 100644 index 0000000..99383b1 --- /dev/null +++ b/src/shaders/ProcAmp.cg @@ -0,0 +1,49 @@ +/* + * ProcAmp.cg - XvBA backend for VA-API (ProcAmp adjustments) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define USE_TEXTURE 1 + +struct ProcAmp_input { +#ifdef USE_TEXTURE + float2 coords : TEXCOORD0; + sampler2D texture : TEXUNIT0; +#else + float4 color : COLOR; +#endif +}; + +struct ProcAmp_output { + float4 color : COLOR; +}; + +ProcAmp_output ProcAmp_main(ProcAmp_input IN, uniform float4x4 color_matrix) +{ + ProcAmp_output OUT; +#ifdef USE_TEXTURE + const float4 color = tex2D(IN.texture, IN.coords); +#else + const float4 color = IN.color; +#endif + + OUT.color.rgb = mul(color_matrix, color).rgb; + OUT.color.a = color.a; + + return OUT; +} diff --git a/src/shaders/ProcAmp.pso b/src/shaders/ProcAmp.pso new file mode 100644 index 0000000..9bb6573 --- /dev/null +++ b/src/shaders/ProcAmp.pso @@ -0,0 +1,23 @@ +!!ARBfp1.0 +# cgc version 3.0.0007, build date Jul 22 2010 +# command line args: -profile arbfp1 +# source file: ProcAmp.cg +#vendor NVIDIA Corporation +#version 3.0.0.07 +#profile arbfp1 +#program ProcAmp_main +#semantic ProcAmp_main.IN +#semantic ProcAmp_main.color_matrix +#var float2 IN.coords : $vin.TEXCOORD0 : TEX0 : 0 : 1 +#var sampler2D IN.texture : TEXUNIT0 : texunit 0 : 0 : 1 +#var float4x4 color_matrix : : c[0], 4 : 1 : 1 +#var float4 ProcAmp_main.color : $vout.COLOR : COL : -1 : 1 +PARAM c[4] = { program.local[0..3] }; +TEMP R0; +TEX R0, fragment.texcoord[0], texture[0], 2D; +MOV result.color.w, R0; +DP4 result.color.z, R0, c[2]; +DP4 result.color.y, R0, c[1]; +DP4 result.color.x, R0, c[0]; +END +# 5 instructions, 1 R-regs diff --git a/src/shaders/YV12.cg b/src/shaders/YV12.cg new file mode 100644 index 0000000..87fec78 --- /dev/null +++ b/src/shaders/YV12.cg @@ -0,0 +1,76 @@ +/* + * YV12.cg - XvBA backend for VA-API (YV12 -> RGB colorspace conversion) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* + * For 8-bit per sample: + * + * 219 219 219 + * Y = 16 + --- * Kr * R + --- * (1 - Kr - Kb) * G + --- * Kb * B + * 255 255 255 + * + * 112 Kr 112 1 - (Kr + Kb) 112 + * U = 128 - --- * ------ * R - --- * ------------- * G + --- * B + * 255 1 - Kb 255 1 - Kb 255 + * + * 112 112 1 - (Kr + Kb) 112 Kb + * V = 128 + --- * R - --- * ------------- * G - --- * ------ * B + * 255 255 1 - Kr 255 1 - Kr + * + * Constants for SDTV (ITU-R BT.601): + * Kb = 0.114 + * Kr = 0.299 + * + * Constants for HDTV (ITU-R BT.709): + * Kb = 0.0722 + * Kr = 0.2126 + * + * Matrix generation with xcas: + * inverse([ + * [ Kr , 1-(Kr+Kb) , Kb ]*219/255, + * [ -Kr/(1-Kb) , -(1-(Kr+Kb))/(1-Kb) , 1 ]*112/255, + * [ 1 , -(1-(Kr+Kb))/(1-Kr) , -Kb/(1-Kr) ]*112/255]) + */ + +struct YV12_input { + float2 Y : TEXCOORD0; + sampler2D Y_tex : TEXUNIT0; + sampler2D U_tex : TEXUNIT2; + sampler2D V_tex : TEXUNIT1; +}; + +struct YV12_output { + float4 color : COLOR; +}; + +YV12_output YV12_main(YV12_input IN) +{ + YV12_output OUT; + + float y = 1.16438356 * (tex2D(IN.Y_tex, IN.Y).g - 0.0625); + float u = tex2D(IN.U_tex, IN.Y).g - 0.5; + float v = tex2D(IN.V_tex, IN.Y).g - 0.5; + + OUT.color.r = y + 1.59602678 * v; + OUT.color.g = y - 0.39176229 * u - 0.81296764 * v; + OUT.color.b = y + 2.01723214 * u; + OUT.color.a = 1.0; + + return OUT; +} diff --git a/src/shaders/YV12.pso b/src/shaders/YV12.pso new file mode 100644 index 0000000..f3a896b --- /dev/null +++ b/src/shaders/YV12.pso @@ -0,0 +1,33 @@ +!!ARBfp1.0 +# cgc version 3.0.0007, build date Jul 22 2010 +# command line args: -profile arbfp1 +# source file: YV12.cg +#vendor NVIDIA Corporation +#version 3.0.0.07 +#profile arbfp1 +#program YV12_main +#semantic YV12_main.IN +#var float2 IN.Y : $vin.TEXCOORD0 : TEX0 : 0 : 1 +#var sampler2D IN.Y_tex : TEXUNIT0 : texunit 0 : 0 : 1 +#var sampler2D IN.U_tex : TEXUNIT2 : texunit 2 : 0 : 1 +#var sampler2D IN.V_tex : TEXUNIT1 : texunit 1 : 0 : 1 +#var float4 YV12_main.color : $vout.COLOR : COL : -1 : 1 +#const c[0] = 1 1.1643835 0.0625 2.0172322 +#const c[1] = 0.5 0.39176229 0.81296766 1.5960268 +PARAM c[2] = { { 1, 1.1643835, 0.0625, 2.0172322 }, + { 0.5, 0.39176229, 0.81296766, 1.5960268 } }; +TEMP R0; +TEX R0.y, fragment.texcoord[0], texture[0], 2D; +ADD R0.x, R0.y, -c[0].z; +TEX R0.y, fragment.texcoord[0], texture[2], 2D; +ADD R0.z, R0.y, -c[1].x; +MUL R0.x, R0, c[0].y; +MAD result.color.z, R0, c[0].w, R0.x; +TEX R0.y, fragment.texcoord[0], texture[1], 2D; +ADD R0.y, R0, -c[1].x; +MAD R0.z, -R0, c[1].y, R0.x; +MAD result.color.y, -R0, c[1].z, R0.z; +MAD result.color.x, R0.y, c[1].w, R0; +MOV result.color.w, c[0].x; +END +# 12 instructions, 1 R-regs diff --git a/src/shaders/pso2h.py b/src/shaders/pso2h.py new file mode 100755 index 0000000..4f30160 --- /dev/null +++ b/src/shaders/pso2h.py @@ -0,0 +1,128 @@ +#!/usr/bin/python +"""pso2h + +A small python script to generated header files from compiled ARBfp1.0 shaders + +Usage: ./pso2h-runtime [options] file.pso + +Options: + -o, --output header file name + -n, --name name of the shader + -h, --help display this help + -v, --verbose verbose output + + file.pso compiled shader""" + +import sys, os, string, getopt + +__author__ = "Damien Lespiau <damien.lespiau@intel.com>" +__version__ = "0.1" +__date__ = "20090426" +__copyright__ = "Copyright (c) 2009 Intel Corporation" +__license__ = "GPL v2" + +_verbose = 0 + +_template = """/* + * This file was generated by pso2h. + */ + +#ifndef %s +#define %s + +/* + * This define is the size of the shader in bytes. More precisely it's the + * sum of strlen() of every string in the array. + */ +#define %s %d + +static const char *%s[] = +{ +%s NULL +}; + +#endif /* %s */ +""" + +def define(format, filename): + path, file = os.path.split(filename) + return format % string.upper(file.replace('.', '_').capitalize()) + +class PSO: + def __init__(self, filename=None, name=None): + self.filename = filename + self.name = name + + def write_header(self, filename): + file = open(self.filename) + header = open(filename, "w") + __HEADER__ = define("SHADERS_%s", filename) + SIZE = define("%s_SZ", self.name) + body = "" + size = 0; + + for line in file: + # skip comments + if line.startswith('#'): + continue + line = string.strip(line) + line += '\\n' + size += len(line) - 1; + body += " \"%s\",\n" % line + + header.write(_template % (__HEADER__, + __HEADER__, + SIZE, + size, + self.name, + body, + __HEADER__)) + +def usage(): + print __doc__ + +def main(argv): + opt_shader = None + opt_header = None + opt_name = None + try: + opts, args = getopt.getopt(argv, "hvo:n:", \ + ["help", "verbose", "--ouput=", "--name="]) + except getopt.GetoptError: + usage() + sys.exit(1) + for opt, arg in opts: + if opt in ("-h", "--help"): + usage() + sys.exit() + elif opt in ("-v", "--verbose"): + global _verbose + _verbose = 1 + elif opt in ("-o", "-output"): + opt_header = arg + elif opt in ("-n", "-name"): + opt_name = arg + if args: + opt_shader = "".join(args) + + #input validation + if not opt_shader: + print "error: you must supply a shader file.\n" + usage() + sys.exit(1) + if not os.access(opt_shader, os.F_OK): + print opt_shader + ": file not found" + sys.exit(1) + file, ext = os.path.splitext(opt_shader) + if not opt_header: + opt_header = file + ".h" + if not opt_name: + path, file = os.path.split(file) + opt_name = file + + pso = PSO(opt_shader, opt_name) + pso.write_header(opt_header) + +if __name__ == "__main__": + main(sys.argv[1:]) + diff --git a/src/sysdeps.h b/src/sysdeps.h new file mode 100644 index 0000000..2031f31 --- /dev/null +++ b/src/sysdeps.h @@ -0,0 +1,68 @@ +/* + * sysdeps.h - System dependent definitions + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef SYSDEPS_H +#define SYSDEPS_H + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <assert.h> + +// Helper macros +#undef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#undef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#undef ARRAY_ELEMS +#define ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0])) +#define UINT_TO_POINTER(i) ((void *)(uintptr_t)(i)) +#define POINTER_TO_UINT(p) ((uintptr_t)(void *)(p)) + +#ifdef HAVE_VISIBILITY_ATTRIBUTE +# define attribute_hidden __attribute__((__visibility__("hidden"))) +#else +# define attribute_hidden +#endif + +#undef ASSERT +#if USE_DEBUG +# define ASSERT assert +#else +# define ASSERT(expr) do { \ + if (!(expr)) { \ + xvba_error_message("Assertion failed in file %s at line %d\n", \ + __FILE__, __LINE__); \ + abort(); \ + } \ +} while (0) +#endif + +/* Check for a specific version of XvBA, or newer */ +#ifndef XVBA_CHECK_VERSION +#define XVBA_CHECK_VERSION(major, minor) \ + (XVBA_VERSION_MAJOR > (major) || \ + (XVBA_VERSION_MAJOR == (major) && XVBA_VERSION_MINOR >= (minor))) +#endif + +#endif /* SYSDEPS_H */ diff --git a/src/uarray.c b/src/uarray.c new file mode 100644 index 0000000..d2232cd --- /dev/null +++ b/src/uarray.c @@ -0,0 +1,144 @@ +/* + * uarray.c - Array utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "uarray.h" + +typedef struct _UArrayPrivate UArrayPrivate; +struct _UArrayPrivate { + UArray array; + unsigned int count_max; + unsigned int element_size; +}; + +#define elem_offset(p,i) ((char *)(p)->array.data + (i) * (p)->element_size) +#define elem_size(p,n) ((n) * (p)->element_size) + +UArray *array_new(unsigned int element_size) +{ + UArrayPrivate *priv = malloc(sizeof(*priv)); + if (!priv) + return NULL; + + priv->array.data = NULL; + priv->array.count = 0; + priv->count_max = 0; + priv->element_size = element_size; + return &priv->array; +} + +void array_free(UArray *array) +{ + if (!array) + return; + + free(array->data); + free(array); +} + +UArray *array_resize(UArray *array, unsigned int num_elements) +{ + UArrayPrivate * const priv = (UArrayPrivate *)array; + void *data; + + if (num_elements <= priv->count_max) + return array; + + num_elements += 4; + data = realloc(array->data, elem_size(priv, num_elements)); + if (!data) + return NULL; + + memset( + elem_offset(priv, priv->count_max), + 0, + elem_size(priv, (num_elements - priv->count_max)) + ); + + array->data = data; + priv->count_max = num_elements; + return array; +} + +UArray *array_append(UArray *array, const void *data) +{ + UArrayPrivate * const priv = (UArrayPrivate *)array; + + if (!array_resize(array, array->count + 1)) + return NULL; + + memcpy(elem_offset(priv, array->count), data, elem_size(priv, 1)); + ++array->count; + return array; +} + +int array_lookup(UArray *array, const void *data, UArrayCompareFunc compare) +{ + UArrayPrivate * const priv = (UArrayPrivate *)array; + char *m; + unsigned int i; + + if (!array) + return -1; + + m = array->data; + if (compare) { + for (i = 0; i < array->count; i++, m += priv->element_size) + if (compare(m, data)) + return i; + } + else { + for (i = 0; i < array->count; i++, m += priv->element_size) + if (memcmp(m, data, elem_size(priv, 1)) == 0) + return i; + } + return -1; +} + +UArray *array_remove_at(UArray *array, int index) +{ + UArrayPrivate * const priv = (UArrayPrivate *)array; + + if (!array) + return NULL; + if (index < 0 || index >= array->count) + return NULL; + + memcpy( + elem_offset(priv, index), + elem_offset(priv, array->count - 1), + elem_size(priv, 1) + ); + --array->count; + return array; +} + +UArray *array_replace_at(UArray *array, int index, const void *data) +{ + UArrayPrivate * const priv = (UArrayPrivate *)array; + + if (!array) + return NULL; + if (index < 0 || index >= array->count) + return NULL; + + memcpy(elem_offset(priv, index), data, elem_size(priv, 1)); + return array; +} diff --git a/src/uarray.h b/src/uarray.h new file mode 100644 index 0000000..bcc3a63 --- /dev/null +++ b/src/uarray.h @@ -0,0 +1,59 @@ +/* + * uarray.h - Array utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef UARRAY_H +#define UARRAY_H + +typedef struct _UArray UArray; +struct _UArray { + void *data; + unsigned int count; +}; + +typedef int (*UArrayCompareFunc)(const void *a, const void *b); + +UArray *array_new(unsigned int element_size) + attribute_hidden; + +void array_free(UArray *array) + attribute_hidden; + +UArray *array_resize(UArray *array, unsigned int num_elements) + attribute_hidden; + +UArray *array_append(UArray *array, const void *data) + attribute_hidden; + +#define array_append_val(array, val) \ + array_append(array, &(val)) + +int array_lookup(UArray *array, const void *data, UArrayCompareFunc compare) + attribute_hidden; + +#define array_index(array, type, index) \ + *(type *)((char *)(array)->data + sizeof(type) * (index)) + +UArray *array_remove_at(UArray *array, int index) + attribute_hidden; + +UArray *array_replace_at(UArray *array, int index, const void *data) + attribute_hidden; + +#endif /* UARRAY_H */ diff --git a/src/uasyncqueue.c b/src/uasyncqueue.c new file mode 100644 index 0000000..8d687a2 --- /dev/null +++ b/src/uasyncqueue.c @@ -0,0 +1,333 @@ +/* + * uasyncqueue.c - Asynchronous queue utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "uasyncqueue.h" +#include "uqueue.h" +#include <pthread.h> + +struct _UAsyncQueue { + UQueue *queue; + pthread_mutex_t mutex; + pthread_cond_t cond; + unsigned int is_waiting; +}; + +UAsyncQueue *async_queue_new(void) +{ + UAsyncQueue *queue = malloc(sizeof(*queue)); + + if (!queue) + return NULL; + + queue->queue = queue_new(); + if (!queue->queue) + goto error; + + if (pthread_cond_init(&queue->cond, NULL) != 0) + goto error; + + pthread_mutex_init(&queue->mutex, NULL); + queue->is_waiting = 0; + return queue; + +error: + async_queue_free(queue); + return NULL; +} + +void async_queue_free(UAsyncQueue *queue) +{ + if (!queue) + return; + + pthread_mutex_unlock(&queue->mutex); + queue_free(queue->queue); + free(queue); +} + +int async_queue_is_empty(UAsyncQueue *queue) +{ + return queue && queue_is_empty(queue->queue); +} + +static UAsyncQueue *async_queue_push_unlocked(UAsyncQueue *queue, void *data) +{ + queue_push(queue->queue, data); + if (queue->is_waiting) + pthread_cond_signal(&queue->cond); + return queue; +} + +UAsyncQueue *async_queue_push(UAsyncQueue *queue, void *data) +{ + if (!queue) + return NULL; + + pthread_mutex_lock(&queue->mutex); + async_queue_push_unlocked(queue, data); + pthread_mutex_unlock(&queue->mutex); + return queue; +} + +static void * +async_queue_timed_pop_unlocked(UAsyncQueue *queue, uint64_t end_time) +{ + if (queue_is_empty(queue->queue)) { + assert(!queue->is_waiting); + ++queue->is_waiting; + if (!end_time) + pthread_cond_wait(&queue->cond, &queue->mutex); + else { + struct timespec timeout; + timeout.tv_sec = end_time / 1000000; + timeout.tv_nsec = 1000 * (end_time % 1000000); + pthread_cond_timedwait(&queue->cond, &queue->mutex, &timeout); + } + --queue->is_waiting; + if (queue_is_empty(queue->queue)) + return NULL; + } + return queue_pop(queue->queue); +} + +void *async_queue_timed_pop(UAsyncQueue *queue, uint64_t end_time) +{ + void *data; + + if (!queue) + return NULL; + + pthread_mutex_lock(&queue->mutex); + data = async_queue_timed_pop_unlocked(queue, end_time); + pthread_mutex_unlock(&queue->mutex); + return data; +} + +#ifdef TEST_ASYNC_QUEUE +#include <stdarg.h> + +enum { + CMD_QUIT, + CMD_TIME, + CMD_ADD_1, + CMD_ADD_2, + CMD_ADD_3, +}; + +typedef enum { + MSG_INVOKE, + MSG_REPLY +} MessageType; + +#define MSG_ARG(v) ((uint64_t)(v)) + +typedef struct { + MessageType type; + unsigned int num_args; + uint64_t args[1]; +} Message; + +static Message *msg_new(MessageType type, unsigned int num_args) +{ + Message *msg; + + msg = malloc(sizeof(*msg) + num_args * sizeof(msg->args[0])); + if (msg) { + msg->type = type; + msg->num_args = num_args; + } + return msg; +} + +static void msg_free(Message *msg) +{ + free(msg); +} + +static int msg_invoke(UAsyncQueue *queue, int cmd, unsigned int num_args, ...) +{ + Message *msg; + va_list args; + unsigned int i; + + msg = msg_new(MSG_INVOKE, 1 + num_args); + if (!msg) + return 0; + + msg->args[0] = cmd; + va_start(args, num_args); + for (i = 0; i < num_args; i++) + msg->args[1 + i] = va_arg(args, uint64_t); + va_end(args); + + if (!async_queue_push(queue, msg)) { + msg_free(msg); + return 0; + } + return 1; +} + +static int msg_wait_for_reply(UAsyncQueue *queue) +{ + Message *msg; + int ret; + + msg = async_queue_pop(queue); + if (!msg || msg->type != MSG_REPLY) + return -1; + + ret = msg->args[0]; + free(msg); + return ret; +} + +typedef struct { + UAsyncQueue *send_queue; + UAsyncQueue *recv_queue; +} ConsumerThreadArgs; + +static void *consumer(void *arg) +{ + ConsumerThreadArgs * const args = arg; + Message *msg; + const char *msg_name; + unsigned int i, ret, stop = 0; + uint64_t end_time = 0; + + while (!stop) { + if (end_time) { + msg = async_queue_timed_pop(args->recv_queue, end_time); + if (!msg) + printf("<timed out>\n"); + } + msg = async_queue_pop(args->recv_queue); + if (!msg || msg->type != MSG_INVOKE) + abort(); + + switch (msg->args[0]) { + case CMD_QUIT: msg_name = "quit"; break; + case CMD_TIME: msg_name = "time"; break; + case CMD_ADD_1: msg_name = "add1"; break; + case CMD_ADD_2: msg_name = "add2"; break; + case CMD_ADD_3: msg_name = "add3"; break; + default: msg_name = NULL; break; + } + if (!msg_name) + abort(); + + printf("recv %s", msg_name); + if (msg->num_args > 1) { + printf(": %d args:", msg->num_args - 1); + for (i = 1; i < msg->num_args; i++) + printf(" %d", (int)msg->args[i]); + } + printf("\n"); + + ret = 0; + switch (msg->args[0]) { + case CMD_QUIT: + stop = 1; + break; + case CMD_TIME: + end_time = msg->args[1]; + break; + case CMD_ADD_1: + case CMD_ADD_2: + case CMD_ADD_3: + for (i = 1; i < msg->num_args; i++) + ret += msg->args[i]; + end_time = 0; + break; + } + msg_free(msg); + + msg = msg_new(MSG_REPLY, 1); + if (!msg) + abort(); + msg->args[0] = ret; + if (!async_queue_push(args->send_queue, msg)) + abort(); + } + return NULL; +} + +int main(void) +{ + struct timespec now; + uint64_t end_time; + pthread_t consumer_thread; + ConsumerThreadArgs consumer_args; + UAsyncQueue *send_queue; + UAsyncQueue *recv_queue; + Message *msg; + + send_queue = async_queue_new(); + if (!send_queue) + abort(); + + recv_queue = async_queue_new(); + if (!recv_queue) + abort(); + + consumer_args.send_queue = recv_queue; + consumer_args.recv_queue = send_queue; + if (pthread_create(&consumer_thread, NULL, consumer, &consumer_args) != 0) + abort(); + + sleep(1); + if (!msg_invoke(send_queue, CMD_ADD_1, 1, MSG_ARG(1))) + abort(); + if (msg_wait_for_reply(recv_queue) != 1) + abort(); + + sleep(1); + if (!msg_invoke(send_queue, CMD_ADD_2, 2, MSG_ARG(1), MSG_ARG(2))) + abort(); + if (msg_wait_for_reply(recv_queue) != 3) + abort(); + + clock_gettime(CLOCK_REALTIME, &now); + end_time = (1 + now.tv_sec) * 1000000 + (now.tv_nsec / 1000); + + sleep(1); + if (!msg_invoke(send_queue, CMD_TIME, 1, MSG_ARG(end_time))) + abort(); + if (msg_wait_for_reply(recv_queue) != 0) + abort(); + + sleep(1); + if (!msg_invoke(send_queue, CMD_ADD_3, 3, MSG_ARG(1), MSG_ARG(2), MSG_ARG(3))) + abort(); + if (msg_wait_for_reply(recv_queue) != 6) + abort(); + + sleep(1); + if (!msg_invoke(send_queue, CMD_QUIT, 0)) + abort(); + if (msg_wait_for_reply(recv_queue) != 0) + abort(); + + pthread_join(consumer_thread, NULL); + async_queue_free(recv_queue); + async_queue_free(send_queue); + return 0; +} +#endif diff --git a/src/uasyncqueue.h b/src/uasyncqueue.h new file mode 100644 index 0000000..adc5a81 --- /dev/null +++ b/src/uasyncqueue.h @@ -0,0 +1,44 @@ +/* + * uasyncqueue.h - Asynchronous queue utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef UASYNCQUEUE_H +#define UASYNCQUEUE_H + +typedef struct _UAsyncQueue UAsyncQueue; + +UAsyncQueue *async_queue_new(void) + attribute_hidden; + +void async_queue_free(UAsyncQueue *queue) + attribute_hidden; + +int async_queue_is_empty(UAsyncQueue *queue) + attribute_hidden; + +UAsyncQueue *async_queue_push(UAsyncQueue *queue, void *data) + attribute_hidden; + +void *async_queue_timed_pop(UAsyncQueue *queue, uint64_t end_time) + attribute_hidden; + +#define async_queue_pop(queue) \ + async_queue_timed_pop(queue, 0) + +#endif /* UASYNCQUEUE_H */ diff --git a/src/ulist.c b/src/ulist.c new file mode 100644 index 0000000..4acbd24 --- /dev/null +++ b/src/ulist.c @@ -0,0 +1,166 @@ +/* + * ulist.c - List utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "ulist.h" + +static UList *list_new(void *data, UList *prev, UList *next) +{ + UList *list = malloc(sizeof(*list)); + + if (list) { + list->data = data; + list->prev = prev; + list->next = next; + if (prev) + prev->next = list; + if (next) + next->prev = list; + } + return list; +} + +void list_free_1(UList *list) +{ + free(list); +} + +void list_free(UList *list) +{ + while (list) { + UList * const next = list->next; + list_free_1(list); + list = next; + } +} + +UList *list_append(UList *list, void *data) +{ + UList * const node = list_new(data, list_last(list), NULL); + + return list ? list : node; +} + +UList *list_prepend(UList *list, void *data) +{ + return list_new(data, list ? list->prev : NULL, list); +} + +UList *list_reverse(UList *list) +{ + UList *last = NULL; + + while (list) { + last = list; + list = last->next; + last->next = last->prev; + last->prev = list; + } + return last; +} + +UList *list_first(UList *list) +{ + if (list) { + while (list->prev) + list = list->prev; + } + return list; +} + +UList *list_last(UList *list) +{ + if (list) { + while (list->next) + list = list->next; + } + return list; +} + +unsigned int list_size(UList *list) +{ + unsigned int size = 0; + + while (list) { + ++size; + list = list->next; + } + return size; +} + +UList *list_lookup_full(UList *list, const void *data, UListCompareFunc compare) +{ + if (!list) + return NULL; + + if (compare) { + for (; list; list = list->next) + if (compare(list->data, data)) + return list; + } + else { + for (; list; list = list->next) + if (list->data == data) + return list; + } + return NULL; +} + +#ifdef TEST_LIST +int main(void) +{ + UList *temp, *list = NULL; + + list = list_append(list, UINT_TO_POINTER(1)); + + temp = list_append(list, UINT_TO_POINTER(2)); + if (temp != list) + abort(); + + temp = list_append(list, UINT_TO_POINTER(3)); + if (temp != list) + abort(); + + list = list_prepend(list, UINT_TO_POINTER(0)); + if (list == temp) + abort(); + + if (list_size(list) != 4) + abort(); + + if (list_first(list_last(list)) != list) + abort(); + + if (POINTER_TO_UINT(list_last(list)->data) != 3) + abort(); + + if (POINTER_TO_UINT(list_first(temp)->data) != 0) + abort(); + + temp = list_lookup(list, UINT_TO_POINTER(2)); + if (!temp) + abort(); + if (list_size(temp) != 2) + abort(); + + list_free(list); + return 0; +} +#endif diff --git a/src/ulist.h b/src/ulist.h new file mode 100644 index 0000000..0fc3056 --- /dev/null +++ b/src/ulist.h @@ -0,0 +1,63 @@ +/* + * ulist.h - List utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef ULIST_H +#define ULIST_H + +typedef struct _UList UList; +struct _UList { + void *data; + UList *prev; + UList *next; +}; + +typedef int (*UListCompareFunc)(const void *a, const void *b); + +void list_free_1(UList *list) + attribute_hidden; + +void list_free(UList *list) + attribute_hidden; + +UList *list_append(UList *list, void *data) + attribute_hidden; + +UList *list_prepend(UList *list, void *data) + attribute_hidden; + +UList *list_reverse(UList *list) + attribute_hidden; + +UList *list_first(UList *list) + attribute_hidden; + +UList *list_last(UList *list) + attribute_hidden; + +unsigned int list_size(UList *list) + attribute_hidden; + +UList *list_lookup_full(UList *list, const void *data, UListCompareFunc compare) + attribute_hidden; + +#define list_lookup(list, data) \ + list_lookup_full(list, data, NULL) + +#endif /* ULIST_H */ diff --git a/src/uqueue.c b/src/uqueue.c new file mode 100644 index 0000000..ef28e46 --- /dev/null +++ b/src/uqueue.c @@ -0,0 +1,107 @@ +/* + * uqueue.c - Queue utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "uqueue.h" +#include "ulist.h" + +struct _UQueue { + UList *head; + UList *tail; + unsigned int size; +}; + +UQueue *queue_new(void) +{ + return calloc(1, sizeof(UQueue)); +} + +void queue_free(UQueue *queue) +{ + if (!queue) + return; + + list_free(queue->head); + free(queue); +} + +int queue_is_empty(UQueue *queue) +{ + return !queue || queue->size == 0; +} + +UQueue *queue_push(UQueue *queue, void *data) +{ + if (!queue) + return NULL; + + queue->tail = list_last(list_append(queue->tail, data)); + + if (!queue->head) + queue->head = queue->tail; + + ++queue->size; + return queue; +} + +void *queue_pop(UQueue *queue) +{ + UList *list; + void *data; + + if (!queue || !queue->head) + return NULL; + + list = queue->head; + data = list->data; + queue->head = list->next; + if (--queue->size == 0) + queue->tail = NULL; + list_free_1(list); + return data; +} + +#ifdef TEST_QUEUE +int main(void) +{ + UQueue *queue = queue_new(); + + if (!queue_push(queue, UINT_TO_POINTER(1))) + abort(); + if (!queue_push(queue, UINT_TO_POINTER(2))) + abort(); + if (queue_is_empty(queue)) + abort(); + + if (POINTER_TO_UINT(queue_pop(queue)) != 1) + abort(); + if (POINTER_TO_UINT(queue_pop(queue)) != 2) + abort(); + if (!queue_is_empty(queue)) + abort(); + + if (!queue_push(queue, UINT_TO_POINTER(3))) + abort(); + if (POINTER_TO_UINT(queue_pop(queue)) != 3) + abort(); + + queue_free(queue); +} +#endif diff --git a/src/uqueue.h b/src/uqueue.h new file mode 100644 index 0000000..6594e7b --- /dev/null +++ b/src/uqueue.h @@ -0,0 +1,41 @@ +/* + * uqueue.h - Queue utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef UQUEUE_H +#define UQUEUE_H + +typedef struct _UQueue UQueue; + +UQueue *queue_new(void) + attribute_hidden; + +void queue_free(UQueue *queue) + attribute_hidden; + +int queue_is_empty(UQueue *queue) + attribute_hidden; + +UQueue *queue_push(UQueue *queue, void *data) + attribute_hidden; + +void *queue_pop(UQueue *queue) + attribute_hidden; + +#endif /* UQUEUE_H */ diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..4bc0835 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,221 @@ +/* + * utils.h - Utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "utils.h" +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <errno.h> +#include <X11/Xutil.h> + +#define DEBUG 1 +#include "debug.h" + + +// Parses ENV environment variable for an integer value +int getenv_int(const char *env, int *pval) +{ + const char *env_str = getenv(env); + if (!env_str) + return -1; + + char *end = NULL; + long val = strtoul(env_str, &end, 10); + if (end == NULL || end[0] != '\0') + return -1; + + if (pval) + *pval = val; + return 0; +} + +// Parses ENV environment variable expecting "yes" | "no" values +int getenv_yesno(const char *env, int *pval) +{ + const char *env_str = getenv(env); + if (!env_str) + return -1; + + int val; + if (strcmp(env_str, "1") == 0 || strcmp(env_str, "yes") == 0) + val = 1; + else if (strcmp(env_str, "0") == 0 || strcmp(env_str, "no") == 0) + val = 0; + else + return -1; + + if (pval) + *pval = val; + return 0; +} + +// Get current value of microsecond timer +uint64_t get_ticks_usec(void) +{ +#ifdef HAVE_CLOCK_GETTIME + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return (uint64_t)t.tv_sec * 1000000 + t.tv_nsec / 1000; +#else + struct timeval t; + gettimeofday(&t, NULL); + return (uint64_t)t.tv_sec * 1000000 + t.tv_usec; +#endif +} + +#if defined(__linux__) +// Linux select() changes its timeout parameter upon return to contain +// the remaining time. Most other unixen leave it unchanged or undefined. +#define SELECT_SETS_REMAINING +#elif defined(__FreeBSD__) || defined(__sun__) || (defined(__MACH__) && defined(__APPLE__)) +#define USE_NANOSLEEP +#elif defined(HAVE_PTHREADS) && defined(sgi) +// SGI pthreads has a bug when using pthreads+signals+nanosleep, +// so instead of using nanosleep, wait on a CV which is never signalled. +#include <pthread.h> +#define USE_COND_TIMEDWAIT +#endif + +// Wait for the specified amount of microseconds +void delay_usec(unsigned int usec) +{ + int was_error; + +#if defined(USE_NANOSLEEP) + struct timespec elapsed, tv; +#elif defined(USE_COND_TIMEDWAIT) + // Use a local mutex and cv, so threads remain independent + pthread_cond_t delay_cond = PTHREAD_COND_INITIALIZER; + pthread_mutex_t delay_mutex = PTHREAD_MUTEX_INITIALIZER; + struct timespec elapsed; + uint64_t future; +#else + struct timeval tv; +#ifndef SELECT_SETS_REMAINING + uint64_t then, now, elapsed; +#endif +#endif + + // Set the timeout interval - Linux only needs to do this once +#if defined(SELECT_SETS_REMAINING) + tv.tv_sec = 0; + tv.tv_usec = usec; +#elif defined(USE_NANOSLEEP) + elapsed.tv_sec = 0; + elapsed.tv_nsec = usec * 1000; +#elif defined(USE_COND_TIMEDWAIT) + future = get_ticks_usec() + usec; + elapsed.tv_sec = future / 1000000; + elapsed.tv_nsec = (future % 1000000) * 1000; +#else + then = get_ticks_usec(); +#endif + + do { + errno = 0; +#if defined(USE_NANOSLEEP) + tv.tv_sec = elapsed.tv_sec; + tv.tv_nsec = elapsed.tv_nsec; + was_error = nanosleep(&tv, &elapsed); +#elif defined(USE_COND_TIMEDWAIT) + was_error = pthread_mutex_lock(&delay_mutex); + was_error = pthread_cond_timedwait(&delay_cond, &delay_mutex, &elapsed); + was_error = pthread_mutex_unlock(&delay_mutex); +#else +#ifndef SELECT_SETS_REMAINING + // Calculate the time interval left (in case of interrupt) + now = get_ticks_usec(); + elapsed = now - then; + then = now; + if (elapsed >= usec) + break; + usec -= elapsed; + tv.tv_sec = 0; + tv.tv_usec = usec; +#endif + was_error = select(0, NULL, NULL, NULL, &tv); +#endif + } while (was_error && (errno == EINTR)); +} + +// Reallocates a BUFFER to NUM_ELEMENTS of ELEMENT_SIZE bytes +void * +realloc_buffer( + void **buffer_p, + unsigned int *max_elements_p, + unsigned int num_elements, + unsigned int element_size +) +{ + if (!buffer_p || !max_elements_p) + return NULL; + + void *buffer = *buffer_p; + if (*max_elements_p > num_elements) + return buffer; + + num_elements += 4; + if ((buffer = realloc(buffer, num_elements * element_size)) == NULL) { + free(*buffer_p); + *buffer_p = NULL; + return NULL; + } + memset((uint8_t *)buffer + *max_elements_p * element_size, 0, + (num_elements - *max_elements_p) * element_size); + + *buffer_p = buffer; + *max_elements_p = num_elements; + return buffer; +} + +// Convert string array to char array +// NOTE: dst char array shall be large enough to contain src strings +void string_array_to_char_array(char *dst, const char **src) +{ + int i, n; + + for (i = 0; src[i]; i++) { + n = strlen(src[i]); + memcpy(dst, src[i], n); + dst += n; + } + *dst = '\0'; +} + +// Lookup for substring NAME in string EXT using SEP as separators +int find_string(const char *name, const char *ext, const char *sep) +{ + const char *end; + int name_len, n; + + if (name == NULL || ext == NULL) + return 0; + + end = ext + strlen(ext); + name_len = strlen(name); + while (ext < end) { + n = strcspn(ext, sep); + if (n == name_len && strncmp(name, ext, n) == 0) + return 1; + ext += (n + 1); + } + return 0; +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..f0a4b9c --- /dev/null +++ b/src/utils.h @@ -0,0 +1,50 @@ +/* + * utils.h - Utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef UTILS_H +#define UTILS_H + +int getenv_int(const char *env, int *pval) + attribute_hidden; + +int getenv_yesno(const char *env, int *pval) + attribute_hidden; + +uint64_t get_ticks_usec(void) + attribute_hidden; + +void delay_usec(unsigned int usec) + attribute_hidden; + +void * +realloc_buffer( + void **buffer_p, + unsigned int *max_elements_p, + unsigned int num_elements, + unsigned int element_size +) attribute_hidden; + +void string_array_to_char_array(char *dst, const char **src) + attribute_hidden; + +int find_string(const char *name, const char *ext, const char *sep) + attribute_hidden; + +#endif /* UTILS_H */ diff --git a/src/utils_glx.c b/src/utils_glx.c new file mode 100644 index 0000000..28273ce --- /dev/null +++ b/src/utils_glx.c @@ -0,0 +1,1231 @@ +/* + * utils_glx.c - GLX utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define _GNU_SOURCE 1 /* RTLD_DEFAULT */ +#include "sysdeps.h" +#include <string.h> +#include <math.h> +#include <dlfcn.h> +#include <pthread.h> +#include "utils.h" +#include "utils_glx.h" +#include "utils_x11.h" + +#define DEBUG 1 +#include "debug.h" + +/** + * gl_get_error_string: + * @error: an OpenGL error enumeration + * + * Retrieves the string representation the OpenGL @error. + * + * Return error: the static string representing the OpenGL @error + */ +const char * +gl_get_error_string(GLenum error) +{ + static const struct { + GLenum val; + const char *str; + } + gl_errors[] = { + { GL_NO_ERROR, "no error" }, + { GL_INVALID_ENUM, "invalid enumerant" }, + { GL_INVALID_VALUE, "invalid value" }, + { GL_INVALID_OPERATION, "invalid operation" }, + { GL_STACK_OVERFLOW, "stack overflow" }, + { GL_STACK_UNDERFLOW, "stack underflow" }, + { GL_OUT_OF_MEMORY, "out of memory" }, +#ifdef GL_INVALID_FRAMEBUFFER_OPERATION_EXT + { GL_INVALID_FRAMEBUFFER_OPERATION_EXT, "invalid framebuffer operation" }, +#endif + { ~0, NULL } + }; + + unsigned int i; + for (i = 0; gl_errors[i].str; i++) { + if (gl_errors[i].val == error) + return gl_errors[i].str; + } + return "unknown"; +} + +/** + * gl_purge_errors: + * + * Purges all OpenGL errors. This function is generally useful to + * clear up the pending errors prior to calling gl_check_error(). + */ +void +gl_purge_errors(void) +{ + while (glGetError() != GL_NO_ERROR) + ; /* nothing */ +} + +/** + * gl_check_error: + * + * Checks whether there is any OpenGL error pending. + * + * Return value: 1 if an error was encountered + */ +int +gl_check_error(void) +{ + GLenum error; + int has_errors = 0; + + while ((error = glGetError()) != GL_NO_ERROR) { + D(bug("glError: %s caught", gl_get_error_string(error))); + has_errors = 1; + } + return has_errors; +} + +/** + * gl_get_param: + * @param: the parameter name + * @pval: return location for the value + * + * This function is a wrapper around glGetIntegerv() that does extra + * error checking. + * + * Return value: 1 on success + */ +int +gl_get_param(GLenum param, unsigned int *pval) +{ + GLint val; + + gl_purge_errors(); + glGetIntegerv(param, &val); + if (gl_check_error()) + return 0; + + if (pval) + *pval = val; + return 1; +} + +/** + * gl_get_texture_param: + * @target: the target to which the texture is bound + * @param: the parameter name + * @pval: return location for the value + * + * This function is a wrapper around glGetTexLevelParameteriv() that + * does extra error checking. + * + * Return value: 1 on success + */ +int +gl_get_texture_param(GLenum target, GLenum param, unsigned int *pval) +{ + GLint val; + + gl_purge_errors(); + glGetTexLevelParameteriv(target, 0, param, &val); + if (gl_check_error()) + return 0; + + if (pval) + *pval = val; + return 1; +} + +/** + * gl_set_bgcolor: + * @color: the requested RGB color + * + * Sets background color to the RGB @color. This basically is a + * wrapper around glClearColor(). + */ +void +gl_set_bgcolor(uint32_t color) +{ + glClearColor( + ((color >> 16) & 0xff) / 255.0f, + ((color >> 8) & 0xff) / 255.0f, + ( color & 0xff) / 255.0f, + 1.0f + ); +} + +/** + * gl_set_texture_scaling: + * @target: the target to which the texture is currently bound + * @scale: scaling algorithm + * + * Sets scaling algorithm used for the texture currently bound. + */ +void +gl_set_texture_scaling(GLenum target, GLenum scale) +{ + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, scale); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, scale); +} + +/** + * gl_set_texture_wrapping: + * @target: the target to which the texture is currently bound + * @wrap: wrapping mode + * + * Sets wrapping mode used for the texture currently bound. + */ +void +gl_set_texture_wrapping(GLenum target, GLenum wrap) +{ + glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap); + glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap); +} + +/** + * gl_perspective: + * @fovy: the field of view angle, in degrees, in the y direction + * @aspect: the aspect ratio that determines the field of view in the + * x direction. The aspect ratio is the ratio of x (width) to y + * (height) + * @zNear: the distance from the viewer to the near clipping plane + * (always positive) + * @zFar: the distance from the viewer to the far clipping plane + * (always positive) + * + * Specified a viewing frustum into the world coordinate system. This + * basically is the Mesa implementation of gluPerspective(). + */ +static void +frustum(GLdouble left, GLdouble right, + GLdouble bottom, GLdouble top, + GLdouble nearval, GLdouble farval) +{ + GLdouble x, y, a, b, c, d; + GLdouble m[16]; + + x = (2.0 * nearval) / (right - left); + y = (2.0 * nearval) / (top - bottom); + a = (right + left) / (right - left); + b = (top + bottom) / (top - bottom); + c = -(farval + nearval) / ( farval - nearval); + d = -(2.0 * farval * nearval) / (farval - nearval); + +#define M(row,col) m[col*4+row] + M(0,0) = x; M(0,1) = 0.0F; M(0,2) = a; M(0,3) = 0.0F; + M(1,0) = 0.0F; M(1,1) = y; M(1,2) = b; M(1,3) = 0.0F; + M(2,0) = 0.0F; M(2,1) = 0.0F; M(2,2) = c; M(2,3) = d; + M(3,0) = 0.0F; M(3,1) = 0.0F; M(3,2) = -1.0F; M(3,3) = 0.0F; +#undef M + + glMultMatrixd(m); +} + +static void +gl_perspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar) +{ + GLdouble xmin, xmax, ymin, ymax; + + ymax = zNear * tan(fovy * M_PI / 360.0); + ymin = -ymax; + xmin = ymin * aspect; + xmax = ymax * aspect; + + /* Don't call glFrustum() because of error semantics (covglu) */ + frustum(xmin, xmax, ymin, ymax, zNear, zFar); +} + +/** + * gl_resize: + * @width: the requested width, in pixels + * @height: the requested height, in pixels + * + * Resizes the OpenGL viewport to the specified dimensions, using an + * orthogonal projection. (0,0) represents the top-left corner of the + * window. + */ +void +gl_resize(unsigned int width, unsigned int height) +{ +#if 0 +#define FOVY 60.0f +#define ASPECT 1.0f +#define Z_NEAR 0.1f +#define Z_FAR 100.0f +#define Z_CAMERA 0.869f + + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gl_perspective(FOVY, ASPECT, Z_NEAR, Z_FAR); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glTranslatef(-0.5f, -0.5f, -Z_CAMERA); + glScalef(1.0f/width, -1.0f/height, 1.0f/width); + glTranslatef(0.0f, -1.0f*height, 0.0f); +#else + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, width, height, 0, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +#endif +} + +/** + * gl_set_current_context_cache: + * @is_enabled: flag to enable the current context cache + * + * Toggles use of the (TLS-based) current context cache. + * + * The cache is important in multi-threaded environments since + * glXGetCurrent*() functions can take up to 76 ms [fglrx 8.77.x] to + * complete!! However, this cannot be used in standard VA/GLX + * situations because foreign changes (from the user program) can + * occur without us knowing about. + */ +static int gl_current_context_cache = 0; +static __thread Display *gl_current_display; +static __thread Window gl_current_window; +static __thread GLXContext gl_current_context; + +void +gl_set_current_context_cache(int is_enabled) +{ + gl_current_context_cache = is_enabled; +} + +static Bool +gl_make_current(Display *dpy, Window win, GLXContext ctx) +{ + Bool ret; + + if (gl_current_context_cache && + gl_current_display == dpy && + gl_current_window == win && + gl_current_context == ctx) + return True; + + ret = glXMakeCurrent(dpy, win, ctx); + + if (ret && gl_current_context_cache) { + gl_current_display = dpy; + gl_current_window = win; + gl_current_context = ctx; + } + return ret; +} + +/** + * gl_create_context: + * @dpy: an X11 #Display + * @screen: the associated screen of @dpy + * @parent: the parent #GLContextState, or %NULL if none is to be used + * + * Creates a GLX context sharing textures and displays lists with + * @parent, if not %NULL. + * + * Return value: the newly created GLX context + */ +GLContextState * +gl_create_context(Display *dpy, int screen, GLContextState *parent) +{ + GLContextState *cs; + GLXFBConfig *fbconfigs = NULL; + int fbconfig_id, val, n, n_fbconfigs; + Status status; + + static GLint fbconfig_attrs[] = { + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DOUBLEBUFFER, True, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + None + }; + + cs = malloc(sizeof(*cs)); + if (!cs) + goto error; + + cs->display = dpy; + cs->window = parent ? parent->window : None; + cs->visual = NULL; + cs->context = NULL; + + if (parent && parent->context) { + status = glXQueryContext( + parent->display, + parent->context, + GLX_FBCONFIG_ID, &fbconfig_id + ); + if (status != Success) + goto error; + + fbconfigs = glXGetFBConfigs(dpy, screen, &n_fbconfigs); + if (!fbconfigs) + goto error; + + /* Find out a GLXFBConfig compatible with the parent context */ + for (n = 0; n < n_fbconfigs; n++) { + status = glXGetFBConfigAttrib( + dpy, + fbconfigs[n], + GLX_FBCONFIG_ID, &val + ); + if (status == Success && val == fbconfig_id) + break; + } + if (n == n_fbconfigs) + goto error; + } + else { + fbconfigs = glXChooseFBConfig( + dpy, + screen, + fbconfig_attrs, &n_fbconfigs + ); + if (!fbconfigs) + goto error; + + /* Select the first one */ + n = 0; + } + + cs->visual = glXGetVisualFromFBConfig(dpy, fbconfigs[n]); + cs->context = glXCreateNewContext( + dpy, + fbconfigs[n], + GLX_RGBA_TYPE, + parent ? parent->context : NULL, + True + ); + if (cs->context) + goto end; + +error: + gl_destroy_context(cs); + cs = NULL; +end: + if (fbconfigs) + XFree(fbconfigs); + return cs; +} + +/** + * gl_destroy_context: + * @cs: a #GLContextState + * + * Destroys the GLX context @cs + */ +void +gl_destroy_context(GLContextState *cs) +{ + if (!cs) + return; + + if (cs->visual) { + XFree(cs->visual); + cs->visual = NULL; + } + + if (cs->display && cs->context) { + if (glXGetCurrentContext() == cs->context) + gl_make_current(cs->display, None, NULL); + glXDestroyContext(cs->display, cs->context); + cs->display = NULL; + cs->context = NULL; + } + free(cs); +} + +/** + * gl_init_context: + * @cs: a #GLContextState + * + * Initializes the GLX context @cs with a base environment. + */ +void +gl_init_context(GLContextState *cs) +{ + GLContextState old_cs, tmp_cs; + + if (!gl_set_current_context(cs, &old_cs)) + return; + + glEnable(GL_TEXTURE_2D); + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glDisable(GL_CULL_FACE); + glDrawBuffer(GL_BACK); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + gl_set_current_context(&old_cs, &tmp_cs); +} + +/** + * gl_get_current_context: + * @cs: return location to the current #GLContextState + * + * Retrieves the current GLX context, display and drawable packed into + * the #GLContextState struct. + */ +void +gl_get_current_context(GLContextState *cs) +{ + if (gl_current_context_cache) { + cs->display = gl_current_display; + cs->window = gl_current_window; + cs->context = gl_current_context; + } + else { + cs->display = glXGetCurrentDisplay(); + cs->window = glXGetCurrentDrawable(); + cs->context = glXGetCurrentContext(); + } +} + +/** + * gl_set_current_context: + * @new_cs: the requested new #GLContextState + * @old_cs: return location to the context that was previously current + * + * Makes the @new_cs GLX context the current GLX rendering context of + * the calling thread, replacing the previously current context if + * there was one. + * + * If @old_cs is non %NULL, the previously current GLX context and + * window are recorded. + * + * Return value: 1 on success + */ +int +gl_set_current_context(GLContextState *new_cs, GLContextState *old_cs) +{ + /* If display is NULL, this could be that new_cs was retrieved from + gl_get_current_context() with none set previously. If that case, + the other fields are also NULL and we don't return an error */ + if (!new_cs->display) + return !new_cs->window && !new_cs->context; + + if (old_cs) { + if (old_cs == new_cs) + return 1; + + gl_get_current_context(old_cs); + if (old_cs->display == new_cs->display && + old_cs->window == new_cs->window && + old_cs->context == new_cs->context) + return 1; + + /* Make sure gl_set_current_context(old_cs, NULL); releases + the current GLX context */ + if (!old_cs->display) + old_cs->display = new_cs->display; + } + return gl_make_current(new_cs->display, new_cs->window, new_cs->context); +} + +/** + * gl_swap_buffers: + * @cs: a #GLContextState + * + * Promotes the contents of the back buffer of the @win window to + * become the contents of the front buffer. This simply is wrapper + * around glXSwapBuffers(). + */ +void +gl_swap_buffers(GLContextState *cs) +{ + glXSwapBuffers(cs->display, cs->window); +} + +/** + * get_proc_address: + * @name: the name of the OpenGL extension function to lookup + * + * Returns the specified OpenGL extension function + * + * Return value: the OpenGL extension matching @name, or %NULL if none + * was found + */ +typedef void (*GLFuncPtr)(void); +typedef GLFuncPtr (*GLXGetProcAddressProc)(const char *); + +static GLFuncPtr +get_proc_address_default(const char *name) +{ + return NULL; +} + +static GLXGetProcAddressProc +get_proc_address_func(void) +{ + GLXGetProcAddressProc get_proc_func; + + dlerror(); + get_proc_func = (GLXGetProcAddressProc) + dlsym(RTLD_DEFAULT, "glXGetProcAddress"); + if (!dlerror()) + return get_proc_func; + + get_proc_func = (GLXGetProcAddressProc) + dlsym(RTLD_DEFAULT, "glXGetProcAddressARB"); + if (!dlerror()) + return get_proc_func; + + return get_proc_address_default; +} + +static inline GLFuncPtr +get_proc_address(const char *name) +{ + static GLXGetProcAddressProc get_proc_func = NULL; + if (!get_proc_func) + get_proc_func = get_proc_address_func(); + return get_proc_func(name); +} + +/** + * gl_init_vtable: + * + * Initializes the global #GLVTable. + * + * Return value: the #GLVTable filled in with OpenGL extensions, or + * %NULL on error. + */ +static GLVTable gl_vtable_static; + +static GLVTable * +gl_init_vtable(void) +{ + GLVTable * const gl_vtable = &gl_vtable_static; + const char *gl_extensions = (const char *)glGetString(GL_EXTENSIONS); + int has_extension; + + /* GL_ARB_texture_non_power_of_two */ + has_extension = ( + find_string("GL_ARB_texture_non_power_of_two", gl_extensions, " ") + ); + if (has_extension) + gl_vtable->has_texture_non_power_of_two = 1; + + /* GL_ARB_texture_rectangle */ + has_extension = ( + find_string("GL_ARB_texture_rectangle", gl_extensions, " ") + ); + if (has_extension) + gl_vtable->has_texture_rectangle = 1; + + /* GL_ARB_texture_float */ + has_extension = ( + find_string("GL_ARB_texture_float", gl_extensions, " ") + ); + if (has_extension) + gl_vtable->has_texture_float = 1; + + /* GL_ARB_framebuffer_object */ + has_extension = ( + find_string("GL_ARB_framebuffer_object", gl_extensions, " ") || + find_string("GL_EXT_framebuffer_object", gl_extensions, " ") + ); + if (has_extension) { + gl_vtable->gl_gen_framebuffers = (PFNGLGENFRAMEBUFFERSEXTPROC) + get_proc_address("glGenFramebuffersEXT"); + if (!gl_vtable->gl_gen_framebuffers) + return NULL; + gl_vtable->gl_delete_framebuffers = (PFNGLDELETEFRAMEBUFFERSEXTPROC) + get_proc_address("glDeleteFramebuffersEXT"); + if (!gl_vtable->gl_delete_framebuffers) + return NULL; + gl_vtable->gl_bind_framebuffer = (PFNGLBINDFRAMEBUFFEREXTPROC) + get_proc_address("glBindFramebufferEXT"); + if (!gl_vtable->gl_bind_framebuffer) + return NULL; + gl_vtable->gl_gen_renderbuffers = (PFNGLGENRENDERBUFFERSEXTPROC) + get_proc_address("glGenRenderbuffersEXT"); + if (!gl_vtable->gl_gen_renderbuffers) + return NULL; + gl_vtable->gl_delete_renderbuffers = (PFNGLDELETERENDERBUFFERSEXTPROC) + get_proc_address("glDeleteRenderbuffersEXT"); + if (!gl_vtable->gl_delete_renderbuffers) + return NULL; + gl_vtable->gl_bind_renderbuffer = (PFNGLBINDRENDERBUFFEREXTPROC) + get_proc_address("glBindRenderbufferEXT"); + if (!gl_vtable->gl_bind_renderbuffer) + return NULL; + gl_vtable->gl_renderbuffer_storage = (PFNGLRENDERBUFFERSTORAGEEXTPROC) + get_proc_address("glRenderbufferStorageEXT"); + if (!gl_vtable->gl_renderbuffer_storage) + return NULL; + gl_vtable->gl_framebuffer_renderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) + get_proc_address("glFramebufferRenderbufferEXT"); + if (!gl_vtable->gl_framebuffer_renderbuffer) + return NULL; + gl_vtable->gl_framebuffer_texture_2d = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) + get_proc_address("glFramebufferTexture2DEXT"); + if (!gl_vtable->gl_framebuffer_texture_2d) + return NULL; + gl_vtable->gl_check_framebuffer_status = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) + get_proc_address("glCheckFramebufferStatusEXT"); + if (!gl_vtable->gl_check_framebuffer_status) + return NULL; + gl_vtable->has_framebuffer_object = 1; + } + + /* GL_ARB_fragment_program */ + has_extension = ( + find_string("GL_ARB_fragment_program", gl_extensions, " ") + ); + if (has_extension) { + gl_vtable->gl_gen_programs = (PFNGLGENPROGRAMSARBPROC) + get_proc_address("glGenProgramsARB"); + if (!gl_vtable->gl_gen_programs) + return NULL; + gl_vtable->gl_delete_programs = (PFNGLDELETEPROGRAMSARBPROC) + get_proc_address("glDeleteProgramsARB"); + if (!gl_vtable->gl_delete_programs) + return NULL; + gl_vtable->gl_bind_program = (PFNGLBINDPROGRAMARBPROC) + get_proc_address("glBindProgramARB"); + if (!gl_vtable->gl_bind_program) + return NULL; + gl_vtable->gl_program_string = (PFNGLPROGRAMSTRINGARBPROC) + get_proc_address("glProgramStringARB"); + if (!gl_vtable->gl_program_string) + return NULL; + gl_vtable->gl_get_program_iv = (PFNGLGETPROGRAMIVARBPROC) + get_proc_address("glGetProgramivARB"); + if (!gl_vtable->gl_get_program_iv) + return NULL; + gl_vtable->gl_program_local_parameter_4fv = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) + get_proc_address("glProgramLocalParameter4fvARB"); + if (!gl_vtable->gl_program_local_parameter_4fv) + return NULL; + gl_vtable->has_fragment_program = 1; + } + + /* GL_ARB_multitexture */ + has_extension = ( + find_string("GL_ARB_multitexture", gl_extensions, " ") + ); + if (has_extension) { + gl_vtable->gl_active_texture = (PFNGLACTIVETEXTUREPROC) + get_proc_address("glActiveTextureARB"); + if (!gl_vtable->gl_active_texture) + return NULL; + gl_vtable->gl_multi_tex_coord_2f = (PFNGLMULTITEXCOORD2FPROC) + get_proc_address("glMultiTexCoord2fARB"); + if (!gl_vtable->gl_multi_tex_coord_2f) + return NULL; + gl_vtable->has_multitexture = 1; + } + + /* GL_ARB_gpu_shader5 */ + has_extension = ( + find_string("GL_ARB_gpu_shader5", gl_extensions, " ") + ); + if (has_extension) { + gl_vtable->has_gpu_shader5 = 1; + } + return gl_vtable; +} + +/** + * gl_get_vtable: + * + * Retrieves a VTable for OpenGL extensions. + * + * Return value: VTable for OpenGL extensions + */ +GLVTable * +gl_get_vtable(void) +{ + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + static int gl_vtable_init = 1; + static GLVTable *gl_vtable = NULL; + + pthread_mutex_lock(&mutex); + if (gl_vtable_init) { + gl_vtable_init = 0; + gl_vtable = gl_init_vtable(); + } + pthread_mutex_unlock(&mutex); + return gl_vtable; +} + +/** + * gl_create_texture: + * @target: the target to which the texture is bound + * @format: the format of the pixel data + * @width: the requested width, in pixels + * @height: the requested height, in pixels + * + * Creates a texture with the specified dimensions and @format. The + * internal format will be automatically derived from @format. + * + * Return value: the newly created texture name + */ +GLuint +gl_create_texture( + GLenum target, + GLenum format, + unsigned int width, + unsigned int height +) +{ + GLVTable * const gl_vtable = gl_get_vtable(); + GLenum internal_format, type; + GLuint texture; + unsigned int bytes_per_component; + + if (!gl_vtable) + return 0; + + switch (target) { + case GL_TEXTURE_1D: + break; + case GL_TEXTURE_2D: + if (!gl_vtable->has_texture_non_power_of_two) + return 0; + break; + case GL_TEXTURE_RECTANGLE_ARB: + if (!gl_vtable->has_texture_rectangle) + return 0; + break; + default: + D(bug("Unsupported texture target 0x%04x\n", target)); + return 0; + } + + internal_format = format; + type = GL_UNSIGNED_BYTE; + switch (format) { + case GL_LUMINANCE: + bytes_per_component = 1; + break; + case GL_LUMINANCE_ALPHA: + bytes_per_component = 2; + break; + case GL_RGBA: + case GL_BGRA: + internal_format = GL_RGBA; + bytes_per_component = 4; + break; + case GL_RGBA32F_ARB: + if (!gl_vtable->has_texture_float) + return 0; + internal_format = GL_RGBA32F_ARB; + format = GL_RGBA; + type = GL_FLOAT; + bytes_per_component = 4 * 4; + break; + default: + bytes_per_component = 0; + break; + } + ASSERT(bytes_per_component > 0); + + glEnable(target); + glGenTextures(1, &texture); + glBindTexture(target, texture); + gl_set_texture_scaling(target, GL_LINEAR); + gl_set_texture_wrapping(target, GL_CLAMP_TO_EDGE); + glPixelStorei(GL_UNPACK_ALIGNMENT, bytes_per_component); + switch (target) { + case GL_TEXTURE_1D: + glTexImage1D( + target, + 0, + internal_format, + width, + 0, + format, + type, + NULL + ); + break; + case GL_TEXTURE_2D: + case GL_TEXTURE_RECTANGLE_ARB: + glTexImage2D( + target, + 0, + internal_format, + width, height, + 0, + format, + type, + NULL + ); + break; + } + glBindTexture(target, 0); + return texture; +} + +/** + * gl_create_texture_object: + * @target: the target to which the texture is bound + * @format: the format of the pixel data + * @width: the requested width, in pixels + * @height: the requested height, in pixels + * + * Creates a texture with the specified dimensions and @format. The + * internal format will be automatically derived from @format. + * + * Return value: the newly created #GLTextureObject, or %NULL if + * an error occurred + */ +GLTextureObject * +gl_create_texture_object( + GLenum target, + GLenum format, + unsigned int width, + unsigned int height +) +{ + GLTextureObject *to; + + to = calloc(1, sizeof(*to)); + if (!to) + return NULL; + + to->texture = gl_create_texture(target, format, width, height); + if (!to->texture) + goto error; + to->target = target; + to->format = format; + to->width = width; + to->height = height; + return to; + +error: + gl_destroy_texture_object(to); + return NULL; +} + +/** + * gl_destroy_texture_object: + * @to: a #GLTextureObject + * + * Destroys the #GLTextureObject object. + */ +void +gl_destroy_texture_object(GLTextureObject *to) +{ + if (!to) + return; + + if (to->texture) { + glDeleteTextures(1, &to->texture); + to->texture = 0; + } + free(to); +} + +/** + * gl_create_framebuffer_object: + * @target: the target to which the texture is bound + * @texture: the GL texture to hold the framebuffer + * @width: the requested width, in pixels + * @height: the requested height, in pixels + * + * Creates an FBO with the specified texture and size. + * + * Return value: the newly created #GLFramebufferObject, or %NULL if + * an error occurred + */ +GLFramebufferObject * +gl_create_framebuffer_object( + GLenum target, + GLuint texture, + unsigned int width, + unsigned int height +) +{ + GLVTable * const gl_vtable = gl_get_vtable(); + GLFramebufferObject *fbo; + GLenum status; + + if (!gl_vtable || !gl_vtable->has_framebuffer_object) + return NULL; + + fbo = calloc(1, sizeof(*fbo)); + if (!fbo) + return NULL; + + fbo->width = width; + fbo->height = height; + fbo->fbo = 0; + fbo->is_bound = 0; + + gl_vtable->gl_gen_framebuffers(1, &fbo->fbo); + gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->fbo); + gl_vtable->gl_framebuffer_texture_2d( + GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + target, texture, + 0 + ); + + status = gl_vtable->gl_check_framebuffer_status(GL_DRAW_FRAMEBUFFER_EXT); + gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, 0); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + goto error; + return fbo; + +error: + gl_destroy_framebuffer_object(fbo); + return NULL; +} + +/** + * gl_destroy_framebuffer_object: + * @fbo: a #GLFramebufferObject + * + * Destroys the @fbo object. + */ +void +gl_destroy_framebuffer_object(GLFramebufferObject *fbo) +{ + GLVTable * const gl_vtable = gl_get_vtable(); + + if (!fbo) + return; + + gl_unbind_framebuffer_object(fbo); + + if (fbo->fbo) { + gl_vtable->gl_delete_framebuffers(1, &fbo->fbo); + fbo->fbo = 0; + } + free(fbo); +} + +/** + * gl_bind_framebuffer_object: + * @fbo: a #GLFramebufferObject + * + * Binds @fbo object. + * + * Return value: 1 on success + */ +int +gl_bind_framebuffer_object(GLFramebufferObject *fbo) +{ + GLVTable * const gl_vtable = gl_get_vtable(); + const unsigned int width = fbo->width; + const unsigned int height = fbo->height; + + const unsigned int attribs = (GL_VIEWPORT_BIT | + GL_CURRENT_BIT | + GL_ENABLE_BIT | + GL_TEXTURE_BIT | + GL_COLOR_BUFFER_BIT); + + if (fbo->is_bound) + return 1; + + gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->fbo); + glPushAttrib(attribs); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glViewport(0, 0, width, height); + glTranslatef(-1.0f, -1.0f, 0.0f); + glScalef(2.0f / width, 2.0f / height, 1.0f); + + fbo->is_bound = 1; + return 1; +} + +/** + * gl_unbind_framebuffer_object: + * @fbo: a #GLFramebufferObject + * + * Releases @fbo object. + * + * Return value: 1 on success + */ +int +gl_unbind_framebuffer_object(GLFramebufferObject *fbo) +{ + GLVTable * const gl_vtable = gl_get_vtable(); + + if (!fbo->is_bound) + return 1; + + glPopAttrib(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, 0); + + fbo->is_bound = 0; + return 1; +} + +/** + * gl_create_shader_object: + * @shader_fp: the shader program source + * @shader_fp_length: the total length of the shader program source + * + * Creates a shader object from the specified program source. + * + * Return value: the newly created #GLShaderObject, or %NULL if + * an error occurred + */ +GLShaderObject * +gl_create_shader_object( + const char **shader_fp, + unsigned int shader_fp_length +) +{ + GLVTable * const gl_vtable = gl_get_vtable(); + GLShaderObject *so; + + if (!gl_vtable || !gl_vtable->has_fragment_program) + return NULL; + + if (!shader_fp || !shader_fp_length) + return NULL; + + so = calloc(1, sizeof(*so)); + if (!so) + return NULL; + + char *shader = malloc(shader_fp_length + 1); + if (!shader) + goto error; + string_array_to_char_array(shader, shader_fp); + + glEnable(GL_FRAGMENT_PROGRAM); + gl_vtable->gl_gen_programs(1, &so->shader); + gl_vtable->gl_bind_program(GL_FRAGMENT_PROGRAM, so->shader); + gl_vtable->gl_program_string( + GL_FRAGMENT_PROGRAM, + GL_PROGRAM_FORMAT_ASCII, + shader_fp_length, shader + ); + free(shader); + + GLint error_position; + glGetIntegerv(GL_PROGRAM_ERROR_POSITION, &error_position); + if (error_position != -1) { + D(bug("Error while loading fragment program: %s\n", + glGetString(GL_PROGRAM_ERROR_STRING))); + goto error; + } + + GLint is_native; + gl_vtable->gl_get_program_iv( + GL_FRAGMENT_PROGRAM, + GL_PROGRAM_UNDER_NATIVE_LIMITS, + &is_native + ); + if (!is_native) { + D(bug("Fragment program is not native\n")); + goto error; + } + gl_vtable->gl_bind_program(GL_FRAGMENT_PROGRAM, 0); + glDisable(GL_FRAGMENT_PROGRAM); + return so; + +error: + gl_destroy_shader_object(so); + return NULL; +} + +/** + * gl_destroy_shader_object: + * @fbo: a #GLShaderObject + * + * Destroys the shader program @so. + */ +void +gl_destroy_shader_object(GLShaderObject *so) +{ + GLVTable * const gl_vtable = gl_get_vtable(); + + if (!so) + return; + + gl_unbind_shader_object(so); + + if (so->shader) { + gl_vtable->gl_delete_programs(1, &so->shader); + so->shader = 0; + } + free(so); +} + +/** + * gl_bind_shader_object: + * @fbo: a #GLShaderObject + * + * Binds @so shader program. + * + * Return value: 1 on success + */ +int +gl_bind_shader_object(GLShaderObject *so) +{ + GLVTable * const gl_vtable = gl_get_vtable(); + + if (so->is_bound) + return 1; + + glEnable(GL_FRAGMENT_PROGRAM); + gl_vtable->gl_bind_program(GL_FRAGMENT_PROGRAM, so->shader); + + so->is_bound = 1; + return 1; +} + +/** + * gl_unbind_shader_object: + * @fbo: a #GLShaderObject + * + * Releases @so shader program. + * + * Return value: 1 on success + */ +int +gl_unbind_shader_object(GLShaderObject *so) +{ + GLVTable * const gl_vtable = gl_get_vtable(); + + if (!so->is_bound) + return 1; + + gl_vtable->gl_bind_program(GL_FRAGMENT_PROGRAM, 0); + glDisable(GL_FRAGMENT_PROGRAM); + + so->is_bound = 0; + return 1; +} diff --git a/src/utils_glx.h b/src/utils_glx.h new file mode 100644 index 0000000..e6f9726 --- /dev/null +++ b/src/utils_glx.h @@ -0,0 +1,233 @@ +/* + * utils_glx.h - GLX utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef UTILS_GLX_H +#define UTILS_GLX_H + +#include <GL/gl.h> +#include <GL/glext.h> +#include <GL/glx.h> + +#ifndef GL_FRAMEBUFFER_BINDING +#define GL_FRAMEBUFFER_BINDING GL_FRAMEBUFFER_BINDING_EXT +#endif +#ifndef GL_FRAGMENT_PROGRAM +#define GL_FRAGMENT_PROGRAM GL_FRAGMENT_PROGRAM_ARB +#endif +#ifndef GL_PROGRAM_FORMAT_ASCII +#define GL_PROGRAM_FORMAT_ASCII GL_PROGRAM_FORMAT_ASCII_ARB +#endif +#ifndef GL_PROGRAM_ERROR_POSITION +#define GL_PROGRAM_ERROR_POSITION GL_PROGRAM_ERROR_POSITION_ARB +#endif +#ifndef GL_PROGRAM_ERROR_STRING +#define GL_PROGRAM_ERROR_STRING GL_PROGRAM_ERROR_STRING_ARB +#endif +#ifndef GL_PROGRAM_UNDER_NATIVE_LIMITS +#define GL_PROGRAM_UNDER_NATIVE_LIMITS GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB +#endif + +const char * +gl_get_error_string(GLenum error) + attribute_hidden; + +void +gl_purge_errors(void) + attribute_hidden; + +int +gl_check_error(void) + attribute_hidden; + +int +gl_get_param(GLenum param, unsigned int *pval) + attribute_hidden; + +int +gl_get_texture_param(GLenum target, GLenum param, unsigned int *pval) + attribute_hidden; + +void +gl_set_bgcolor(uint32_t color) + attribute_hidden; + +void +gl_set_texture_scaling(GLenum target, GLenum scale) + attribute_hidden; + +void +gl_set_texture_wrapping(GLenum target, GLenum wrap) + attribute_hidden; + +void +gl_resize(unsigned int width, unsigned int height) + attribute_hidden; + +typedef struct _GLContextState GLContextState; +struct _GLContextState { + Display *display; + Window window; + XVisualInfo *visual; + GLXContext context; +}; + +GLContextState * +gl_create_context(Display *dpy, int screen, GLContextState *parent) + attribute_hidden; + +void +gl_destroy_context(GLContextState *cs) + attribute_hidden; + +void +gl_init_context(GLContextState *cs) + attribute_hidden; + +void +gl_get_current_context(GLContextState *cs) + attribute_hidden; + +int +gl_set_current_context(GLContextState *new_cs, GLContextState *old_cs) + attribute_hidden; + +void +gl_set_current_context_cache(int is_enabled) + attribute_hidden; + +void +gl_swap_buffers(GLContextState *cs) + attribute_hidden; + +typedef struct _GLVTable GLVTable; +struct _GLVTable { + PFNGLGENFRAMEBUFFERSEXTPROC gl_gen_framebuffers; + PFNGLDELETEFRAMEBUFFERSEXTPROC gl_delete_framebuffers; + PFNGLBINDFRAMEBUFFEREXTPROC gl_bind_framebuffer; + PFNGLGENRENDERBUFFERSEXTPROC gl_gen_renderbuffers; + PFNGLDELETERENDERBUFFERSEXTPROC gl_delete_renderbuffers; + PFNGLBINDRENDERBUFFEREXTPROC gl_bind_renderbuffer; + PFNGLRENDERBUFFERSTORAGEEXTPROC gl_renderbuffer_storage; + PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC gl_framebuffer_renderbuffer; + PFNGLFRAMEBUFFERTEXTURE2DEXTPROC gl_framebuffer_texture_2d; + PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC gl_check_framebuffer_status; + PFNGLGENPROGRAMSARBPROC gl_gen_programs; + PFNGLDELETEPROGRAMSARBPROC gl_delete_programs; + PFNGLBINDPROGRAMARBPROC gl_bind_program; + PFNGLPROGRAMSTRINGARBPROC gl_program_string; + PFNGLGETPROGRAMIVARBPROC gl_get_program_iv; + PFNGLPROGRAMLOCALPARAMETER4FVARBPROC gl_program_local_parameter_4fv; + PFNGLACTIVETEXTUREPROC gl_active_texture; + PFNGLMULTITEXCOORD2FPROC gl_multi_tex_coord_2f; + unsigned int has_texture_non_power_of_two : 1; + unsigned int has_texture_rectangle : 1; + unsigned int has_texture_float : 1; + unsigned int has_framebuffer_object : 1; + unsigned int has_fragment_program : 1; + unsigned int has_multitexture : 1; + unsigned int has_gpu_shader5 : 1; +}; + +GLVTable * +gl_get_vtable(void) + attribute_hidden; + +typedef struct _GLTextureObject GLTextureObject; +struct _GLTextureObject { + GLenum target; + GLenum format; + GLuint texture; + unsigned int width; + unsigned int height; +}; + +GLuint +gl_create_texture( + GLenum target, + GLenum format, + unsigned int width, + unsigned int height +) attribute_hidden; + +GLTextureObject * +gl_create_texture_object( + GLenum target, + GLenum format, + unsigned int width, + unsigned int height +) attribute_hidden; + +void +gl_destroy_texture_object(GLTextureObject *to) + attribute_hidden; + +typedef struct _GLFramebufferObject GLFramebufferObject; +struct _GLFramebufferObject { + unsigned int width; + unsigned int height; + GLuint fbo; + unsigned int is_bound : 1; +}; + +GLFramebufferObject * +gl_create_framebuffer_object( + GLenum target, + GLuint texture, + unsigned int width, + unsigned int height +) attribute_hidden; + +void +gl_destroy_framebuffer_object(GLFramebufferObject *fbo) + attribute_hidden; + +int +gl_bind_framebuffer_object(GLFramebufferObject *fbo) + attribute_hidden; + +int +gl_unbind_framebuffer_object(GLFramebufferObject *fbo) + attribute_hidden; + +typedef struct _GLShaderObject GLShaderObject; +struct _GLShaderObject { + GLuint shader; + unsigned int is_bound : 1; +}; + +GLShaderObject * +gl_create_shader_object( + const char **shader_fp, + unsigned int shader_fp_length +) attribute_hidden; + +void +gl_destroy_shader_object(GLShaderObject *so) + attribute_hidden; + +int +gl_bind_shader_object(GLShaderObject *so) + attribute_hidden; + +int +gl_unbind_shader_object(GLShaderObject *so) + attribute_hidden; + +#endif /* UTILS_GLX_H */ diff --git a/src/utils_x11.c b/src/utils_x11.c new file mode 100644 index 0000000..c4ceeba --- /dev/null +++ b/src/utils_x11.c @@ -0,0 +1,184 @@ +/* + * utils_x11.c - X11 utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include <X11/Xutil.h> +#include "utils_x11.h" +#include "utils.h" + +// X error trap +static int x11_error_code = 0; +static int (*old_error_handler)(Display *, XErrorEvent *); + +static int error_handler(Display *dpy, XErrorEvent *error) +{ + x11_error_code = error->error_code; + return 0; +} + +void x11_trap_errors(void) +{ + x11_error_code = 0; + old_error_handler = XSetErrorHandler(error_handler); +} + +int x11_untrap_errors(void) +{ + XSetErrorHandler(old_error_handler); + return x11_error_code; +} + +// X window management +static const int x11_event_mask = (KeyPressMask | + KeyReleaseMask | + ButtonPressMask | + ButtonReleaseMask | + PointerMotionMask | + EnterWindowMask | + ExposureMask | + StructureNotifyMask); + +/** + * x11_create_window: + * @dpy: an X11 #Display + * @w: the requested width, in pixels + * @h: the requested height, in pixels + * @vis: the request visual + * @cmap: the request colormap + * + * Creates a border-less window with the specified dimensions. If @vis + * is %NULL, the default visual for @display will be used. If @cmap is + * %None, no specific colormap will be bound to the window. Also note + * the default background color is black. + * + * Return value: the newly created X #Window. + */ + +Window +x11_create_window( + Display *dpy, + unsigned int w, + unsigned int h, + Visual *vis, + Colormap cmap +) +{ + Window rootwin, win; + int screen, depth; + XSetWindowAttributes xswa; + unsigned long xswa_mask; + XWindowAttributes wattr; + unsigned long black_pixel, white_pixel; + + screen = DefaultScreen(dpy); + rootwin = RootWindow(dpy, screen); + black_pixel = BlackPixel(dpy, screen); + white_pixel = WhitePixel(dpy, screen); + + if (!vis) + vis = DefaultVisual(dpy, screen); + + XGetWindowAttributes(dpy, rootwin, &wattr); + depth = wattr.depth; + if (depth != 15 && depth != 16 && depth != 24 && depth != 32) + depth = 24; + + xswa_mask = CWBorderPixel | CWBackPixel; + xswa.border_pixel = black_pixel; + xswa.background_pixel = black_pixel; + + if (cmap) { + xswa_mask |= CWColormap; + xswa.colormap = cmap; + } + + win = XCreateWindow( + dpy, + rootwin, + 0, 0, w, h, + 0, + depth, + InputOutput, + vis, + xswa_mask, &xswa + ); + if (!win) + return None; + + XSelectInput(dpy, win, x11_event_mask); + return win; +} + +int +x11_get_geometry( + Display *dpy, + Drawable drawable, + int *px, + int *py, + unsigned int *pwidth, + unsigned int *pheight +) +{ + Window rootwin; + int x, y; + unsigned int width, height, border_width, depth; + + x11_trap_errors(); + XGetGeometry( + dpy, + drawable, + &rootwin, + &x, &y, &width, &height, + &border_width, + &depth + ); + if (x11_untrap_errors()) + return 0; + + if (px) *px = x; + if (py) *py = y; + if (pwidth) *pwidth = width; + if (pheight) *pheight = height; + return 1; +} + +void x11_wait_event(Display *dpy, Window w, int type) +{ + XEvent e; + while (!XCheckTypedWindowEvent(dpy, w, type, &e)) + delay_usec(10); +} + +// Returns X window background color at specified location +int +x11_get_window_colorkey(Display *dpy, Window w, int x, int y, unsigned int *ck) +{ + XClearArea(dpy, w, x, y, 1, 1, False); + + XImage *img = XGetImage(dpy, w, x, y, 1, 1, AllPlanes, ZPixmap); + if (img == NULL) + return -1; + + if (ck) + *ck = XGetPixel(img, 0, 0); + + XDestroyImage(img); + return 0; +} diff --git a/src/utils_x11.h b/src/utils_x11.h new file mode 100644 index 0000000..43c96d7 --- /dev/null +++ b/src/utils_x11.h @@ -0,0 +1,59 @@ +/* + * utils_x11.h - X11 utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef UTILS_X11_H +#define UTILS_X11_H + +#include "config.h" +#include <X11/Xlib.h> + +void x11_trap_errors(void) + attribute_hidden; + +int x11_untrap_errors(void) + attribute_hidden; + +Window +x11_create_window( + Display *dpy, + unsigned int w, + unsigned int h, + Visual *vis, + Colormap cmap +) attribute_hidden; + +int +x11_get_geometry( + Display *dpy, + Drawable drawable, + int *px, + int *py, + unsigned int *pwidth, + unsigned int *pheight +) attribute_hidden; + +void x11_wait_event(Display *dpy, Window w, int type) + attribute_hidden; + +int +x11_get_window_colorkey(Display *dpy, Window w, int x, int y, unsigned int *ck) + attribute_hidden; + +#endif /* UTILS_X11_H */ diff --git a/src/vaapi_compat.h b/src/vaapi_compat.h new file mode 100644 index 0000000..08adef9 --- /dev/null +++ b/src/vaapi_compat.h @@ -0,0 +1,64 @@ +/* + * vaapi_compat.h - VA-API compatibility glue + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef VAAPI_COMPAT_H +#define VAAPI_COMPAT_H + +#ifndef VA_INVALID_ID +#define VA_INVALID_ID 0xffffffff +#endif +#ifndef VA_INVALID_BUFFER +#define VA_INVALID_BUFFER VA_INVALID_ID +#endif +#ifndef VA_INVALID_SURFACE +#define VA_INVALID_SURFACE VA_INVALID_ID +#endif +#ifndef VA_STATUS_ERROR_UNIMPLEMENTED +#define VA_STATUS_ERROR_UNIMPLEMENTED 0x00000014 +#endif +#ifndef VA_DISPLAY_X11 +#define VA_DISPLAY_X11 1 +#endif +#ifndef VA_DISPLAY_GLX +#define VA_DISPLAY_GLX 2 +#endif +#ifndef VA_FILTER_SCALING_DEFAULT +#define VA_FILTER_SCALING_DEFAULT 0x00000000 +#endif +#ifndef VA_FILTER_SCALING_FAST +#define VA_FILTER_SCALING_FAST 0x00000100 +#endif +#ifndef VA_FILTER_SCALING_HQ +#define VA_FILTER_SCALING_HQ 0x00000200 +#endif +#ifndef VA_FILTER_SCALING_NL_ANAMORPHIC +#define VA_FILTER_SCALING_NL_ANAMORPHIC 0x00000300 +#endif +#ifndef VA_FILTER_SCALING_MASK +#define VA_FILTER_SCALING_MASK 0x00000f00 +#endif + +#if VA_CHECK_VERSION(0,31,1) +typedef void *VADrawable; +#else +typedef Drawable VADrawable; +#endif + +#endif /* VAAPI_COMPAT_H */ diff --git a/src/xvba_buffer.c b/src/xvba_buffer.c new file mode 100644 index 0000000..4facbbf --- /dev/null +++ b/src/xvba_buffer.c @@ -0,0 +1,946 @@ +/* + * xvba_buffer.c - XvBA backend for VA-API (VA buffers) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "xvba_buffer.h" +#include "xvba_driver.h" +#include "xvba_decode.h" +#include "xvba_video.h" +#include "xvba_dump.h" +#include "fglrxinfo.h" +#include <math.h> + +#define DEBUG 1 +#include "debug.h" + + +// Definitions for XVBAPictureDescriptor.picture_structure field +enum { + PICT_TOP_FIELD = 0, + PICT_BOTTOM_FIELD = 1, + PICT_FRAME = 3 +}; + +// Reconstruct H.264 level_idc syntax element +static int +vaapi_h264_get_level( + VAPictureParameterBufferH264 *pic_param, + unsigned int picture_width, + unsigned int picture_height +) +{ + const unsigned int PicWidthInMbs = pic_param->picture_width_in_mbs_minus1 + 1; + const unsigned int FrameHeightInMbs = (picture_height + 15) / 16; + + /* Table A-1. Level limits */ + static const struct { + int level; + unsigned int MaxMBPS; + unsigned int MaxFS; + unsigned int MaxDPBx2; + unsigned int MaxBR; + unsigned int MaxCPB; + } + map[] = { + { 10, 1485, 99, 297, 64, 175 }, + { 9, 1485, 99, 297, 128, 350 }, + { 11, 3000, 396, 675, 192, 500 }, + { 12, 6000, 396, 1782, 384, 1000 }, + { 13, 11880, 396, 1782, 768, 2000 }, + { 20, 11880, 396, 1782, 2000, 2000 }, + { 21, 19800, 792, 3564, 4000, 4000 }, + { 22, 20250, 1620, 6075, 4000, 4000 }, + { 30, 40500, 1620, 6075, 10000, 10000 }, + { 31, 108000, 3600, 13500, 14000, 14000 }, + { 32, 216000, 5120, 15360, 20000, 20000 }, + { 40, 245760, 8192, 24576, 20000, 25000 }, + { 41, 245760, 8192, 24576, 50000, 62500 }, + { 42, 522240, 8704, 26112, 50000, 62500 }, + { 50, 589824, 22080, 82800, 135000, 135000 }, + { 51, 983040, 36864, 138240, 240000, 240000 }, + { -1, } + }; + + int i; + for (i = 0; map[i].level >= 0; i++) { + if (map[i].MaxFS < PicWidthInMbs * FrameHeightInMbs) /* A.3.1 (e) */ + continue; + const unsigned int sqrt_8xMaxFS = sqrt(8.0 * map[i].MaxFS); + if (sqrt_8xMaxFS < PicWidthInMbs) /* A.3.1 (f) */ + continue; + if (sqrt_8xMaxFS < FrameHeightInMbs) /* A.3.1 (g) */ + continue; + const unsigned int max_ref_frames = map[i].MaxDPBx2 * 512 / (PicWidthInMbs * FrameHeightInMbs * 384); + if (max_ref_frames < pic_param->num_ref_frames) /* A.3.1 (h) */ + continue; + return map[i].level; + } + D(bug("vaapi_h264_get_level(): could not reconstruct level\n")); + return 0; +} + +// Reconstruct picture_structure +static int +vaapi_h264_get_picture_structure(VAPictureParameterBufferH264 *pic_param) +{ + int picture_structure; + switch (pic_param->CurrPic.flags & (VA_PICTURE_H264_TOP_FIELD | + VA_PICTURE_H264_BOTTOM_FIELD)) { + case VA_PICTURE_H264_TOP_FIELD: + picture_structure = PICT_TOP_FIELD; + break; + case VA_PICTURE_H264_BOTTOM_FIELD: + picture_structure = PICT_BOTTOM_FIELD; + break; + default: /* 0 || VA_PICTURE_H264_TOP_FIELD|VA_PICTURE_H264_BOTTOM_FIELD */ + picture_structure = PICT_FRAME; + break; + } + return picture_structure; +} + +// Reconstruct VC-1 LEVEL syntax element +static int +vaapi_vc1_get_level_advanced( + VAPictureParameterBufferVC1 *pic_param, + unsigned int picture_width, + unsigned int picture_height +) +{ + /* XXX: use CODED_WIDTH and CODED_HEIGHT syntax elements instead? */ + const unsigned int mb_width = (picture_width + 15) / 16; + const unsigned int mb_height = (picture_height + 15) / 16; + const unsigned int MB_f = mb_width * mb_height; + const unsigned int MB_s = MB_f * 30; /* default to 30 Hz */ + + /* Table 253. Limitations of profiles and levels */ + static const struct { + int level; + unsigned int MB_s; + unsigned int MB_f; + int interlace; + unsigned int Rmax; + unsigned int Bmax; + } + map[] = { + { 0, 11880, 396, 0, 2000, 250 }, + { 1, 48600, 1620, 1, 10000, 1250 }, + { 2, 110400, 3680, 1, 20000, 2500 }, + { 3, 245760, 8192, 1, 45000, 5500 }, + { 4, 491520, 16384, 1, 135000, 16500 }, + { -1, } + }; + + int i; + for (i = 0; map[i].level >= 0; i++) { + if (pic_param->sequence_fields.bits.interlace && !map[i].interlace) + continue; + if (map[i].MB_f < MB_f) + continue; + if (map[i].MB_s < MB_s) + continue; + return map[i].level; + } + D(bug("vaapi_vc1_get_level_advanced(): could not reconstruct level\n")); + return 0; +} + +// Reconstruct picture_structure +static int +vaapi_vc1_get_picture_structure(VAPictureParameterBufferVC1 *pic_param) +{ + int picture_structure; + if (pic_param->sequence_fields.bits.interlace && + pic_param->picture_fields.bits.frame_coding_mode == 3) { + /* Field-Interlace mode */ + if (pic_param->picture_fields.bits.is_first_field && + pic_param->picture_fields.bits.top_field_first) + picture_structure = PICT_TOP_FIELD; + else + picture_structure = PICT_BOTTOM_FIELD; + } + else + picture_structure = PICT_FRAME; + return picture_structure; +} + +// Create VA buffer object +object_buffer_p +create_va_buffer( + xvba_driver_data_t *driver_data, + VAContextID context, + VABufferType buffer_type, + unsigned int num_elements, + unsigned int size +) +{ + VABufferID buffer_id; + object_buffer_p obj_buffer; + + buffer_id = object_heap_allocate(&driver_data->buffer_heap); + if (buffer_id == VA_INVALID_BUFFER) + return NULL; + + obj_buffer = XVBA_BUFFER(buffer_id); + if (!obj_buffer) + return NULL; + + obj_buffer->va_context = context; + obj_buffer->type = buffer_type; + obj_buffer->max_num_elements = num_elements; + obj_buffer->num_elements = num_elements; + obj_buffer->buffer_size = size * num_elements; + obj_buffer->buffer_data = malloc(obj_buffer->buffer_size); + obj_buffer->mtime = 0; + + if (!obj_buffer->buffer_data) { + destroy_va_buffer(driver_data, obj_buffer); + return NULL; + } + return obj_buffer; +} + +// Destroy VA buffer object +void +destroy_va_buffer( + xvba_driver_data_t *driver_data, + object_buffer_p obj_buffer +) +{ + if (!obj_buffer) + return; + + if (obj_buffer->buffer_data) { + free(obj_buffer->buffer_data); + obj_buffer->buffer_data = NULL; + } + object_heap_free(&driver_data->buffer_heap, &obj_buffer->base); +} + +// Destroy VA buffer objects stored in VA context +void +destroy_va_buffers( + xvba_driver_data_t *driver_data, + object_context_p obj_context +) +{ + unsigned int i; + + for (i = 0; i < obj_context->va_buffers_count; i++) { + object_buffer_p obj_buffer = XVBA_BUFFER(obj_context->va_buffers[i]); + if (!obj_buffer) + continue; + destroy_va_buffer(driver_data, obj_buffer); + } + obj_context->va_buffers_count = 0; +} + +// Determines whether BUFFER is queued for decoding +int +is_queued_buffer( + xvba_driver_data_t *driver_data, + object_buffer_p obj_buffer +) +{ + object_context_p obj_context = XVBA_CONTEXT(obj_buffer->va_context); + if (!obj_context) + return 0; + + unsigned int i; + for (i = 0; i < obj_context->va_buffers_count; i++) { + if (obj_context->va_buffers[i] == obj_buffer->base.id) + return 1; + } + return 0; +} + +// Translate no buffer +static int +translate_nothing( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_buffer_p obj_buffer +) +{ + return 1; +} + +// Translate VASliceDataBuffer +static int +translate_VASliceDataBuffer( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_buffer_p obj_buffer +) +{ + /* Slice data buffers are copied in VASliceParameterBuffer + translation routines */ + return 1; +} + +// Translate VAPictureParameterBufferMPEG2 +static int +translate_VAPictureParameterBufferMPEG2( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_buffer_p obj_buffer +) +{ + return 0; +} + +// Translate VAIQMatrixBufferMPEG2 +static int +translate_VAIQMatrixBufferMPEG2( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_buffer_p obj_buffer +) +{ + return 0; +} + +// Translate VASliceParameterBufferMPEG2 +static int +translate_VASliceParameterBufferMPEG2( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_buffer_p obj_buffer + ) +{ + return 0; +} + +// Translate VAPictureParameterBufferH264 +static int +translate_VAPictureParameterBufferH264( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_buffer_p obj_buffer +) +{ + VAPictureParameterBufferH264 * const pic_param = obj_buffer->buffer_data; + + object_surface_p obj_surface = XVBA_SURFACE(obj_context->current_render_target); + if (!obj_surface) + return 0; + + object_config_p obj_config = XVBA_CONFIG(obj_context->va_config); + if (!obj_config) + return 0; + + int profile, level; + switch (obj_config->profile) { + case VAProfileH264Baseline: + profile = XVBA_H264_BASELINE; + break; + case VAProfileH264Main: + profile = XVBA_H264_MAIN; + break; + case VAProfileH264High: + profile = XVBA_H264_HIGH; + break; + default: + D(bug("translate_VAPictureParameterBufferH264(): invalid profile\n")); + return 0; + } + + level = vaapi_h264_get_level( + pic_param, + obj_context->picture_width, + obj_context->picture_height + ); + + /* Check for H.264 content over HP@L4.1 */ + unsigned int num_ref_frames = pic_param->num_ref_frames; + if (profile == XVBA_H264_HIGH && level > 41) { + if (!driver_data->warn_h264_over_hp_l41) { + driver_data->warn_h264_over_hp_l41 = 1; + xvba_information_message( + "driver does not support H.264 content over HP@L4.1. " + "Please upgrade.\n" + ); + } + + /* Use fail-safe values (lower ref frames) */ + const unsigned int mbw = pic_param->picture_width_in_mbs_minus1 + 1; + const unsigned int mbh = (obj_context->picture_height + 15) / 16; + const unsigned int max_ref_frames = 12288 * 1024 / (mbw * mbh * 384); + if (max_ref_frames < num_ref_frames) + num_ref_frames = max_ref_frames; + } + + XVBABufferDescriptor * const xvba_buffer = obj_surface->pic_desc_buffer; + ASSERT(xvba_buffer); + if (!xvba_buffer) + return 0; + + XVBAPictureDescriptor * const pic_desc = xvba_buffer->bufferXVBA; + memset(pic_desc, 0, sizeof(*pic_desc)); + + pic_desc->past_surface = NULL; + pic_desc->future_surface = NULL; + pic_desc->profile = profile; + pic_desc->level = level; + pic_desc->width_in_mb = 1 + pic_param->picture_width_in_mbs_minus1; + pic_desc->height_in_mb = 1 + pic_param->picture_height_in_mbs_minus1; + pic_desc->picture_structure = vaapi_h264_get_picture_structure(pic_param); + pic_desc->sps_info.flags = 0; /* reset all bits */ + pic_desc->sps_info.avc.residual_colour_transform_flag = pic_param->seq_fields.bits.residual_colour_transform_flag; + pic_desc->sps_info.avc.delta_pic_always_zero_flag = pic_param->seq_fields.bits.delta_pic_order_always_zero_flag; + pic_desc->sps_info.avc.gaps_in_frame_num_value_allowed_flag = pic_param->seq_fields.bits.gaps_in_frame_num_value_allowed_flag; + pic_desc->sps_info.avc.frame_mbs_only_flag = pic_param->seq_fields.bits.frame_mbs_only_flag; + pic_desc->sps_info.avc.mb_adaptive_frame_field_flag = pic_param->seq_fields.bits.mb_adaptive_frame_field_flag; + pic_desc->sps_info.avc.direct_8x8_inference_flag = pic_param->seq_fields.bits.direct_8x8_inference_flag; + pic_desc->chroma_format = pic_param->seq_fields.bits.chroma_format_idc; + pic_desc->avc_bit_depth_luma_minus8 = pic_param->bit_depth_luma_minus8; + pic_desc->avc_bit_depth_chroma_minus8 = pic_param->bit_depth_chroma_minus8; + pic_desc->avc_log2_max_frame_num_minus4 = pic_param->seq_fields.bits.log2_max_frame_num_minus4; + pic_desc->avc_pic_order_cnt_type = pic_param->seq_fields.bits.pic_order_cnt_type; + pic_desc->avc_log2_max_pic_order_cnt_lsb_minus4 = pic_param->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4; + pic_desc->avc_num_ref_frames = num_ref_frames; + pic_desc->pps_info.flags = 0; /* reset all bits */ + pic_desc->pps_info.avc.entropy_coding_mode_flag = pic_param->pic_fields.bits.entropy_coding_mode_flag; + pic_desc->pps_info.avc.pic_order_present_flag = pic_param->pic_fields.bits.pic_order_present_flag; + pic_desc->pps_info.avc.weighted_pred_flag = pic_param->pic_fields.bits.weighted_pred_flag; + pic_desc->pps_info.avc.weighted_bipred_idc = pic_param->pic_fields.bits.weighted_bipred_idc; + pic_desc->pps_info.avc.deblocking_filter_control_present_flag = pic_param->pic_fields.bits.deblocking_filter_control_present_flag; + pic_desc->pps_info.avc.constrained_intra_pred_flag = pic_param->pic_fields.bits.constrained_intra_pred_flag; + pic_desc->pps_info.avc.redundant_pic_cnt_present_flag = pic_param->pic_fields.bits.redundant_pic_cnt_present_flag; + pic_desc->pps_info.avc.transform_8x8_mode_flag = pic_param->pic_fields.bits.transform_8x8_mode_flag; + pic_desc->avc_num_slice_groups_minus1 = pic_param->num_slice_groups_minus1; + pic_desc->avc_slice_group_map_type = pic_param->slice_group_map_type; + pic_desc->avc_pic_init_qp_minus26 = pic_param->pic_init_qp_minus26; + pic_desc->avc_pic_init_qs_minus26 = pic_param->pic_init_qs_minus26; + pic_desc->avc_chroma_qp_index_offset = pic_param->chroma_qp_index_offset; + pic_desc->avc_second_chroma_qp_index_offset = pic_param->second_chroma_qp_index_offset; + pic_desc->avc_slice_group_change_rate_minus1 = pic_param->slice_group_change_rate_minus1; + pic_desc->avc_frame_num = pic_param->frame_num; + pic_desc->avc_reference = pic_param->pic_fields.bits.reference_pic_flag; + + pic_desc->avc_curr_field_order_cnt_list[0] = pic_param->CurrPic.TopFieldOrderCnt; + pic_desc->avc_curr_field_order_cnt_list[1] = pic_param->CurrPic.BottomFieldOrderCnt; + + xvba_buffer->data_size_in_buffer = sizeof(*pic_desc); + return 1; +} + +// Translate VAIQMatrixBufferH264 +static int +translate_VAIQMatrixBufferH264( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_buffer_p obj_buffer +) +{ + VAIQMatrixBufferH264 * const iq_matrix = obj_buffer->buffer_data; + + object_surface_p obj_surface = XVBA_SURFACE(obj_context->current_render_target); + if (!obj_surface) + return 0; + + XVBABufferDescriptor * const xvba_buffer = obj_surface->iq_matrix_buffer; + if (!xvba_buffer) + return 0; + + XVBAQuantMatrixAvc * const qm = xvba_buffer->bufferXVBA; + int i, j; + + if (sizeof(qm->bScalingLists4x4) == sizeof(iq_matrix->ScalingList4x4)) + memcpy(qm->bScalingLists4x4, iq_matrix->ScalingList4x4, + sizeof(qm->bScalingLists4x4)); + else { + for (j = 0; j < 6; j++) { + for (i = 0; i < 16; i++) + qm->bScalingLists4x4[j][i] = iq_matrix->ScalingList4x4[j][i]; + } + } + + if (sizeof(qm->bScalingLists8x8) == sizeof(iq_matrix->ScalingList8x8)) + memcpy(qm->bScalingLists8x8, iq_matrix->ScalingList8x8, + sizeof(qm->bScalingLists8x8)); + else { + for (j = 0; j < 2; j++) { + for (i = 0; i < 64; i++) + qm->bScalingLists8x8[j][i] = iq_matrix->ScalingList8x8[j][i]; + } + } + + xvba_buffer->data_size_in_buffer = sizeof(*qm); + return 1; +} + +// Translate VASliceParameterBufferH264 +static int +translate_VASliceParameterBufferH264( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_buffer_p obj_buffer +) +{ + VASliceParameterBufferH264 * const slice_param = obj_buffer->buffer_data; + + /* XXX: we don't handle partial slice data buffers yet */ + if (slice_param->slice_data_flag != VA_SLICE_DATA_FLAG_ALL) { + D(bug("partial slice data buffers are not handled\n")); + return 0; + } + + object_surface_p obj_surface = XVBA_SURFACE(obj_context->current_render_target); + if (!obj_surface) + return 0; + + XVBABufferDescriptor * const xvba_pic_desc_buffer = obj_surface->pic_desc_buffer; + if (!xvba_pic_desc_buffer) + return 0; + + XVBAPictureDescriptor * const pic_desc = xvba_pic_desc_buffer->bufferXVBA; + pic_desc->avc_intra_flag = slice_param->slice_type == 2; /* I-type */ + pic_desc->avc_num_ref_idx_l0_active_minus1 = slice_param->num_ref_idx_l0_active_minus1; + pic_desc->avc_num_ref_idx_l1_active_minus1 = slice_param->num_ref_idx_l1_active_minus1; + + object_buffer_p data_buffer = obj_context->data_buffer; + ASSERT(data_buffer); + if (!data_buffer) + return 0; + ASSERT(slice_param->slice_data_offset + slice_param->slice_data_size <= data_buffer->buffer_size); + if (slice_param->slice_data_offset + slice_param->slice_data_size > data_buffer->buffer_size) + return 0; + + XVBABufferDescriptor * const xvba_data_buffer = obj_surface->data_buffer; + ASSERT(xvba_data_buffer); + if (!xvba_data_buffer) + return 0; + + XVBABufferDescriptor * const xvba_data_ctrl_buffer = obj_surface->data_ctrl_buffers[obj_context->slice_count++]; + ASSERT(xvba_data_ctrl_buffer); + if (!xvba_data_ctrl_buffer) + return 0; + + XVBADataCtrl * const data_ctrl = xvba_data_ctrl_buffer->bufferXVBA; + const unsigned int data_offset = xvba_data_buffer->data_size_in_buffer; + + const uint8_t * const va_slice_data = ((uint8_t *)data_buffer->buffer_data + + slice_param->slice_data_offset); + + static const uint8_t start_code_prefix_one_3byte[3] = { 0x00, 0x00, 0x01 }; + if (memcmp(va_slice_data, start_code_prefix_one_3byte, 3) != 0) + append_buffer(xvba_data_buffer, start_code_prefix_one_3byte, 3); + append_buffer(xvba_data_buffer, va_slice_data, slice_param->slice_data_size); + + /* XXX: should XVBA_DATA_CTRL_BUFFER.SliceBytesInBuffer and + SliceBitsInBuffer be required to account for padding bytes too? */ + data_ctrl->SliceDataLocation = data_offset; + data_ctrl->SliceBytesInBuffer = xvba_data_buffer->data_size_in_buffer - data_offset; + data_ctrl->SliceBitsInBuffer = 8 * data_ctrl->SliceBytesInBuffer; + pad_buffer(xvba_data_buffer); + + xvba_data_ctrl_buffer->data_size_in_buffer = sizeof(*data_ctrl); + return 1; +} + +// Translate VAPictureParameterBufferVC1 +static int +translate_VAPictureParameterBufferVC1( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_buffer_p obj_buffer +) +{ + VAPictureParameterBufferVC1 * const pic_param = obj_buffer->buffer_data; + + object_surface_p obj_surface = XVBA_SURFACE(obj_context->current_render_target); + if (!obj_surface) + return 0; + + object_config_p obj_config = XVBA_CONFIG(obj_context->va_config); + if (!obj_config) + return 0; + + int profile, level = 0; + switch (obj_config->profile) { + case VAProfileVC1Simple: + profile = XVBA_VC1_SIMPLE; + break; + case VAProfileVC1Main: + profile = XVBA_VC1_MAIN; + break; + case VAProfileVC1Advanced: + profile = XVBA_VC1_ADVANCED; + level = vaapi_vc1_get_level_advanced( + pic_param, + obj_context->picture_width, + obj_context->picture_height + ); + break; + default: + D(bug("translate_VAPictureParameterBufferVC1(): invalid profile\n")); + return 0; + } + + /* Check for VC-1 content over AP@L3 */ + if (profile == XVBA_VC1_ADVANCED && level > 3) { + if (!driver_data->warn_vc1_over_ap_l3) { + driver_data->warn_vc1_over_ap_l3 = 1; + xvba_information_message( + "driver does not support VC-1 content over AP@L3. " + "Please upgrade.\n" + ); + } + } + + XVBAPictureDescriptor * const pic_desc = obj_surface->pic_desc_buffer->bufferXVBA; + memset(pic_desc, 0, sizeof(*pic_desc)); + + pic_desc->past_surface = NULL; + pic_desc->future_surface = NULL; + pic_desc->profile = profile; + pic_desc->level = level; + pic_desc->width_in_mb = pic_param->coded_width; + pic_desc->height_in_mb = pic_param->coded_height; + pic_desc->picture_structure = vaapi_vc1_get_picture_structure(pic_param); + pic_desc->sps_info.flags = 0; /* reset all bits */ + pic_desc->sps_info.vc1.postprocflag = pic_param->post_processing != 0; + pic_desc->sps_info.vc1.pulldown = pic_param->sequence_fields.bits.pulldown; + pic_desc->sps_info.vc1.interlace = pic_param->sequence_fields.bits.interlace; + pic_desc->sps_info.vc1.tfcntrflag = pic_param->sequence_fields.bits.tfcntrflag; + pic_desc->sps_info.vc1.finterpflag = pic_param->sequence_fields.bits.finterpflag; + pic_desc->sps_info.vc1.psf = pic_param->sequence_fields.bits.psf; + pic_desc->sps_info.vc1.second_field = !pic_param->picture_fields.bits.is_first_field; + pic_desc->chroma_format = 1; /* XXX: 4:2:0 */ + pic_desc->pps_info.flags = 0; /* reset all bits */ + pic_desc->pps_info.vc1.panscan_flag = pic_param->entrypoint_fields.bits.panscan_flag; + pic_desc->pps_info.vc1.refdist_flag = pic_param->reference_fields.bits.reference_distance_flag; + pic_desc->pps_info.vc1.loopfilter = pic_param->entrypoint_fields.bits.loopfilter; + pic_desc->pps_info.vc1.fastuvmc = pic_param->fast_uvmc_flag; + pic_desc->pps_info.vc1.extended_mv = pic_param->mv_fields.bits.extended_mv_flag; + pic_desc->pps_info.vc1.dquant = pic_param->pic_quantizer_fields.bits.dquant; + pic_desc->pps_info.vc1.vstransform = pic_param->transform_fields.bits.variable_sized_transform_flag; + pic_desc->pps_info.vc1.overlap = pic_param->conditional_overlap_flag; + pic_desc->pps_info.vc1.quantizer = pic_param->pic_quantizer_fields.bits.quantizer; + pic_desc->pps_info.vc1.extended_dmv = pic_param->mv_fields.bits.extended_dmv_flag; + pic_desc->pps_info.vc1.maxbframes = pic_param->sequence_fields.bits.max_b_frames; + pic_desc->pps_info.vc1.rangered = pic_param->sequence_fields.bits.rangered; + pic_desc->pps_info.vc1.syncmarker = pic_param->sequence_fields.bits.syncmarker; + pic_desc->pps_info.vc1.multires = pic_param->sequence_fields.bits.multires; + pic_desc->pps_info.vc1.range_mapy_flag = pic_param->range_mapping_fields.bits.luma_flag; + pic_desc->pps_info.vc1.range_mapy = pic_param->range_mapping_fields.bits.luma; + pic_desc->pps_info.vc1.range_mapuv_flag = pic_param->range_mapping_fields.bits.chroma_flag; + pic_desc->pps_info.vc1.range_mapuv = pic_param->range_mapping_fields.bits.chroma; + + if (pic_param->backward_reference_picture != VA_INVALID_SURFACE) { + object_surface_p s = XVBA_SURFACE(pic_param->backward_reference_picture); + ASSERT(s); + if (!s) + return 0; + pic_desc->past_surface = s->xvba_surface; + } + + if (pic_param->forward_reference_picture != VA_INVALID_SURFACE) { + object_surface_p s = XVBA_SURFACE(pic_param->forward_reference_picture); + ASSERT(s); + if (!s) + return 0; + pic_desc->future_surface = s->xvba_surface; + } + + obj_surface->pic_desc_buffer->data_size_in_buffer = sizeof(*pic_desc); + return 1; +} + +// Translate VASliceParameterBufferVC1 +static int +translate_VASliceParameterBufferVC1( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_buffer_p obj_buffer +) +{ + VASliceParameterBufferVC1 * const slice_param = obj_buffer->buffer_data; + + /* XXX: we don't handle partial slice data buffers yet */ + if (slice_param->slice_data_flag != VA_SLICE_DATA_FLAG_ALL) { + D(bug("partial slice data buffers are not handled\n")); + return 0; + } + + object_surface_p obj_surface = XVBA_SURFACE(obj_context->current_render_target); + if (!obj_surface) + return 0; + + XVBABufferDescriptor * const xvba_pic_desc_buffer = obj_surface->pic_desc_buffer; + if (!xvba_pic_desc_buffer) + return 0; + + XVBAPictureDescriptor * const pic_desc = xvba_pic_desc_buffer->bufferXVBA; + + object_buffer_p data_buffer = obj_context->data_buffer; + ASSERT(data_buffer); + if (!data_buffer) + return 0; + ASSERT(slice_param->slice_data_offset + slice_param->slice_data_size <= data_buffer->buffer_size); + if (slice_param->slice_data_offset + slice_param->slice_data_size > data_buffer->buffer_size) + return 0; + + XVBABufferDescriptor * const xvba_data_buffer = obj_surface->data_buffer; + ASSERT(xvba_data_buffer); + if (!xvba_data_buffer) + return 0; + + XVBABufferDescriptor * const xvba_data_ctrl_buffer = obj_surface->data_ctrl_buffers[obj_context->slice_count++]; + ASSERT(xvba_data_ctrl_buffer); + if (!xvba_data_ctrl_buffer) + return 0; + + XVBADataCtrl * const data_ctrl = xvba_data_ctrl_buffer->bufferXVBA; + const unsigned int data_offset = xvba_data_buffer->data_size_in_buffer; + + const uint8_t * const va_slice_data = ((uint8_t *)data_buffer->buffer_data + + slice_param->slice_data_offset); + + static const uint8_t start_code_prefix[3] = { 0x00, 0x00, 0x01 }; + if (memcmp(va_slice_data, start_code_prefix, sizeof(start_code_prefix)) != 0) { + append_buffer(xvba_data_buffer, start_code_prefix, sizeof(start_code_prefix)); + + uint8_t start_code_ext = 0; + if (pic_desc->picture_structure == PICT_FRAME) { + /* XXX: we only support Progressive mode at this time */ + start_code_ext = 0x0d; + } + ASSERT(start_code_ext); + append_buffer(xvba_data_buffer, &start_code_ext, 1); + } + + append_buffer(xvba_data_buffer, va_slice_data, slice_param->slice_data_size); + + /* XXX: should XVBA_DATA_CTRL_BUFFER.SliceBytesInBuffer and + SliceBitsInBuffer be required to account for padding bytes too? */ + data_ctrl->SliceDataLocation = data_offset; + data_ctrl->SliceBytesInBuffer = xvba_data_buffer->data_size_in_buffer - data_offset; + data_ctrl->SliceBitsInBuffer = 8 * data_ctrl->SliceBytesInBuffer; + pad_buffer(xvba_data_buffer); + + xvba_data_ctrl_buffer->data_size_in_buffer = sizeof(*data_ctrl); + return 1; +} + +// Translate VA buffer +typedef int +(*translate_buffer_func_t)(xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_buffer_p obj_buffer); + +typedef struct translate_buffer_info translate_buffer_info_t; +struct translate_buffer_info { + XVBACodec codec; + VABufferType type; + translate_buffer_func_t func; +}; + +int +translate_buffer( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_buffer_p obj_buffer +) +{ + static const translate_buffer_info_t translate_info[] = { +#define _(CODEC, TYPE) \ + { XVBA_CODEC_##CODEC, VA##TYPE##BufferType, \ + translate_VA##TYPE##Buffer##CODEC } + _(MPEG2, PictureParameter), + _(MPEG2, IQMatrix), + _(MPEG2, SliceParameter), + _(H264, PictureParameter), + _(H264, IQMatrix), + _(H264, SliceParameter), + _(VC1, PictureParameter), + _(VC1, SliceParameter), +#undef _ + { XVBA_CODEC_VC1, VABitPlaneBufferType, translate_nothing }, + { 0, VASliceDataBufferType, translate_VASliceDataBuffer }, + { 0, 0, NULL } + }; + const translate_buffer_info_t *tbip; + for (tbip = translate_info; tbip->func != NULL; tbip++) { + if (tbip->codec && tbip->codec != obj_context->xvba_codec) + continue; + if (tbip->type != obj_buffer->type) + continue; + return tbip->func(driver_data, obj_context, obj_buffer); + } + D(bug("ERROR: no translate function found for %s%s\n", + string_of_VABufferType(obj_buffer->type), + obj_context->xvba_codec ? string_of_XVBACodec(obj_context->xvba_codec) : NULL)); + return 0; +} + +// vaCreateBuffer +VAStatus +xvba_CreateBuffer( + VADriverContextP ctx, + VAContextID context, + VABufferType type, + unsigned int size, + unsigned int num_elements, + void *data, + VABufferID *buf_id +) +{ + XVBA_DRIVER_DATA_INIT; + + if (buf_id) + *buf_id = VA_INVALID_BUFFER; + + /* Validate type */ + switch (type) { + case VAPictureParameterBufferType: + case VAIQMatrixBufferType: + case VASliceParameterBufferType: + case VASliceDataBufferType: + case VABitPlaneBufferType: + case VAImageBufferType: + /* Ok */ + break; + default: + D(bug("ERROR: unsupported buffer type %d\n", type)); + return VA_STATUS_ERROR_UNSUPPORTED_BUFFERTYPE; + } + + object_buffer_p obj_buffer; + obj_buffer = create_va_buffer(driver_data, context, type, num_elements, size); + if (!obj_buffer) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + + if (data) + memcpy(obj_buffer->buffer_data, data, obj_buffer->buffer_size); + + if (buf_id) + *buf_id = obj_buffer->base.id; + + return VA_STATUS_SUCCESS; +} + +// vaDestroyBuffer +VAStatus +xvba_DestroyBuffer( + VADriverContextP ctx, + VABufferID buffer_id +) +{ + XVBA_DRIVER_DATA_INIT; + + object_buffer_p obj_buffer = XVBA_BUFFER(buffer_id); + + if (obj_buffer) { + if (!is_queued_buffer(driver_data, obj_buffer)) + destroy_va_buffer(driver_data, obj_buffer); + /* else, it will be destroyed in the next commit_picture() call */ + } + return VA_STATUS_SUCCESS; +} + +// vaBufferSetNumElements +VAStatus +xvba_BufferSetNumElements( + VADriverContextP ctx, + VABufferID buf_id, + unsigned int num_elements +) +{ + XVBA_DRIVER_DATA_INIT; + + object_buffer_p obj_buffer = XVBA_BUFFER(buf_id); + if (!obj_buffer) + return VA_STATUS_ERROR_INVALID_BUFFER; + + if (num_elements < 0 || num_elements > obj_buffer->max_num_elements) + return VA_STATUS_ERROR_UNKNOWN; + + obj_buffer->num_elements = num_elements; + return VA_STATUS_SUCCESS; +} + +// vaMapBuffer +VAStatus +xvba_MapBuffer( + VADriverContextP ctx, + VABufferID buf_id, + void **pbuf +) +{ + XVBA_DRIVER_DATA_INIT; + + object_buffer_p obj_buffer = XVBA_BUFFER(buf_id); + if (!obj_buffer) + return VA_STATUS_ERROR_INVALID_BUFFER; + + if (pbuf) + *pbuf = obj_buffer->buffer_data; + + if (!obj_buffer->buffer_data) + return VA_STATUS_ERROR_UNKNOWN; + + ++obj_buffer->mtime; + return VA_STATUS_SUCCESS; +} + +// vaUnmapBuffer +VAStatus +xvba_UnmapBuffer( + VADriverContextP ctx, + VABufferID buf_id +) +{ + XVBA_DRIVER_DATA_INIT; + + object_buffer_p obj_buffer = XVBA_BUFFER(buf_id); + if (!obj_buffer) + return VA_STATUS_ERROR_INVALID_BUFFER; + + ++obj_buffer->mtime; + return VA_STATUS_SUCCESS; +} + +// vaBufferInfo +VAStatus +xvba_BufferInfo( + VADriverContextP ctx, + VAContextID context, + VABufferID buf_id, + VABufferType *type, + unsigned int *size, + unsigned int *num_elements +) +{ + XVBA_DRIVER_DATA_INIT; + + object_buffer_p obj_buffer = XVBA_BUFFER(buf_id); + if (!obj_buffer) + return VA_STATUS_ERROR_INVALID_BUFFER; + + if (type) + *type = obj_buffer->type; + if (size) + *size = obj_buffer->buffer_size / obj_buffer->num_elements; + if (num_elements) + *num_elements = obj_buffer->num_elements; + return VA_STATUS_SUCCESS; +} diff --git a/src/xvba_buffer.h b/src/xvba_buffer.h new file mode 100644 index 0000000..518b724 --- /dev/null +++ b/src/xvba_buffer.h @@ -0,0 +1,129 @@ +/* + * xvba_buffer.h - XvBA backend for VA-API (VA buffers) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef XVBA_BUFFER_H +#define XVBA_BUFFER_H + +#include "xvba_driver.h" + +typedef struct object_buffer object_buffer_t; +struct object_buffer { + struct object_base base; + VAContextID va_context; + VABufferType type; + void *buffer_data; + unsigned int buffer_size; + unsigned int max_num_elements; + unsigned int num_elements; + uint64_t mtime; +}; + +// Create VA buffer object +object_buffer_p +create_va_buffer( + xvba_driver_data_t *driver_data, + VAContextID context, + VABufferType buffer_type, + unsigned int num_elements, + unsigned int size +) attribute_hidden; + +// Destroy VA buffer object +void +destroy_va_buffer( + xvba_driver_data_t *driver_data, + object_buffer_p obj_buffer +) attribute_hidden; + +// Destroy VA buffer objects stored in VA context +void +destroy_va_buffers( + xvba_driver_data_t *driver_data, + object_context_p obj_context +) attribute_hidden; + +// Determines whether BUFFER is queued for decoding +int +is_queued_buffer( + xvba_driver_data_t *driver_data, + object_buffer_p obj_buffer +) attribute_hidden; + +// Translate VA buffer +int translate_buffer( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_buffer_p obj_buffer +) attribute_hidden; + +// vaCreateBuffer +VAStatus +xvba_CreateBuffer( + VADriverContextP ctx, + VAContextID context, + VABufferType type, + unsigned int size, + unsigned int num_elements, + void *data, + VABufferID *buf_id +) attribute_hidden; + +// vaDestroyBuffer +VAStatus +xvba_DestroyBuffer( + VADriverContextP ctx, + VABufferID buffer_id +) attribute_hidden; + +// vaBufferSetNumElements +VAStatus +xvba_BufferSetNumElements( + VADriverContextP ctx, + VABufferID buf_id, + unsigned int num_elements +) attribute_hidden; + +// vaMapBuffer +VAStatus +xvba_MapBuffer( + VADriverContextP ctx, + VABufferID buf_id, + void **pbuf +) attribute_hidden; + +// vaUnmapBuffer +VAStatus +xvba_UnmapBuffer( + VADriverContextP ctx, + VABufferID buf_id +) attribute_hidden; + +// vaBufferInfo +VAStatus +xvba_BufferInfo( + VADriverContextP ctx, + VAContextID context, + VABufferID buf_id, + VABufferType *type, + unsigned int *size, + unsigned int *num_elements +) attribute_hidden; + +#endif /* XVBA_BUFFER_H */ diff --git a/src/xvba_decode.c b/src/xvba_decode.c new file mode 100644 index 0000000..5c9ec71 --- /dev/null +++ b/src/xvba_decode.c @@ -0,0 +1,716 @@ +/* + * xvba_decode.c - XvBA backend for VA-API (decoding) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "xvba_decode.h" +#include "xvba_driver.h" +#include "xvba_video.h" +#include "xvba_buffer.h" +#include "xvba_dump.h" +#include "xvba_image.h" +#include "utils.h" + +#define DEBUG 1 +#include "debug.h" + + +// NAL units in XVBA_DATA_BUFFER must be 128-byte aligned +#define XVBA_BUFFER_ALIGN 128 + +// Determines XVBA_CAPABILITY_ID from VAProfile/VAEntrypoint +static XVBA_CAPABILITY_ID +get_XVBA_CAPABILITY_ID(VAProfile profile, VAEntrypoint entrypoint) +{ + switch (profile) { + case VAProfileMPEG2Simple: + case VAProfileMPEG2Main: + switch (entrypoint) { + case VAEntrypointIDCT: return XVBA_MPEG2_IDCT; + case VAEntrypointVLD: return XVBA_MPEG2_VLD; + default: return 0; + } + return 0; + case VAProfileMPEG4Simple: + case VAProfileMPEG4AdvancedSimple: + case VAProfileMPEG4Main: + return 0; +#if VA_CHECK_VERSION(0,30,0) + case VAProfileH263Baseline: + return 0; +#endif + case VAProfileH264Baseline: + case VAProfileH264Main: + case VAProfileH264High: + switch (entrypoint) { + case VAEntrypointVLD: return XVBA_H264; + default: return 0; + } + return 0; + case VAProfileVC1Simple: + case VAProfileVC1Main: + case VAProfileVC1Advanced: + switch (entrypoint) { + case VAEntrypointVLD: return XVBA_VC1; + default: return 0; + } + return 0; +#if VA_CHECK_VERSION(0,31,0) + case VAProfileJPEGBaseline: + return 0; +#endif + } + return 0; +} + +// Translates VAProfile to XVBA_DECODE_FLAGS +static XVBA_DECODE_FLAGS get_XVBA_DECODE_FLAGS(VAProfile profile) +{ + switch (profile) { + case VAProfileMPEG2Simple: + case VAProfileMPEG2Main: + return 0; + case VAProfileMPEG4Simple: + case VAProfileMPEG4AdvancedSimple: + case VAProfileMPEG4Main: + return 0; +#if VA_CHECK_VERSION(0,30,0) + case VAProfileH263Baseline: + return 0; +#endif + case VAProfileH264Baseline: return XVBA_H264_BASELINE; + case VAProfileH264Main: return XVBA_H264_MAIN; + case VAProfileH264High: return XVBA_H264_HIGH; + case VAProfileVC1Simple: return XVBA_VC1_SIMPLE; + case VAProfileVC1Main: return XVBA_VC1_MAIN; + case VAProfileVC1Advanced: return XVBA_VC1_ADVANCED; +#if VA_CHECK_VERSION(0,31,0) + case VAProfileJPEGBaseline: + return 0; +#endif + } + return 0; +} + +// Initializes XvBA decode caps +static int ensure_decode_caps(xvba_driver_data_t *driver_data) +{ + int status; + + if (driver_data->xvba_decode_caps && driver_data->xvba_decode_caps_count) + return 1; + + if (driver_data->xvba_decode_caps) { + free(driver_data->xvba_decode_caps); + driver_data->xvba_decode_caps = NULL; + } + driver_data->xvba_decode_caps_count = 0; + + status = xvba_get_decode_caps( + driver_data->xvba_context, + &driver_data->xvba_decode_caps_count, + &driver_data->xvba_decode_caps + ); + if (status < 0) + return 0; + return 1; +} + +// Translates VAProfile/VAEntrypoint to XVBADecodeCap +static XVBADecodeCap * +get_XVBADecodeCap( + xvba_driver_data_t *driver_data, + VAProfile profile, + VAEntrypoint entrypoint +) +{ + unsigned int i; + + if (!ensure_decode_caps(driver_data)) + return NULL; + + XVBA_CAPABILITY_ID cap_id = get_XVBA_CAPABILITY_ID(profile, entrypoint); + XVBA_DECODE_FLAGS decode_flags = get_XVBA_DECODE_FLAGS(profile); + for (i = 0; i < driver_data->xvba_decode_caps_count; i++) { + XVBADecodeCap * const decode_cap = &driver_data->xvba_decode_caps[i]; + if (decode_cap->capability_id == cap_id && + decode_cap->flags == decode_flags) + return decode_cap; + } + return NULL; +} + +// Translates VABufferType to XVBA_BUFFER +static XVBA_DECODE_FLAGS get_XVBA_BUFFER(VABufferType buffer_type) +{ + switch (buffer_type) { + case VAPictureParameterBufferType: return XVBA_PICTURE_DESCRIPTION_BUFFER; + case VASliceDataBufferType: return XVBA_DATA_BUFFER; + case VASliceParameterBufferType: return XVBA_DATA_CTRL_BUFFER; + case VAIQMatrixBufferType: return XVBA_QM_BUFFER; + default: break; + } + return XVBA_NONE; +} + +// Checks decoder for profile/entrypoint is available +int +has_decoder( + xvba_driver_data_t *driver_data, + VAProfile profile, + VAEntrypoint entrypoint +) +{ + return get_XVBADecodeCap(driver_data, profile, entrypoint) != NULL; +} + +// Checks whether VA profile is implemented +static int +is_supported_profile(VAProfile profile) +{ + switch (profile) { + case VAProfileH264Baseline: + case VAProfileH264Main: + case VAProfileH264High: + case VAProfileVC1Simple: + case VAProfileVC1Main: + case VAProfileVC1Advanced: + return 1; + default: + break; + } + return 0; +} + +// Creates current XvBA decode session +VAStatus +create_decoder(xvba_driver_data_t *driver_data, object_context_p obj_context) +{ + object_config_p obj_config = XVBA_CONFIG(obj_context->va_config); + if (!obj_config) + return VA_STATUS_ERROR_INVALID_CONFIG; + + XVBADecodeCap *decode_cap; + decode_cap = get_XVBADecodeCap( + driver_data, + obj_config->profile, + obj_config->entrypoint + ); + if (!decode_cap) + return VA_STATUS_ERROR_UNKNOWN; + + XVBASession *decode_session; + decode_session = xvba_create_decode_session( + driver_data->xvba_context, + obj_context->picture_width, + obj_context->picture_height, + decode_cap + ); + if (!decode_session) + return VA_STATUS_ERROR_UNKNOWN; + + obj_context->xvba_decoder = decode_session; + obj_context->xvba_session = decode_session; + return VA_STATUS_SUCCESS; +} + +// Destroys current XvBA decode session +void +destroy_decoder(xvba_driver_data_t *driver_data, object_context_p obj_context) +{ + if (!obj_context->xvba_decoder) + return; + xvba_destroy_decode_session(obj_context->xvba_decoder); + if (obj_context->xvba_session == obj_context->xvba_decoder) + obj_context->xvba_session = NULL; + obj_context->xvba_decoder = NULL; +} + +// Allocates a new XvBA buffer +int +create_buffer( + object_context_p obj_context, + XVBABufferDescriptor **buffer_p, + XVBA_BUFFER type +) +{ + if (buffer_p) + *buffer_p = NULL; + + XVBABufferDescriptor *buffer; + buffer = xvba_create_decode_buffers(obj_context->xvba_decoder, type, 1); + if (!buffer) + return 0; + + if (buffer_p) + *buffer_p = buffer; + + buffer->appPrivate = obj_context; + buffer->size = sizeof(*buffer); + clear_buffer(buffer); + return 1; +} + +// Destroys XvBA buffer +void +destroy_buffer( + object_context_p obj_context, + XVBABufferDescriptor **buffer_p +) +{ + if (!buffer_p || !*buffer_p) + return; + + /* Something went wrong otherwise */ + ASSERT((*buffer_p)->appPrivate == obj_context); + if (!obj_context) + return; + + xvba_destroy_decode_buffers(obj_context->xvba_decoder, *buffer_p, 1); + *buffer_p = NULL; +} + +// Appends data to the specified buffer +void +append_buffer( + XVBABufferDescriptor *xvba_buffer, + const uint8_t *buf, + unsigned int buf_size +) +{ + memcpy( + ((uint8_t *)xvba_buffer->bufferXVBA + xvba_buffer->data_size_in_buffer), + buf, + buf_size + ); + xvba_buffer->data_size_in_buffer += buf_size; +} + +// Pads buffer to 128-byte boundaries +void pad_buffer(XVBABufferDescriptor *xvba_buffer) +{ + unsigned int r, align; + + if ((r = xvba_buffer->data_size_in_buffer % XVBA_BUFFER_ALIGN) != 0) { + align = XVBA_BUFFER_ALIGN - r; + ASSERT(xvba_buffer->data_size_in_buffer + align <= xvba_buffer->buffer_size); + memset(((uint8_t *)xvba_buffer->bufferXVBA + xvba_buffer->data_size_in_buffer), 0, align); + xvba_buffer->data_size_in_buffer += align; + } +} + +// Clears an XvBA buffer +void clear_buffer(XVBABufferDescriptor *xvba_buffer) +{ + if (!xvba_buffer) + return; + xvba_buffer->data_offset = 0; + xvba_buffer->data_size_in_buffer = 0; +} + +// Creates XvBA buffers associated to a surface +static VAStatus +ensure_buffer( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface, + object_buffer_p obj_buffer +) +{ + object_context_p obj_context = XVBA_CONTEXT(obj_surface->va_context); + if (!obj_context) + return VA_STATUS_ERROR_INVALID_CONTEXT; + + XVBABufferDescriptor **buffer_p = NULL; + XVBA_BUFFER xvba_buffer_type = get_XVBA_BUFFER(obj_buffer->type); + switch (obj_buffer->type) { + case VAPictureParameterBufferType: + buffer_p = &obj_surface->pic_desc_buffer; + break; + case VAIQMatrixBufferType: + buffer_p = &obj_surface->iq_matrix_buffer; + break; + case VASliceDataBufferType: + buffer_p = &obj_surface->data_buffer; + break; + case VASliceParameterBufferType: + if (realloc_buffer(&obj_surface->data_ctrl_buffers, + &obj_surface->data_ctrl_buffers_count_max, + 1 + obj_surface->data_ctrl_buffers_count, + sizeof(*obj_surface->data_ctrl_buffers)) == NULL) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + + buffer_p = &obj_surface->data_ctrl_buffers[obj_surface->data_ctrl_buffers_count++]; + break; + default: + break; + } + if (buffer_p && !*buffer_p && + !create_buffer(obj_context, buffer_p, xvba_buffer_type)) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + return VA_STATUS_SUCCESS; +} + +static VAStatus +ensure_buffers( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_surface_p obj_surface +) +{ + unsigned int i; + for (i = 0; i < obj_context->va_buffers_count; i++) { + object_buffer_p obj_buffer = XVBA_BUFFER(obj_context->va_buffers[i]); + if (!obj_buffer) + continue; + VAStatus status = ensure_buffer(driver_data, obj_surface, obj_buffer); + if (status != VA_STATUS_SUCCESS) + return status; + } + return VA_STATUS_SUCCESS; +} + +// Destroys XvBA buffers associated to a surface +void +destroy_surface_buffers( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface +) +{ + object_context_p obj_context = XVBA_CONTEXT(obj_surface->va_context); + if (!obj_context) + return; + + destroy_buffer(obj_context, &obj_surface->pic_desc_buffer); + destroy_buffer(obj_context, &obj_surface->iq_matrix_buffer); + destroy_buffer(obj_context, &obj_surface->data_buffer); + + unsigned int i; + for (i = 0; i < obj_surface->data_ctrl_buffers_count_max; i++) + destroy_buffer(obj_context, &obj_surface->data_ctrl_buffers[i]); + free(obj_surface->data_ctrl_buffers); + obj_surface->data_ctrl_buffers = NULL; + obj_surface->data_ctrl_buffers_count = 0; + obj_surface->data_ctrl_buffers_count_max = 0; +} + +// Send picture to the HW for decoding +static VAStatus +decode_picture( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_surface_p obj_surface +) +{ + XVBASession * const xvba_decoder = obj_context->xvba_decoder; + XVBABufferDescriptor *xvba_buffers[2]; + unsigned int i, n_buffers; + + if (xvba_decode_picture_start(xvba_decoder, obj_surface->xvba_surface) < 0) + return VA_STATUS_ERROR_UNKNOWN; + + n_buffers = 0; + xvba_buffers[n_buffers++] = obj_surface->pic_desc_buffer; + if (obj_surface->iq_matrix_buffer) + xvba_buffers[n_buffers++] = obj_surface->iq_matrix_buffer; + if (xvba_decode_picture(xvba_decoder, xvba_buffers, n_buffers) < 0) + return VA_STATUS_ERROR_UNKNOWN; + + for (i = 0; i < obj_surface->data_ctrl_buffers_count; i++) { + n_buffers = 0; + xvba_buffers[n_buffers++] = obj_surface->data_buffer; + xvba_buffers[n_buffers++] = obj_surface->data_ctrl_buffers[i]; + if (xvba_decode_picture(xvba_decoder, xvba_buffers, n_buffers) < 0) + return VA_STATUS_ERROR_UNKNOWN; + } + + if (xvba_decode_picture_end(xvba_decoder) < 0) + return VA_STATUS_ERROR_UNKNOWN; + + obj_surface->va_surface_status = VASurfaceRendering; + return 0; +} + +// Translate picture buffers and send it to the HW for decoding +static VAStatus +commit_picture( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_surface_p obj_surface +) +{ + VAStatus va_status = ensure_buffers(driver_data, obj_context, obj_surface); + if (va_status != VA_STATUS_SUCCESS) + return va_status; + + obj_context->data_buffer = NULL; + obj_context->slice_count = 0; + + int i, j, slice_data_is_first = -1; + for (i = 0; i < obj_context->va_buffers_count; i++) { + object_buffer_p obj_buffer = XVBA_BUFFER(obj_context->va_buffers[i]); + if (!obj_buffer) + return VA_STATUS_ERROR_INVALID_BUFFER; + + switch (obj_buffer->type) { + case VASliceDataBufferType: + if (slice_data_is_first < 0) + slice_data_is_first = 1; + break; + case VASliceParameterBufferType: + if (slice_data_is_first < 0) + slice_data_is_first = 0; + if (slice_data_is_first) { + for (j = i - 1; j >= 0; j--) { + object_buffer_p data_buffer = XVBA_BUFFER(obj_context->va_buffers[j]); + ASSERT(data_buffer); + if (data_buffer->type == VASliceDataBufferType) { + obj_context->data_buffer = data_buffer; + break; + } + } + } + else { + for (j = i + 1; j < obj_context->va_buffers_count; j++) { + object_buffer_p data_buffer = XVBA_BUFFER(obj_context->va_buffers[j]); + ASSERT(data_buffer); + if (data_buffer->type == VASliceDataBufferType) { + obj_context->data_buffer = data_buffer; + break; + } + } + } + ASSERT(obj_context->data_buffer); + break; + default: + break; + } + + if (!translate_buffer(driver_data, obj_context, obj_buffer)) + return VA_STATUS_ERROR_UNSUPPORTED_BUFFERTYPE; + } + + /* Wait for the surface to be free for decoding */ + if (sync_surface(driver_data, obj_context, obj_surface) < 0) + return VA_STATUS_ERROR_UNKNOWN; + + /* Send picture to the HW */ + return decode_picture(driver_data, obj_context, obj_surface); +} + +// vaQueryConfigProfiles +VAStatus +xvba_QueryConfigProfiles( + VADriverContextP ctx, + VAProfile *profile_list, + int *num_profiles +) +{ + XVBA_DRIVER_DATA_INIT; + + static const VAProfile va_profiles[] = { + VAProfileMPEG2Simple, + VAProfileMPEG2Main, + VAProfileH264Baseline, + VAProfileH264Main, + VAProfileH264High, + VAProfileVC1Simple, + VAProfileVC1Main, + VAProfileVC1Advanced + }; + + static const VAEntrypoint va_entrypoints[] = { + VAEntrypointVLD, + VAEntrypointIDCT + }; + + int i, j, n = 0; + for (i = 0; i < ARRAY_ELEMS(va_profiles); i++) { + VAProfile profile = va_profiles[i]; + if (!is_supported_profile(profile)) + continue; + for (j = 0; j < ARRAY_ELEMS(va_entrypoints); j++) { + VAEntrypoint entrypoint = va_entrypoints[j]; + if (has_decoder(driver_data, profile, entrypoint)) { + profile_list[n++] = profile; + break; + } + } + } + + /* If the assert fails then XVBA_MAX_PROFILES needs to be bigger */ + ASSERT(n <= XVBA_MAX_PROFILES); + if (num_profiles) + *num_profiles = n; + + return VA_STATUS_SUCCESS; +} + +// vaQueryConfigEntrypoints +VAStatus +xvba_QueryConfigEntrypoints( + VADriverContextP ctx, + VAProfile profile, + VAEntrypoint *entrypoint_list, + int *num_entrypoints +) +{ + XVBA_DRIVER_DATA_INIT; + + static const VAEntrypoint va_entrypoints[] = { + VAEntrypointVLD, + VAEntrypointIDCT + }; + + if (!is_supported_profile(profile)) + return VA_STATUS_ERROR_UNSUPPORTED_PROFILE; + + int i, n = 0; + for (i = 0; i < ARRAY_ELEMS(va_entrypoints); i++) { + const VAEntrypoint entrypoint = va_entrypoints[i]; + if (has_decoder(driver_data, profile, entrypoint)) + entrypoint_list[n++] = entrypoint; + } + + /* If the assert fails then XVBA_MAX_ENTRUYPOINTS needs to be bigger */ + ASSERT(n <= XVBA_MAX_ENTRYPOINTS); + if (num_entrypoints) + *num_entrypoints = n; + + return VA_STATUS_SUCCESS; +} + +// vaBeginPicture +VAStatus +xvba_BeginPicture( + VADriverContextP ctx, + VAContextID context, + VASurfaceID render_target +) +{ + XVBA_DRIVER_DATA_INIT; + + D(bug("vaBeginPicture(): context 0x%08x, surface 0x%08x\n", + context, render_target)); + + object_context_p obj_context = XVBA_CONTEXT(context); + if (!obj_context) + return VA_STATUS_ERROR_INVALID_CONTEXT; + + object_surface_p obj_surface = XVBA_SURFACE(render_target); + if (!obj_surface) + return VA_STATUS_ERROR_INVALID_SURFACE; + + /* Destroy any previous surface override from vaPutImage() */ + putimage_hacks_disable(driver_data, obj_surface); + + obj_context->current_render_target = obj_surface->base.id; + obj_surface->va_surface_status = VASurfaceRendering; + obj_surface->used_for_decoding = 1; + + ASSERT(!obj_context->va_buffers_count); + destroy_va_buffers(driver_data, obj_context); + + unsigned int i; + clear_buffer(obj_surface->pic_desc_buffer); + clear_buffer(obj_surface->iq_matrix_buffer); + clear_buffer(obj_surface->data_buffer); + for (i = 0; i < obj_surface->data_ctrl_buffers_count; i++) + clear_buffer(obj_surface->data_ctrl_buffers[i]); + obj_surface->data_ctrl_buffers_count = 0; + return VA_STATUS_SUCCESS; +} + +// vaRenderPicture +VAStatus +xvba_RenderPicture( + VADriverContextP ctx, + VAContextID context, + VABufferID *buffers, + int num_buffers +) +{ + XVBA_DRIVER_DATA_INIT; + int i; + + D(bug("vaRenderPicture(): context 0x%08x, %d buffers\n", + context, num_buffers)); + + object_context_p obj_context = XVBA_CONTEXT(context); + if (!obj_context) + return VA_STATUS_ERROR_INVALID_CONTEXT; + + object_surface_p obj_surface = XVBA_SURFACE(obj_context->current_render_target); + if (!obj_surface) + return VA_STATUS_ERROR_INVALID_SURFACE; + + /* Verify that we got valid buffer references */ + for (i = 0; i < num_buffers; i++) { + object_buffer_p obj_buffer = XVBA_BUFFER(buffers[i]); + if (!obj_buffer) + return VA_STATUS_ERROR_INVALID_BUFFER; + } + + /* Record buffers. They will be processed in EndPicture() */ + for (i = 0; i < num_buffers; i++) { + object_buffer_p obj_buffer = XVBA_BUFFER(buffers[i]); + + D(bug(" buffer 0x%08x\n", buffers[i])); + + VABufferID *va_buffers; + va_buffers = realloc_buffer( + &obj_context->va_buffers, + &obj_context->va_buffers_count_max, + 1 + obj_context->va_buffers_count, + sizeof(*va_buffers) + ); + if (!va_buffers) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + va_buffers[obj_context->va_buffers_count++] = obj_buffer->base.id; + } + return VA_STATUS_SUCCESS; +} + +// vaEndPicture +VAStatus +xvba_EndPicture( + VADriverContextP ctx, + VAContextID context +) +{ + XVBA_DRIVER_DATA_INIT; + + D(bug("vaEndPicture(): context 0x%08x\n", context)); + + object_context_p obj_context = XVBA_CONTEXT(context); + if (!obj_context) + return VA_STATUS_ERROR_INVALID_CONTEXT; + + object_surface_p obj_surface = XVBA_SURFACE(obj_context->current_render_target); + if (!obj_surface || !obj_surface->xvba_surface) + return VA_STATUS_ERROR_INVALID_SURFACE; + + /* Send picture bits to the HW and free VA resources (buffers) */ + VAStatus va_status = commit_picture(driver_data, obj_context, obj_surface); + + /* XXX: assume we are done with rendering right away */ + obj_context->current_render_target = VA_INVALID_SURFACE; + + destroy_va_buffers(driver_data, obj_context); + return va_status; +} diff --git a/src/xvba_decode.h b/src/xvba_decode.h new file mode 100644 index 0000000..22478a3 --- /dev/null +++ b/src/xvba_decode.h @@ -0,0 +1,130 @@ +/* + * xvba_decode.h - XvBA backend for VA-API (decoding) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef XVBA_DECODE_H +#define XVBA_DECODE_H + +#include "xvba_driver.h" + +// Checks decoder for profile/entrypoint is available +int +has_decoder( + xvba_driver_data_t *driver_data, + VAProfile profile, + VAEntrypoint entrypoint +) attribute_hidden; + +// Create XvBA decode session +VAStatus +create_decoder(xvba_driver_data_t *driver_data, object_context_p obj_context) + attribute_hidden; + +// Destroy XvBA decode session +void +destroy_decoder(xvba_driver_data_t *driver_data, object_context_p obj_context) + attribute_hidden; + +// Create XvBA buffer +int +create_buffer( + object_context_p obj_context, + XVBABufferDescriptor **buffer_p, + XVBA_BUFFER type +) attribute_hidden; + +// Destroy XvBA buffer +void +destroy_buffer( + object_context_p obj_context, + XVBABufferDescriptor **buffer_p +) attribute_hidden; + +// Append data to the XvBA buffer +void +append_buffer( + XVBABufferDescriptor *xvba_buffer, + const uint8_t *buf, + unsigned int buf_size +) attribute_hidden; + +// Pad XvBA buffer to 128-byte boundaries +void pad_buffer(XVBABufferDescriptor *xvba_buffer) + attribute_hidden; + +// Clear XvBA buffer +void clear_buffer(XVBABufferDescriptor *xvba_buffer) + attribute_hidden; + +// Create surface XvBA buffers +VAStatus +create_surface_buffers( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface +) attribute_hidden; + +// Destroy surface XvBA buffers +void +destroy_surface_buffers( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface +) attribute_hidden; + +// vaQueryConfigProfiles +VAStatus +xvba_QueryConfigProfiles( + VADriverContextP ctx, + VAProfile *profile_list, + int *num_profiles +) attribute_hidden; + +// vaQueryConfigEntrypoints +VAStatus +xvba_QueryConfigEntrypoints( + VADriverContextP ctx, + VAProfile profile, + VAEntrypoint *entrypoint_list, + int *num_entrypoints +) attribute_hidden; + +// vaBeginPicture +VAStatus +xvba_BeginPicture( + VADriverContextP ctx, + VAContextID context, + VASurfaceID render_target +) attribute_hidden; + +// vaRenderPicture +VAStatus +xvba_RenderPicture( + VADriverContextP ctx, + VAContextID context, + VABufferID *buffers, + int num_buffers +) attribute_hidden; + +// vaEndPicture +VAStatus +xvba_EndPicture( + VADriverContextP ctx, + VAContextID context +) attribute_hidden; + +#endif /* XVBA_DECODE_H */ diff --git a/src/xvba_driver.c b/src/xvba_driver.c new file mode 100644 index 0000000..12e03ab --- /dev/null +++ b/src/xvba_driver.c @@ -0,0 +1,282 @@ +/* + * xvba_driver.c - XvBA driver + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "xvba_driver.h" +#include "xvba_buffer.h" +#include "xvba_decode.h" +#include "xvba_image.h" +#include "xvba_subpic.h" +#include "xvba_video.h" +#include "xvba_video_x11.h" +#if USE_GLX +#include "xvba_video_glx.h" +#include <va/va_backend_glx.h> +#endif +#include "fglrxinfo.h" + +#define DEBUG 1 +#include "debug.h" + + +// Set display type +int xvba_set_display_type(xvba_driver_data_t *driver_data, unsigned int type) +{ + if (driver_data->va_display_type == 0) { + driver_data->va_display_type = type; + return 1; + } + return driver_data->va_display_type == type; +} + +// Destroy BUFFER objects +static void destroy_buffer_cb(object_base_p obj, void *user_data) +{ + object_buffer_p const obj_buffer = (object_buffer_p)obj; + xvba_driver_data_t * const driver_data = user_data; + + destroy_va_buffer(driver_data, obj_buffer); +} + +// Destroy object heap +typedef void (*destroy_heap_func_t)(object_base_p obj, void *user_data); + +static void +destroy_heap( + const char *name, + object_heap_p heap, + destroy_heap_func_t destroy_func, + void *user_data +) +{ + object_base_p obj; + object_heap_iterator iter; + + if (!heap) + return; + + obj = object_heap_first(heap, &iter); + while (obj) { + xvba_information_message("vaTerminate(): %s ID 0x%08x is still allocated, destroying\n", name, obj->id); + if (destroy_func) + destroy_func(obj, user_data); + else + object_heap_free(heap, obj); + obj = object_heap_next(heap, &iter); + } + object_heap_destroy(heap); +} + +#define DESTROY_HEAP(heap, func) \ + destroy_heap(#heap, &driver_data->heap##_heap, func, driver_data) + +#define CREATE_HEAP(type, id) do { \ + int result = object_heap_init(&driver_data->type##_heap, \ + sizeof(struct object_##type), \ + XVBA_##id##_ID_OFFSET); \ + ASSERT(result == 0); \ + if (result != 0) \ + return VA_STATUS_ERROR_ALLOCATION_FAILED; \ + } while (0) + +// vaTerminate +static void xvba_common_Terminate(xvba_driver_data_t *driver_data) +{ + if (driver_data->xvba_decode_caps) { + free(driver_data->xvba_decode_caps); + driver_data->xvba_decode_caps = NULL; + driver_data->xvba_decode_caps_count = 0; + } + + if (driver_data->xvba_surface_caps) { + free(driver_data->xvba_surface_caps); + driver_data->xvba_surface_caps = NULL; + driver_data->xvba_surface_caps_count = 0; + } + + DESTROY_HEAP(buffer, destroy_buffer_cb); + DESTROY_HEAP(output, NULL); + DESTROY_HEAP(image, NULL); + DESTROY_HEAP(subpicture, NULL); + DESTROY_HEAP(surface, NULL); + DESTROY_HEAP(context, NULL); + DESTROY_HEAP(config, NULL); + + if (driver_data->xvba_context) { + xvba_destroy_context(driver_data->xvba_context); + driver_data->xvba_context = NULL; + } + + xvba_gate_exit(); +} + +// vaInitialize +static VAStatus xvba_common_Initialize(xvba_driver_data_t *driver_data) +{ + int xvba_version; + int fglrx_major_version, fglrx_minor_version, fglrx_micro_version; + unsigned int device_id; + + driver_data->x11_dpy_local = XOpenDisplay(driver_data->x11_dpy_name); + if (!driver_data->x11_dpy_local) + return VA_STATUS_ERROR_UNKNOWN; + + if (!fglrx_is_dri_capable(driver_data->x11_dpy, driver_data->x11_screen)) + return VA_STATUS_ERROR_UNKNOWN; + + if (!fglrx_get_version(driver_data->x11_dpy, driver_data->x11_screen, + &fglrx_major_version, + &fglrx_minor_version, + &fglrx_micro_version)) + return VA_STATUS_ERROR_UNKNOWN; + D(bug("FGLRX driver version %d.%d.%d detected\n", + fglrx_major_version, fglrx_minor_version, fglrx_micro_version)); + + if (!fglrx_check_version(8,80,5)) { + xvba_error_message("FGLRX driver version 8.80.5 (Catalyst 10.12) or later is required\n"); + return VA_STATUS_ERROR_UNKNOWN; + } + + if (!fglrx_get_device_id(driver_data->x11_dpy, driver_data->x11_screen, + &device_id)) + return VA_STATUS_ERROR_UNKNOWN; + D(bug("FGLRX device ID 0x%04x\n", device_id)); + driver_data->device_id = device_id; + switch (device_id & 0xff00) { + case 0x6700: // Radeon HD 6000 series + case 0x6800: // Radeon HD 5000 series + D(bug("Evergreen GPU detected\n")); + driver_data->is_evergreen_gpu = 1; + break; + case 0x9800: // Fusion series + D(bug("Fusion IGP detected\n")); + driver_data->is_evergreen_gpu = 1; + driver_data->is_fusion_igp = 1; + break; + } + + if (xvba_gate_init() < 0) + return VA_STATUS_ERROR_UNKNOWN; + + if (xvba_query_extension(driver_data->x11_dpy, &xvba_version) < 0) + return VA_STATUS_ERROR_UNKNOWN; + D(bug("XvBA version %d.%d detected\n", + (xvba_version >> 16) & 0xffff, xvba_version & 0xffff)); + + if (!xvba_check_version(0,74)) { + xvba_information_message("Please upgrade to XvBA >= 0.74\n"); + return VA_STATUS_ERROR_UNIMPLEMENTED; + } + + driver_data->xvba_context = xvba_create_context(driver_data->x11_dpy, None); + if (!driver_data->xvba_context) + return VA_STATUS_ERROR_UNKNOWN; + + sprintf(driver_data->va_vendor, "%s %s - %d.%d.%d", + XVBA_STR_DRIVER_VENDOR, + XVBA_STR_DRIVER_NAME, + XVBA_VIDEO_MAJOR_VERSION, + XVBA_VIDEO_MINOR_VERSION, + XVBA_VIDEO_MICRO_VERSION); + + if (XVBA_VIDEO_PRE_VERSION > 0) { + const int len = strlen(driver_data->va_vendor); + sprintf(&driver_data->va_vendor[len], ".pre%d", XVBA_VIDEO_PRE_VERSION); + } + + CREATE_HEAP(config, CONFIG); + CREATE_HEAP(context, CONTEXT); + CREATE_HEAP(surface, SURFACE); + CREATE_HEAP(buffer, BUFFER); + CREATE_HEAP(output, OUTPUT); + CREATE_HEAP(image, IMAGE); + CREATE_HEAP(subpicture, SUBPICTURE); + + return VA_STATUS_SUCCESS; +} + +#if VA_MAJOR_VERSION == 0 && VA_MINOR_VERSION >= 31 +#define VA_INIT_VERSION_MAJOR 0 +#define VA_INIT_VERSION_MINOR 31 +#define VA_INIT_VERSION_MICRO 0 +#define VA_INIT_SUFFIX 0_31_0 +#include "xvba_driver_template.h" + +#define VA_INIT_VERSION_MAJOR 0 +#define VA_INIT_VERSION_MINOR 31 +#define VA_INIT_VERSION_MICRO 1 +#define VA_INIT_SUFFIX 0_31_1 +#define VA_INIT_GLX USE_GLX +#include "xvba_driver_template.h" + +#define VA_INIT_VERSION_MAJOR 0 +#define VA_INIT_VERSION_MINOR 31 +#define VA_INIT_VERSION_MICRO 2 +#define VA_INIT_SUFFIX 0_31_2 +#define VA_INIT_GLX USE_GLX +#include "xvba_driver_template.h" + +VAStatus __vaDriverInit_0_31(void *ctx) +{ + VADriverContextP_0_31_0 const ctx0 = ctx; + VADriverContextP_0_31_1 const ctx1 = ctx; + VADriverContextP_0_31_2 const ctx2 = ctx; + + /* Assume a NULL display implies VA-API 0.31.1 struct with the + vtable_tpi field placed just after the vtable, thus replacing + original native_dpy field */ + if (ctx0->native_dpy) + return xvba_Initialize_0_31_0(ctx); + if (ctx1->native_dpy) + return xvba_Initialize_0_31_1(ctx); + if (ctx2->native_dpy) + return xvba_Initialize_0_31_2(ctx); + return VA_STATUS_ERROR_INVALID_DISPLAY; +} +#endif + +#if VA_MAJOR_VERSION == 0 && VA_MINOR_VERSION >= 32 +#define VA_INIT_VERSION_MAJOR 0 +#define VA_INIT_VERSION_MINOR 32 +#define VA_INIT_VERSION_MICRO 0 +#define VA_INIT_SUFFIX 0_32_0 +#define VA_INIT_GLX USE_GLX +#include "xvba_driver_template.h" + +VAStatus __vaDriverInit_0_32(void *ctx) +{ + return xvba_Initialize_0_32_0(ctx); +} +#endif + +#define VA_INIT_VERSION_MAJOR VA_MAJOR_VERSION +#define VA_INIT_VERSION_MINOR VA_MINOR_VERSION +#define VA_INIT_VERSION_MICRO VA_MICRO_VERSION +#define VA_INIT_VERSION_SDS VA_SDS_VERSION +#define VA_INIT_GLX USE_GLX +#include "xvba_driver_template.h" + +VAStatus VA_DRIVER_INIT_FUNC(void *ctx) +{ +#if VA_MAJOR_VERSION == 0 && VA_MINOR_VERSION == 31 + return __vaDriverInit_0_31(ctx); +#endif + return xvba_Initialize_Current(ctx); +} diff --git a/src/xvba_driver.h b/src/xvba_driver.h new file mode 100644 index 0000000..855f8ed --- /dev/null +++ b/src/xvba_driver.h @@ -0,0 +1,110 @@ +/* + * xvba_driver.h - XvBA driver + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef XVBA_DRIVER_H +#define XVBA_DRIVER_H + +#include <va/va_backend.h> +#include "vaapi_compat.h" +#include "xvba_gate.h" +#include "object_heap.h" +#include "color_matrix.h" + +#define XVBA_DRIVER_DATA_INIT \ + struct xvba_driver_data *driver_data = \ + (struct xvba_driver_data *)ctx->pDriverData + +#define XVBA_OBJECT(id, type) \ + ((object_##type##_p)object_heap_lookup(&driver_data->type##_heap, (id))) + +#define XVBA_CONFIG(id) XVBA_OBJECT(id, config) +#define XVBA_CONTEXT(id) XVBA_OBJECT(id, context) +#define XVBA_SURFACE(id) XVBA_OBJECT(id, surface) +#define XVBA_BUFFER(id) XVBA_OBJECT(id, buffer) +#define XVBA_OUTPUT(id) XVBA_OBJECT(id, output) +#define XVBA_IMAGE(id) XVBA_OBJECT(id, image) +#define XVBA_SUBPICTURE(id) XVBA_OBJECT(id, subpicture) + +#define XVBA_CONFIG_ID_OFFSET 0x01000000 +#define XVBA_CONTEXT_ID_OFFSET 0x02000000 +#define XVBA_SURFACE_ID_OFFSET 0x03000000 +#define XVBA_BUFFER_ID_OFFSET 0x04000000 +#define XVBA_OUTPUT_ID_OFFSET 0x05000000 +#define XVBA_IMAGE_ID_OFFSET 0x06000000 +#define XVBA_SUBPICTURE_ID_OFFSET 0x07000000 + +#define XVBA_MAX_DELAYED_PICTURES 32 +#define XVBA_MAX_PROFILES 12 +#define XVBA_MAX_ENTRYPOINTS 5 +#define XVBA_MAX_CONFIG_ATTRIBUTES 10 +#define XVBA_MAX_IMAGE_FORMATS 10 +#define XVBA_MAX_DISPLAY_ATTRIBUTES 6 +#define XVBA_STR_DRIVER_VENDOR "Splitted-Desktop Systems" +#define XVBA_STR_DRIVER_NAME "XvBA backend for VA-API" + +typedef struct xvba_driver_data xvba_driver_data_t; +struct xvba_driver_data { + XVBAContext *xvba_context; + struct object_heap config_heap; + struct object_heap context_heap; + struct object_heap surface_heap; + struct object_heap buffer_heap; + struct object_heap output_heap; + struct object_heap image_heap; + struct object_heap subpicture_heap; + Display *x11_dpy; + const char *x11_dpy_name; + int x11_screen; + Display *x11_dpy_local; + XVBADecodeCap *xvba_decode_caps; + unsigned int xvba_decode_caps_count; + XVBASurfaceCap *xvba_surface_caps; + unsigned int xvba_surface_caps_count; + VADisplayAttribute *va_background_color; + VADisplayAttribute va_display_attrs[XVBA_MAX_DISPLAY_ATTRIBUTES]; + uint64_t va_display_attrs_mtime[XVBA_MAX_DISPLAY_ATTRIBUTES]; + unsigned int va_display_attrs_count; + unsigned int va_display_type; + ColorMatrix cm_brightness; + ColorMatrix cm_contrast; + ColorMatrix cm_saturation; + ColorMatrix cm_hue; + ColorMatrix cm_composite; + unsigned int cm_composite_ok; + char va_vendor[256]; + unsigned int device_id; + unsigned int is_evergreen_gpu : 1; + unsigned int is_fusion_igp : 1; + unsigned int warn_h264_over_hp_l41 : 1; + unsigned int warn_vc1_over_ap_l3 : 1; +}; + +typedef struct object_config *object_config_p; +typedef struct object_context *object_context_p; +typedef struct object_surface *object_surface_p; +typedef struct object_buffer *object_buffer_p; +typedef struct object_output *object_output_p; +typedef struct object_image *object_image_p; + +// Set display type +int xvba_set_display_type(xvba_driver_data_t *driver_data, unsigned int type) + attribute_hidden; + +#endif /* XVBA_DRIVER_H */ diff --git a/src/xvba_driver_template.h b/src/xvba_driver_template.h new file mode 100644 index 0000000..b5a7700 --- /dev/null +++ b/src/xvba_driver_template.h @@ -0,0 +1,676 @@ +/* + * xvba_driver_template.h - XvBA driver initialization template + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#undef CONCAT_ +#define CONCAT_(x, y) x##y +#undef CONCAT +#define CONCAT(x, y) CONCAT_(x, y) +#undef FUNC +#define FUNC(name) CONCAT(CONCAT(CONCAT(xvba_,name),_),VA_INIT_SUFFIX) + +#undef VA_INIT_CHECK_VERSION +#define VA_INIT_CHECK_VERSION(major, minor, micro) \ + (VA_INIT_VERSION_MAJOR > (major) || \ + (VA_INIT_VERSION_MAJOR == (major) && \ + VA_INIT_VERSION_MINOR > (minor)) || \ + (VA_INIT_VERSION_MAJOR == (major) && \ + VA_INIT_VERSION_MINOR == (minor) && \ + VA_INIT_VERSION_MICRO >= (micro))) + +#undef VA_INIT_CHECK_VERSION_SDS +#define VA_INIT_CHECK_VERSION_SDS(major, minor, micro, sds) \ + (VA_INIT_CHECK_VERSION(major, minor, (micro)+1) || \ + (VA_INIT_CHECK_VERSION(major, minor, micro) && \ + VA_INIT_VERSION_SDS >= (sds))) + +#ifndef VA_INIT_SUFFIX +#define VA_INIT_SUFFIX Current +#define VA_INIT_CURRENT 1 +#else +#define VA_INIT_CURRENT 0 +#endif + +#ifndef VA_INIT_VERSION_SDS +#define VA_INIT_VERSION_SDS 0 +#endif + +#ifndef VA_INIT_GLX +#define VA_INIT_GLX 0 +#endif + +#if VA_INIT_CURRENT +#define VA_DRIVER_VTABLE VADriverVTable +#define VA_DRIVER_CONTEXT VADriverContext +#define VA_DRIVER_CONTEXT_P VADriverContextP +#else +#define VA_DRIVER_VTABLE CONCAT(VADriverVTable_,VA_INIT_SUFFIX) +#define VA_DRIVER_VTABLE_GLX_P CONCAT(VADriverVTableGLX_,VA_INIT_SUFFIX) +#define VA_DRIVER_CONTEXT CONCAT(VADriverContext_,VA_INIT_SUFFIX) +#define VA_DRIVER_CONTEXT_P CONCAT(VADriverContextP_,VA_INIT_SUFFIX) + +typedef struct VA_DRIVER_CONTEXT *VA_DRIVER_CONTEXT_P; + +/* Driver VTable */ +struct VA_DRIVER_VTABLE { + VAStatus (*vaTerminate) ( VA_DRIVER_CONTEXT_P ctx ); + + VAStatus (*vaQueryConfigProfiles) ( + VADriverContextP ctx, + VAProfile *profile_list, /* out */ + int *num_profiles /* out */ + ); + + VAStatus (*vaQueryConfigEntrypoints) ( + VADriverContextP ctx, + VAProfile profile, + VAEntrypoint *entrypoint_list, /* out */ + int *num_entrypoints /* out */ + ); + + VAStatus (*vaGetConfigAttributes) ( + VADriverContextP ctx, + VAProfile profile, + VAEntrypoint entrypoint, + VAConfigAttrib *attrib_list, /* in/out */ + int num_attribs + ); + + VAStatus (*vaCreateConfig) ( + VADriverContextP ctx, + VAProfile profile, + VAEntrypoint entrypoint, + VAConfigAttrib *attrib_list, + int num_attribs, + VAConfigID *config_id /* out */ + ); + + VAStatus (*vaDestroyConfig) ( + VADriverContextP ctx, + VAConfigID config_id + ); + + VAStatus (*vaQueryConfigAttributes) ( + VADriverContextP ctx, + VAConfigID config_id, + VAProfile *profile, /* out */ + VAEntrypoint *entrypoint, /* out */ + VAConfigAttrib *attrib_list, /* out */ + int *num_attribs /* out */ + ); + + VAStatus (*vaCreateSurfaces) ( + VADriverContextP ctx, + int width, + int height, + int format, + int num_surfaces, + VASurfaceID *surfaces /* out */ + ); + + VAStatus (*vaDestroySurfaces) ( + VADriverContextP ctx, + VASurfaceID *surface_list, + int num_surfaces + ); + + VAStatus (*vaCreateContext) ( + VADriverContextP ctx, + VAConfigID config_id, + int picture_width, + int picture_height, + int flag, + VASurfaceID *render_targets, + int num_render_targets, + VAContextID *context /* out */ + ); + + VAStatus (*vaDestroyContext) ( + VADriverContextP ctx, + VAContextID context + ); + + VAStatus (*vaCreateBuffer) ( + VADriverContextP ctx, + VAContextID context, /* in */ + VABufferType type, /* in */ + unsigned int size, /* in */ + unsigned int num_elements, /* in */ + void *data, /* in */ + VABufferID *buf_id /* out */ + ); + + VAStatus (*vaBufferSetNumElements) ( + VADriverContextP ctx, + VABufferID buf_id, /* in */ + unsigned int num_elements /* in */ + ); + + VAStatus (*vaMapBuffer) ( + VADriverContextP ctx, + VABufferID buf_id, /* in */ + void **pbuf /* out */ + ); + + VAStatus (*vaUnmapBuffer) ( + VADriverContextP ctx, + VABufferID buf_id /* in */ + ); + + VAStatus (*vaDestroyBuffer) ( + VADriverContextP ctx, + VABufferID buffer_id + ); + + VAStatus (*vaBeginPicture) ( + VADriverContextP ctx, + VAContextID context, + VASurfaceID render_target + ); + + VAStatus (*vaRenderPicture) ( + VADriverContextP ctx, + VAContextID context, + VABufferID *buffers, + int num_buffers + ); + + VAStatus (*vaEndPicture) ( + VADriverContextP ctx, + VAContextID context + ); + + VAStatus (*vaSyncSurface) ( + VADriverContextP ctx, + VASurfaceID render_target + ); + + VAStatus (*vaQuerySurfaceStatus) ( + VADriverContextP ctx, + VASurfaceID render_target, + VASurfaceStatus *status /* out */ + ); + +#if VA_INIT_CHECK_VERSION(0,31,2) + VAStatus (*vaQuerySurfaceError) ( + VADriverContextP ctx, + VASurfaceID render_target, + VAStatus error_status, + void **error_info /*out*/ + ); +#endif + + VAStatus (*vaPutSurface) ( + VADriverContextP ctx, + VASurfaceID surface, + VADrawable draw, /* X Drawable */ + short srcx, + short srcy, + unsigned short srcw, + unsigned short srch, + short destx, + short desty, + unsigned short destw, + unsigned short desth, + VARectangle *cliprects, /* client supplied clip list */ + unsigned int number_cliprects, /* number of clip rects in the clip list */ + unsigned int flags /* de-interlacing flags */ + ); + + VAStatus (*vaQueryImageFormats) ( + VADriverContextP ctx, + VAImageFormat *format_list, /* out */ + int *num_formats /* out */ + ); + + VAStatus (*vaCreateImage) ( + VADriverContextP ctx, + VAImageFormat *format, + int width, + int height, + VAImage *image /* out */ + ); + + VAStatus (*vaDeriveImage) ( + VADriverContextP ctx, + VASurfaceID surface, + VAImage *image /* out */ + ); + + VAStatus (*vaDestroyImage) ( + VADriverContextP ctx, + VAImageID image + ); + + VAStatus (*vaSetImagePalette) ( + VADriverContextP ctx, + VAImageID image, + /* + * pointer to an array holding the palette data. The size of the array is + * num_palette_entries * entry_bytes in size. The order of the components + * in the palette is described by the component_order in VAImage struct + */ + unsigned char *palette + ); + + VAStatus (*vaGetImage) ( + VADriverContextP ctx, + VASurfaceID surface, + int x, /* coordinates of the upper left source pixel */ + int y, + unsigned int width, /* width and height of the region */ + unsigned int height, + VAImageID image + ); + + VAStatus (*vaPutImage) ( + VADriverContextP ctx, + VASurfaceID surface, + VAImageID image, + int src_x, + int src_y, + unsigned int src_width, + unsigned int src_height, + int dest_x, + int dest_y, + unsigned int dest_width, + unsigned int dest_height + ); + + VAStatus (*vaQuerySubpictureFormats) ( + VADriverContextP ctx, + VAImageFormat *format_list, /* out */ + unsigned int *flags, /* out */ + unsigned int *num_formats /* out */ + ); + + VAStatus (*vaCreateSubpicture) ( + VADriverContextP ctx, + VAImageID image, + VASubpictureID *subpicture /* out */ + ); + + VAStatus (*vaDestroySubpicture) ( + VADriverContextP ctx, + VASubpictureID subpicture + ); + + VAStatus (*vaSetSubpictureImage) ( + VADriverContextP ctx, + VASubpictureID subpicture, + VAImageID image + ); + + VAStatus (*vaSetSubpictureChromakey) ( + VADriverContextP ctx, + VASubpictureID subpicture, + unsigned int chromakey_min, + unsigned int chromakey_max, + unsigned int chromakey_mask + ); + + VAStatus (*vaSetSubpictureGlobalAlpha) ( + VADriverContextP ctx, + VASubpictureID subpicture, + float global_alpha + ); + + VAStatus (*vaAssociateSubpicture) ( + VADriverContextP ctx, + VASubpictureID subpicture, + VASurfaceID *target_surfaces, + int num_surfaces, + short src_x, /* upper left offset in subpicture */ + short src_y, + unsigned short src_width, + unsigned short src_height, + short dest_x, /* upper left offset in surface */ + short dest_y, + unsigned short dest_width, + unsigned short dest_height, + /* + * whether to enable chroma-keying or global-alpha + * see VA_SUBPICTURE_XXX values + */ + unsigned int flags + ); + + VAStatus (*vaDeassociateSubpicture) ( + VADriverContextP ctx, + VASubpictureID subpicture, + VASurfaceID *target_surfaces, + int num_surfaces + ); + + VAStatus (*vaQueryDisplayAttributes) ( + VADriverContextP ctx, + VADisplayAttribute *attr_list, /* out */ + int *num_attributes /* out */ + ); + + VAStatus (*vaGetDisplayAttributes) ( + VADriverContextP ctx, + VADisplayAttribute *attr_list, /* in/out */ + int num_attributes + ); + + VAStatus (*vaSetDisplayAttributes) ( + VADriverContextP ctx, + VADisplayAttribute *attr_list, + int num_attributes + ); + +#if VA_INIT_CHECK_VERSION(0,31,1) + /* used by va trace */ + VAStatus (*vaBufferInfo) ( + VADriverContextP ctx, + VAContextID context, /* in */ + VABufferID buf_id, /* in */ + VABufferType *type, /* out */ + unsigned int *size, /* out */ + unsigned int *num_elements /* out */ + ); + + /* lock/unlock surface for external access */ + VAStatus (*vaLockSurface) ( + VADriverContextP ctx, + VASurfaceID surface, + unsigned int *fourcc, /* out for follow argument */ + unsigned int *luma_stride, + unsigned int *chroma_u_stride, + unsigned int *chroma_v_stride, + unsigned int *luma_offset, + unsigned int *chroma_u_offset, + unsigned int *chroma_v_offset, + unsigned int *buffer_name, /* if it is not NULL, assign the low lever + * surface buffer name + */ + void **buffer /* if it is not NULL, map the surface buffer for + * CPU access + */ + ); + + VAStatus (*vaUnlockSurface) ( + VADriverContextP ctx, + VASurfaceID surface + ); + +#if !VA_INIT_CHECK_VERSION(0,32,0) + /* Optional: GLX support hooks */ + struct VADriverVTableGLX *glx; +#endif +#else + /* device specific */ + VAStatus (*vaCreateSurfaceFromCIFrame) ( + VADriverContextP ctx, + unsigned long frame_id, + VASurfaceID *surface /* out */ + ); + + + VAStatus (*vaCreateSurfaceFromV4L2Buf) ( + VADriverContextP ctx, + int v4l2_fd, /* file descriptor of V4L2 device */ + struct v4l2_format *v4l2_fmt, /* format of V4L2 */ + struct v4l2_buffer *v4l2_buf, /* V4L2 buffer */ + VASurfaceID *surface /* out */ + ); + + VAStatus (*vaCopySurfaceToBuffer) ( + VADriverContextP ctx, + VASurfaceID surface, + unsigned int *fourcc, /* out for follow argument */ + unsigned int *luma_stride, + unsigned int *chroma_u_stride, + unsigned int *chroma_v_stride, + unsigned int *luma_offset, + unsigned int *chroma_u_offset, + unsigned int *chroma_v_offset, + void **buffer + ); +#endif +}; + +/* Driver context */ +struct VA_DRIVER_CONTEXT { + void *pDriverData; +#if VA_INIT_CHECK_VERSION(0,32,0) + struct VA_DRIVER_VTABLE *vtable; + struct VADriverVTableGLX *vtable_glx; + struct VADriverVTableEGL *vtable_egl; +#else + struct VA_DRIVER_VTABLE vtable; +#endif +#if VA_INIT_CHECK_VERSION(0,31,1) + void *vtable_tpi; /* the structure is malloc-ed */ +#endif + + Display *native_dpy; + int x11_screen; + int version_major; + int version_minor; + int max_profiles; + int max_entrypoints; + int max_attributes; + int max_image_formats; + int max_subpic_formats; + int max_display_attributes; + const char *str_vendor; + + void *handle; /* dlopen handle */ + + void *dri_state; +#if VA_INIT_CHECK_VERSION(0,31,1) + void *glx; /* opaque for GLX code */ +#endif +}; +#endif + +// Check for VA/GLX changes from libVA API >= 0.31.0-sds2 +#if VA_INIT_GLX +#if VA_INIT_CHECK_VERSION_SDS(0,31,0,2) +typedef struct VADriverVTableGLX *VA_DRIVER_VTABLE_GLX_P; +#else +typedef struct VA_DRIVER_VTABLE *VA_DRIVER_VTABLE_GLX_P; +#endif + +static inline VA_DRIVER_VTABLE_GLX_P FUNC(GetVTableGLX)(VA_DRIVER_CONTEXT_P ctx) +{ +#if VA_INIT_CHECK_VERSION_SDS(0,31,0,6) +#if VA_INIT_CHECK_VERSION(0,32,0) + /* Upstream VA-API 0.32 */ + VA_DRIVER_VTABLE_GLX_P *p_vtable_glx = &ctx->vtable_glx; +#else + /* Upstream VA-API 0.31.1 or SDS >= 0.31.0-sds6 */ + VA_DRIVER_VTABLE_GLX_P *p_vtable_glx = &ctx->vtable.glx; +#endif + VA_DRIVER_VTABLE_GLX_P vtable_glx = *p_vtable_glx; + + if (!vtable_glx) { + vtable_glx = calloc(1, sizeof(*vtable_glx)); + if (!vtable_glx) + return NULL; + *p_vtable_glx = vtable_glx; + } + return vtable_glx; +#elif VA_INIT_CHECK_VERSION_SDS(0,31,0,2) + /* SDS >= 0.31.0-sds2 */ + return &ctx->vtable.glx; +#else + /* Any other VA-API version 0.31.0 or lower */ + return &ctx->vtable; +#endif +} + +static inline void FUNC(ReleaseVTableGLX)(VA_DRIVER_CONTEXT_P ctx) +{ +#if VA_INIT_CHECK_VERSION(0,32,0) + free(ctx->vtable_glx); + ctx->vtable_glx = NULL; +#elif VA_INIT_CHECK_VERSION_SDS(0,31,0,6) + free(ctx->vtable.glx); + ctx->vtable.glx = NULL; +#endif +} +#endif + +static VAStatus FUNC(Terminate)(VA_DRIVER_CONTEXT_P ctx) +{ + XVBA_DRIVER_DATA_INIT; + + xvba_common_Terminate(driver_data); + +#if VA_INIT_GLX + FUNC(ReleaseVTableGLX)(ctx); +#endif + + free(ctx->pDriverData); + ctx->pDriverData = NULL; + + return VA_STATUS_SUCCESS; +} + +static VAStatus FUNC(Initialize)(VA_DRIVER_CONTEXT_P ctx) +{ + struct xvba_driver_data *driver_data; + + driver_data = calloc(1, sizeof(*driver_data)); + if (!driver_data) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + + ctx->pDriverData = driver_data; + driver_data->x11_dpy = ctx->native_dpy; + driver_data->x11_screen = ctx->x11_screen; + + VAStatus va_status = xvba_common_Initialize(driver_data); + if (va_status != VA_STATUS_SUCCESS) { + FUNC(Terminate)(ctx); + return va_status; + } + + ctx->version_major = VA_INIT_VERSION_MAJOR; + ctx->version_minor = VA_INIT_VERSION_MINOR; + ctx->max_profiles = XVBA_MAX_PROFILES; + ctx->max_entrypoints = XVBA_MAX_ENTRYPOINTS; + ctx->max_attributes = XVBA_MAX_CONFIG_ATTRIBUTES; + ctx->max_image_formats = XVBA_MAX_IMAGE_FORMATS; + ctx->max_subpic_formats = XVBA_MAX_SUBPICTURE_FORMATS; + ctx->max_display_attributes = XVBA_MAX_DISPLAY_ATTRIBUTES; + ctx->str_vendor = driver_data->va_vendor; + + struct VA_DRIVER_VTABLE *vtable; +#if VA_INIT_CHECK_VERSION(0,32,0) + vtable = ctx->vtable; +#else + vtable = &ctx->vtable; +#endif + memset(vtable, 0, sizeof(*vtable)); + vtable->vaTerminate = FUNC(Terminate); + vtable->vaQueryConfigEntrypoints = xvba_QueryConfigEntrypoints; + vtable->vaQueryConfigProfiles = xvba_QueryConfigProfiles; + vtable->vaQueryConfigEntrypoints = xvba_QueryConfigEntrypoints; + vtable->vaQueryConfigAttributes = xvba_QueryConfigAttributes; + vtable->vaCreateConfig = xvba_CreateConfig; + vtable->vaDestroyConfig = xvba_DestroyConfig; + vtable->vaGetConfigAttributes = xvba_GetConfigAttributes; + vtable->vaCreateSurfaces = xvba_CreateSurfaces; + vtable->vaDestroySurfaces = xvba_DestroySurfaces; + vtable->vaCreateContext = xvba_CreateContext; + vtable->vaDestroyContext = xvba_DestroyContext; + vtable->vaCreateBuffer = xvba_CreateBuffer; + vtable->vaBufferSetNumElements = xvba_BufferSetNumElements; + vtable->vaMapBuffer = xvba_MapBuffer; + vtable->vaUnmapBuffer = xvba_UnmapBuffer; + vtable->vaDestroyBuffer = xvba_DestroyBuffer; + vtable->vaBeginPicture = xvba_BeginPicture; + vtable->vaRenderPicture = xvba_RenderPicture; + vtable->vaEndPicture = xvba_EndPicture; +#if VA_INIT_CHECK_VERSION(0,31,0) + vtable->vaSyncSurface = xvba_SyncSurface2; +#else + vtable->vaSyncSurface = xvba_SyncSurface3; +#endif + vtable->vaQuerySurfaceStatus = xvba_QuerySurfaceStatus; + vtable->vaPutSurface = xvba_PutSurface; + vtable->vaQueryImageFormats = xvba_QueryImageFormats; + vtable->vaCreateImage = xvba_CreateImage; + vtable->vaDeriveImage = xvba_DeriveImage; + vtable->vaDestroyImage = xvba_DestroyImage; + vtable->vaSetImagePalette = xvba_SetImagePalette; + vtable->vaGetImage = xvba_GetImage; +#if VA_INIT_CHECK_VERSION(0,31,0) + vtable->vaPutImage = xvba_PutImage_full; +#else + vtable->vaPutImage = xvba_PutImage; + vtable->vaPutImage2 = xvba_PutImage_full; +#endif + vtable->vaQuerySubpictureFormats = xvba_QuerySubpictureFormats; + vtable->vaCreateSubpicture = xvba_CreateSubpicture; + vtable->vaDestroySubpicture = xvba_DestroySubpicture; + vtable->vaSetSubpictureImage = xvba_SetSubpictureImage; + vtable->vaSetSubpictureChromakey = xvba_SetSubpictureChromakey; + vtable->vaSetSubpictureGlobalAlpha = xvba_SetSubpictureGlobalAlpha; +#if VA_INIT_CHECK_VERSION(0,31,0) + vtable->vaAssociateSubpicture = xvba_AssociateSubpicture_full; +#else + vtable->vaAssociateSubpicture = xvba_AssociateSubpicture; + vtable->vaAssociateSubpicture2 = xvba_AssociateSubpicture_full; +#endif + vtable->vaDeassociateSubpicture = xvba_DeassociateSubpicture; + vtable->vaQueryDisplayAttributes = xvba_QueryDisplayAttributes; + vtable->vaGetDisplayAttributes = xvba_GetDisplayAttributes; + vtable->vaSetDisplayAttributes = xvba_SetDisplayAttributes; +#if VA_INIT_CHECK_VERSION(0,31,1) + vtable->vaBufferInfo = xvba_BufferInfo; + vtable->vaLockSurface = xvba_LockSurface; + vtable->vaUnlockSurface = xvba_UnlockSurface; +#else +#if VA_INIT_CHECK_VERSION(0,30,0) + vtable->vaCreateSurfaceFromCIFrame = xvba_CreateSurfaceFromCIFrame; + vtable->vaCreateSurfaceFromV4L2Buf = xvba_CreateSurfaceFromV4L2Buf; + vtable->vaCopySurfaceToBuffer = xvba_CopySurfaceToBuffer; +#else + vtable->vaSetSubpicturePalette = xvba_SetSubpicturePalette; + vtable->vaDbgCopySurfaceToBuffer = xvba_DbgCopySurfaceToBuffer; +#endif +#endif + +#if VA_INIT_GLX + VA_DRIVER_VTABLE_GLX_P const glx_vtable = FUNC(GetVTableGLX)(ctx); + if (!glx_vtable) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + glx_vtable->vaCreateSurfaceGLX = xvba_CreateSurfaceGLX; + glx_vtable->vaDestroySurfaceGLX = xvba_DestroySurfaceGLX; + glx_vtable->vaCopySurfaceGLX = xvba_CopySurfaceGLX; +#endif + return VA_STATUS_SUCCESS; +} + +#undef VA_INIT_CURRENT +#undef VA_INIT_VERSION_MAJOR +#undef VA_INIT_VERSION_MINOR +#undef VA_INIT_VERSION_MICRO +#undef VA_INIT_VERSION_SDS +#undef VA_INIT_SUFFIX +#undef VA_INIT_GLX + +#undef VA_DRIVER_VTABLE +#undef VA_DRIVER_VTABLE_GLX_P +#undef VA_DRIVER_CONTEXT +#undef VA_DRIVER_CONTEXT_P diff --git a/src/xvba_dump.c b/src/xvba_dump.c new file mode 100644 index 0000000..5b63950 --- /dev/null +++ b/src/xvba_dump.c @@ -0,0 +1,705 @@ +/* + * xvba_dump.c - Dump utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "xvba_dump.h" + +#define DEBUG 1 +#include "debug.h" + + +// Returns string representation of FOURCC +const char *string_of_FOURCC(uint32_t fourcc) +{ + static char str[5]; + str[0] = fourcc; + str[1] = fourcc >> 8; + str[2] = fourcc >> 16; + str[3] = fourcc >> 24; + str[4] = '\0'; + return str; +} + +// Returns string representation of VA surface format +const char *string_of_VAConfigAttribRTFormat(unsigned int format) +{ + switch (format) { +#define FORMAT(FORMAT) \ + case VA_RT_FORMAT_##FORMAT: return #FORMAT + FORMAT(YUV420); + FORMAT(YUV422); + FORMAT(YUV444); + } + return "<unknown>"; +} + +// Returns string representation of XVBA_SURFACE_FLAG +const char *string_of_XVBA_SURFACE_FLAG(XVBA_SURFACE_FLAG flag) +{ + switch (flag) { +#define FLAG(flag) \ + case XVBA_##flag: return "XVBA_" #flag + FLAG(FRAME); + FLAG(TOP_FIELD); + FLAG(BOTTOM_FIELD); +#undef FLAG + } + return "<unknown>"; +} + +// Returns string representation of XVBA_BUFFER +const char *string_of_XVBA_BUFFER(XVBA_BUFFER buffer_type) +{ + switch (buffer_type) { +#define BUFFER_TYPE(t) \ + case XVBA_##t##_BUFFER: return "XVBA_" #t "_BUFFER" + BUFFER_TYPE(PICTURE_DESCRIPTION); + BUFFER_TYPE(QM); + BUFFER_TYPE(DATA); + BUFFER_TYPE(DATA_CTRL); +#undef BUFFER_TYPE + } + return "<unknown>"; +} + +// Returns string representation of XVBA_CAPABILITY_ID +const char *string_of_XVBA_CAPABILITY_ID(XVBA_CAPABILITY_ID cap_id) +{ + switch (cap_id) { +#define CAP_ID(cap_id) \ + case XVBA_##cap_id: return "XVBA_" #cap_id + CAP_ID(H264); + CAP_ID(VC1); + CAP_ID(MPEG2_IDCT); + CAP_ID(MPEG2_VLD); +#undef CAP_ID + } + return "<unknown>"; +} + +// Returns string representation of XVBA_DECODE_FLAGS +const char *string_of_XVBA_DECODE_FLAGS(XVBA_DECODE_FLAGS flag) +{ + switch (flag) { +#define FLAG(flag) \ + case XVBA_##flag: return "XVBA_" #flag + FLAG(NOFLAG); + FLAG(H264_BASELINE); + FLAG(H264_MAIN); + FLAG(H264_HIGH); + FLAG(VC1_SIMPLE); + FLAG(VC1_MAIN); + FLAG(VC1_ADVANCED); +#undef FLAG + } + return "<unknown>"; +} + +// Returns string representation of XVBACodec +const char *string_of_XVBACodec(XVBACodec codec) +{ + const char *str = NULL; + switch (codec) { +#define _(X) case XVBA_CODEC_##X: str = #X; break + _(MPEG1); + _(MPEG2); + _(MPEG4); + _(H264); + _(VC1); +#undef _ + } + return str; +} + +// Returns string representation of VABufferType +const char *string_of_VABufferType(VABufferType type) +{ + const char *str = NULL; + switch (type) { +#define _(X) case X: str = #X; break + _(VAPictureParameterBufferType); + _(VAIQMatrixBufferType); + _(VABitPlaneBufferType); + _(VASliceGroupMapBufferType); + _(VASliceParameterBufferType); + _(VASliceDataBufferType); + _(VAMacroblockParameterBufferType); + _(VAResidualDataBufferType); + _(VADeblockingParameterBufferType); + _(VAImageBufferType); +#if VA_CHECK_VERSION(0,30,0) + _(VAProtectedSliceDataBufferType); + _(VAEncCodedBufferType); + _(VAEncSequenceParameterBufferType); + _(VAEncPictureParameterBufferType); + _(VAEncSliceParameterBufferType); + _(VAEncH264VUIBufferType); + _(VAEncH264SEIBufferType); +#endif +#undef _ + } + return str; +} + +#if USE_TRACER +#define TRACE trace_print +#define INDENT(INC) trace_indent(INC) +#define DUMPi(S, M) TRACE("." #M " = %d,\n", S->M) +#define DUMPx(S, M) TRACE("." #M " = 0x%08x,\n", S->M) +#define DUMPp(S, M) TRACE("." #M " = %p,\n", S->M) +#define DUMPm(S, M, I, J) dump_matrix_NxM(#M, (uint8_t *)S->M, I, J, I * J) +#else +#define trace_enabled() (0) +#define do_nothing() do { } while (0) +#define TRACE(FORMAT,...) do_nothing() +#define INDENT(INC) do_nothing() +#define DUMPi(S, M) do_nothing() +#define DUMPx(S, M) do_nothing() +#define DUMPp(S, M) do_nothing() +#define DUMPm(S, M, I, J) do_nothing() +#endif + +// Dumps matrix[N][M] = N rows x M columns (uint8_t) +static void +dump_matrix_NxM(const char *label, uint8_t *matrix, int N, int M, int L) +{ + int i, j, n = 0; + + TRACE(".%s = {\n", label); + INDENT(1); + for (j = 0; j < N; j++) { + for (i = 0; i < M; i++, n++) { + if (n >= L) + break; + if (i > 0) + TRACE(", "); + TRACE("0x%02x", matrix[n]); + } + if (j < (N - 1)) + TRACE(","); + TRACE("\n"); + if (n >= L) + break; + } + INDENT(-1); + TRACE("}\n"); +} + +// Dump XVBAPictureDescriptor buffer +static void +dump_XVBABufferDescriptor__XVBA_PICTURE_DESCRIPTION_BUFFER( + XVBABufferDescriptor *buffer +) +{ + if (!buffer) + return; + + object_context_p obj_context = buffer->appPrivate; + ASSERT(obj_context); + if (!obj_context) + return; + + XVBAPictureDescriptor *pic_desc = buffer->bufferXVBA; + if (!pic_desc) + return; + + DUMPp(pic_desc, past_surface); + DUMPp(pic_desc, future_surface); + DUMPi(pic_desc, profile); + DUMPi(pic_desc, level); + DUMPi(pic_desc, width_in_mb); + DUMPi(pic_desc, height_in_mb); + DUMPi(pic_desc, picture_structure); + + switch (obj_context->xvba_codec) { + case XVBA_CODEC_H264: + TRACE(".sps_info.flags = 0x%08x, /* %d:%d:%d:%d:%d:%d */\n", + pic_desc->sps_info.flags, + pic_desc->sps_info.avc.residual_colour_transform_flag, + pic_desc->sps_info.avc.delta_pic_always_zero_flag, + pic_desc->sps_info.avc.gaps_in_frame_num_value_allowed_flag, + pic_desc->sps_info.avc.frame_mbs_only_flag, + pic_desc->sps_info.avc.mb_adaptive_frame_field_flag, + pic_desc->sps_info.avc.direct_8x8_inference_flag); + break; + case XVBA_CODEC_VC1: + TRACE(".sps_info.flags = 0x%08x, /* %d:%d:%d:%d:%d:%d:%d */\n", + pic_desc->sps_info.flags, + pic_desc->sps_info.vc1.postprocflag, + pic_desc->sps_info.vc1.pulldown, + pic_desc->sps_info.vc1.interlace, + pic_desc->sps_info.vc1.tfcntrflag, + pic_desc->sps_info.vc1.finterpflag, + pic_desc->sps_info.vc1.psf, + pic_desc->sps_info.vc1.second_field); + break; + default: + break; + } + + DUMPi(pic_desc, chroma_format); + + if (obj_context->xvba_codec == XVBA_CODEC_H264) { + DUMPi(pic_desc, avc_bit_depth_luma_minus8); + DUMPi(pic_desc, avc_bit_depth_chroma_minus8); + DUMPi(pic_desc, avc_log2_max_frame_num_minus4); + DUMPi(pic_desc, avc_pic_order_cnt_type); + DUMPi(pic_desc, avc_log2_max_pic_order_cnt_lsb_minus4); + DUMPi(pic_desc, avc_num_ref_frames); + } + + switch (obj_context->xvba_codec) { + case XVBA_CODEC_H264: + TRACE(".pps_info.flags = 0x%08x, /* %d:%d:%d:%d:%d:%d:%d:%d */\n", + pic_desc->pps_info.flags, + pic_desc->pps_info.avc.entropy_coding_mode_flag, + pic_desc->pps_info.avc.pic_order_present_flag, + pic_desc->pps_info.avc.weighted_pred_flag, + pic_desc->pps_info.avc.weighted_bipred_idc, + pic_desc->pps_info.avc.deblocking_filter_control_present_flag, + pic_desc->pps_info.avc.constrained_intra_pred_flag, + pic_desc->pps_info.avc.redundant_pic_cnt_present_flag, + pic_desc->pps_info.avc.transform_8x8_mode_flag); + break; + case XVBA_CODEC_VC1: + TRACE(".pps_info.flags = 0x%08x, /* %d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d */\n", + pic_desc->pps_info.flags, + pic_desc->pps_info.vc1.panscan_flag, + pic_desc->pps_info.vc1.refdist_flag, + pic_desc->pps_info.vc1.loopfilter, + pic_desc->pps_info.vc1.fastuvmc, + pic_desc->pps_info.vc1.extended_mv, + pic_desc->pps_info.vc1.dquant, + pic_desc->pps_info.vc1.vstransform, + pic_desc->pps_info.vc1.overlap, + pic_desc->pps_info.vc1.quantizer, + pic_desc->pps_info.vc1.extended_dmv, + pic_desc->pps_info.vc1.maxbframes, + pic_desc->pps_info.vc1.rangered, + pic_desc->pps_info.vc1.syncmarker, + pic_desc->pps_info.vc1.multires, + pic_desc->pps_info.vc1.range_mapy_flag, + pic_desc->pps_info.vc1.range_mapy, + pic_desc->pps_info.vc1.range_mapuv_flag, + pic_desc->pps_info.vc1.range_mapuv); + break; + default: + break; + } + + if (obj_context->xvba_codec == XVBA_CODEC_H264) { + DUMPi(pic_desc, avc_num_slice_groups_minus1); + DUMPi(pic_desc, avc_slice_group_map_type); + DUMPi(pic_desc, avc_num_ref_idx_l0_active_minus1); + DUMPi(pic_desc, avc_num_ref_idx_l1_active_minus1); + + DUMPi(pic_desc, avc_pic_init_qp_minus26); + DUMPi(pic_desc, avc_pic_init_qs_minus26); + DUMPi(pic_desc, avc_chroma_qp_index_offset); + DUMPi(pic_desc, avc_second_chroma_qp_index_offset); + + DUMPi(pic_desc, avc_slice_group_change_rate_minus1); + + DUMPi(pic_desc, avc_curr_field_order_cnt_list[0]); + DUMPi(pic_desc, avc_curr_field_order_cnt_list[1]); + } + + DUMPi(pic_desc, avc_frame_num); + DUMPi(pic_desc, avc_intra_flag); + DUMPi(pic_desc, avc_reference); +} + +// Dump data buffer +static void +dump_XVBABufferDescriptor__XVBA_DATA_BUFFER(XVBABufferDescriptor *buffer) +{ + if (!buffer) + return; + + uint8_t *data = buffer->bufferXVBA; + if (!data) + return; + + dump_matrix_NxM("data", data + buffer->data_offset, 4, 12, buffer->data_size_in_buffer); +} + +// Dump XVBADataCtrl buffer +static void +dump_XVBABufferDescriptor__XVBA_DATA_CTRL_BUFFER(XVBABufferDescriptor *buffer) +{ + if (!buffer) + return; + + XVBADataCtrl *data_ctrl = buffer->bufferXVBA; + if (!data_ctrl) + return; + + DUMPi(data_ctrl, SliceBitsInBuffer); + DUMPi(data_ctrl, SliceDataLocation); + DUMPi(data_ctrl, SliceBytesInBuffer); +} + +// Dump quantization matrix buffer (H.264 only for now) +static void +dump_XVBABufferDescriptor__XVBA_QM_BUFFER(XVBABufferDescriptor *buffer) +{ + if (!buffer) + return; + + object_context_p obj_context = buffer->appPrivate; + ASSERT(obj_context); + if (!obj_context) + return; + + if (obj_context->xvba_codec != XVBA_CODEC_H264) + return; + + XVBAQuantMatrixAvc *qm = buffer->bufferXVBA; + if (!qm) + return; + + DUMPm(qm, bScalingLists4x4, 6, 16); + DUMPm(qm, bScalingLists8x8[0], 8, 8); + DUMPm(qm, bScalingLists8x8[1], 8, 8); +} + +// Dump XVBABufferDescriptor +static void dump_XVBABufferDescriptor(XVBABufferDescriptor *buffer) +{ + if (!buffer) + return; + + TRACE("%s = {\n", string_of_XVBA_BUFFER(buffer->buffer_type)); + INDENT(1); + DUMPi(buffer, buffer_size); + DUMPp(buffer, bufferXVBA); + DUMPi(buffer, data_size_in_buffer); + DUMPi(buffer, data_offset); + switch (buffer->buffer_type) { + case XVBA_PICTURE_DESCRIPTION_BUFFER: + dump_XVBABufferDescriptor__XVBA_PICTURE_DESCRIPTION_BUFFER(buffer); + break; + case XVBA_DATA_BUFFER: + dump_XVBABufferDescriptor__XVBA_DATA_BUFFER(buffer); + break; + case XVBA_DATA_CTRL_BUFFER: + dump_XVBABufferDescriptor__XVBA_DATA_CTRL_BUFFER(buffer); + break; + case XVBA_QM_BUFFER: + dump_XVBABufferDescriptor__XVBA_QM_BUFFER(buffer); + break; + default: + break; + } + INDENT(-1); + TRACE("};\n"); +} + +// Dumps XVBACreateContext() +void dump_XVBACreateContext(void *input_arg) +{ + XVBA_Create_Context_Input * const input = input_arg; + + if (trace_enabled()) + TRACE("XVBACreateContext(): display %p, drawable 0x%08x\n", + input->display, + input->draw); +} + +void dump_XVBACreateContext_output(void *output_arg) +{ + XVBA_Create_Context_Output * const output = output_arg; + + if (trace_enabled()) + TRACE("XVBACreateContext(): -> context %p\n", output->context); +} + +// Dumps XVBADestroyContext() +void dump_XVBADestroyContext(void *context) +{ + if (trace_enabled()) + TRACE("XVBADestroyContext(): context %p\n", context); +} + +// Dumps XVBACreateSurface() +void dump_XVBACreateSurface(void *input_arg) +{ + XVBA_Create_Surface_Input * const input = input_arg; + + if (trace_enabled()) + TRACE("XVBACreateSurface(): session %p, size %ux%u, format %s\n", + input->session, + input->width, + input->height, + string_of_FOURCC(input->surface_type)); +} + +void dump_XVBACreateSurface_output(void *output_arg) +{ + XVBA_Create_Surface_Output * const output = output_arg; + + if (trace_enabled()) + TRACE("XVBACreateSurface(): -> surface %p\n", output->surface); +} + +// Dumps XVBACreateGLSharedSurface() +void dump_XVBACreateGLSharedSurface(void *input_arg) +{ + XVBA_Create_GLShared_Surface_Input * const input = input_arg; + + if (trace_enabled()) + TRACE("XVBACreateGLSharedSurface(): session %p, GLXContext %p, texture %d\n", + input->session, + input->glcontext, + input->gltexture); +} + +void dump_XVBACreateGLSharedSurface_output(void *output_arg) +{ + XVBA_Create_GLShared_Surface_Output * const output = output_arg; + + if (trace_enabled()) + TRACE("XVBACreateGLSharedSurface(): -> surface %p\n", output->surface); +} + +// Dumps XVBADestroySurface() +void dump_XVBADestroySurface(void *surface) +{ + if (trace_enabled()) + TRACE("XVBADestroySurface(): surface %p\n", surface); +} + +// Dumps XVBAGetCapDecode() +void dump_XVBAGetCapDecode(void *input_arg) +{ + XVBA_GetCapDecode_Input * const input = input_arg; + + if (trace_enabled()) + TRACE("XVBAGetCapDecode(): context %p\n", input->context); +} + +void +dump_XVBAGetCapDecode_output_decode_caps( + unsigned int num_decode_caps, + XVBADecodeCap *decode_caps +) +{ + unsigned int i; + + if (!trace_enabled()) + return; + + INDENT(1); + for (i = 0; i < num_decode_caps; i++) { + XVBADecodeCap * const decode_cap = &decode_caps[i]; + TRACE("capability %d = {\n", i + 1); + INDENT(1); + TRACE("capability_id = %s\n", + string_of_XVBA_CAPABILITY_ID(decode_cap->capability_id)); + TRACE("flags = %s\n", + string_of_XVBA_DECODE_FLAGS(decode_cap->flags)); + TRACE("surface_type = %s\n", + string_of_FOURCC(decode_cap->surface_type)); + INDENT(-1); + TRACE("}\n"); + } + INDENT(-1); +} + +void +dump_XVBAGetCapDecode_output_surface_caps( + unsigned int num_surface_caps, + XVBASurfaceCap *surface_caps +) +{ + unsigned int i; + + if (!trace_enabled()) + return; + + INDENT(1); + for (i = 0; i < num_surface_caps; i++) { + XVBASurfaceCap * const surface_cap = &surface_caps[i]; + TRACE("getsurface target %d = {\n", i + 1); + INDENT(1); + TRACE(" format = %s\n", + string_of_FOURCC(surface_cap->format)); + TRACE(" flag = %s\n", + string_of_XVBA_SURFACE_FLAG(surface_cap->flag)); + INDENT(-1); + TRACE("}\n"); + } + INDENT(-1); +} + +// Dumps XVBACreateDecode() +void dump_XVBACreateDecode(void *input_arg) +{ + XVBA_Create_Decode_Session_Input * const input = input_arg; + + if (!trace_enabled()) + return; + + TRACE("XVBACreateDecode()"); + TRACE(": context %p", input->context); + TRACE(", size %ux%u", input->width, input->height); + + if (input->decode_cap) { + XVBADecodeCap * const decode_cap = input->decode_cap; + TRACE(", capability_id %s, flags %s, surface_type %s", + string_of_XVBA_CAPABILITY_ID(decode_cap->capability_id), + string_of_XVBA_DECODE_FLAGS(decode_cap->flags), + string_of_FOURCC(decode_cap->surface_type)); + } + TRACE("\n"); +} + +void dump_XVBACreateDecode_output(void *output_arg) +{ + XVBA_Create_Decode_Session_Output * const output = output_arg; + + if (trace_enabled()) + TRACE("XVBACreateDecode(): -> session %p\n", output->session); +} + +// Dumps XVBADestroyDecode() +void dump_XVBADestroyDecode(void *session) +{ + if (trace_enabled()) + TRACE("XVBADestroyDecode(): session %p\n", session); +} + +// Dumps XVBACreateDecodeBuffers() +void dump_XVBACreateDecodeBuffers(void *input_arg) +{ + XVBA_Create_DecodeBuff_Input * const input = input_arg; + + if (trace_enabled()) + TRACE("XVBACreateDecodeBuffers(): session %p, %s x %d\n", + input->session, + string_of_XVBA_BUFFER(input->buffer_type), + input->num_of_buffers); +} + +void dump_XVBACreateDecodeBuffers_output(void *output_arg) +{ + XVBA_Create_DecodeBuff_Output * const output = output_arg; + + if (trace_enabled()) + TRACE("XVBACreateDecodeBuffers(): -> buffers %p\n", + output->buffer_list); +} + +// Dumps XVBADestroyDecodeBuffers() +void dump_XVBADestroyDecodeBuffers(void *input_arg) +{ + XVBA_Destroy_Decode_Buffers_Input const * input = input_arg; + + if (trace_enabled()) + TRACE("XVBADestroyDecodeBuffers(): session %p, buffers %p x %d\n", + input->session, + input->buffer_list, + input->num_of_buffers_in_list); +} + +// Dumps XVBAStartDecodePicture() +void dump_XVBAStartDecodePicture(void *input_arg) +{ + XVBA_Decode_Picture_Start_Input * const input = input_arg; + + if (trace_enabled()) + TRACE("XVBAStartDecodePicture(): session %p, surface %p\n", + input->session, + input->target_surface); +} + +// Dumps XVBADecodePicture() +void dump_XVBADecodePicture(void *input_arg) +{ + XVBA_Decode_Picture_Input * const input = input_arg; + + if (!trace_enabled()) + return; + + TRACE("XVBADecodePicture(): session %p\n", input->session); + + unsigned int i; + for (i = 0; i < input->num_of_buffers_in_list; i++) + dump_XVBABufferDescriptor(input->buffer_list[i]); +} + +// Dumps XVBAEndDecodePicture() +void dump_XVBAEndDecodePicture(void *input_arg) +{ + XVBA_Decode_Picture_End_Input * const input = input_arg; + + if (trace_enabled()) + TRACE("XVBAEndDecodePicture(): session %p\n", input->session); +} + +// Dumps XVBAGetSurface() +void dump_XVBAGetSurface(void *input_arg) +{ + if (!trace_enabled()) + return; + +#if XVBA_CHECK_VERSION(0,74) + if (!xvba_check_version(0,74)) + return; + + XVBA_Get_Surface_Input * const input = input_arg; + + TRACE("XVBAGetSurface()"); + TRACE(": session %p", input->session); + TRACE(", surface %p", input->src_surface); + TRACE(", dest pixels %p size %ux%u pitch %d format %s", + input->target_buffer, + input->target_width, + input->target_height, + input->target_pitch, + string_of_FOURCC(input->target_parameter.surfaceType)); +#if 0 + /* XXX: always default to XVBA_FRAME here */ + TRACE(" flag %s",string_of_XVBA_SURFACE_FLAG(input->target_parameter.flag)); +#endif + TRACE("\n"); +#endif +} + +// Dumps XVBATransferSurface() +void dump_XVBATransferSurface(void *input_arg) +{ + if (!trace_enabled()) + return; + +#if XVBA_CHECK_VERSION(0,74) + if (!xvba_check_version(0,74)) + return; + + XVBA_Transfer_Surface_Input * const input = input_arg; + + TRACE("XVBATransferSurface()"); + TRACE(": session %p", input->session); + TRACE(", from surface %p", input->src_surface); + TRACE(", to GL surface %p", input->target_surface); + TRACE(", flag %s", string_of_XVBA_SURFACE_FLAG(input->flag)); + TRACE("\n"); +#endif +} diff --git a/src/xvba_dump.h b/src/xvba_dump.h new file mode 100644 index 0000000..2f4ff14 --- /dev/null +++ b/src/xvba_dump.h @@ -0,0 +1,145 @@ +/* + * xvba_dump.h - Dump utilities + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef XVBA_DUMP_H +#define XVBA_DUMP_H + +#include "xvba_driver.h" +#include "xvba_video.h" + +// Returns string representation of FOURCC +const char *string_of_FOURCC(uint32_t fourcc) + attribute_hidden; + +// Returns string representation of VA surface format +const char *string_of_VAConfigAttribRTFormat(unsigned int format) + attribute_hidden; + +// Returns string representation of XVBA_SURFACE_FLAG +const char *string_of_XVBA_SURFACE_FLAG(XVBA_SURFACE_FLAG flag) + attribute_hidden; + +// Returns string representation of XVBA_BUFFER +const char *string_of_XVBA_BUFFER(XVBA_BUFFER buffer_type) + attribute_hidden; + +// Returns string representation of XVBA_CAPABILITY_ID +const char *string_of_XVBA_CAPABILITY_ID(XVBA_CAPABILITY_ID cap_id) + attribute_hidden; + +// Returns string representation of XVBA_DECODE_FLAGS +const char *string_of_XVBA_DECODE_FLAGS(XVBA_DECODE_FLAGS flag) + attribute_hidden; + +// Returns string representation of XVBACodec +const char *string_of_XVBACodec(XVBACodec codec) + attribute_hidden; + +// Returns string representation of VABufferType +const char *string_of_VABufferType(VABufferType type) + attribute_hidden; + +// Dumps XVBACreateContext() +void dump_XVBACreateContext(void *input) + attribute_hidden; +void dump_XVBACreateContext_output(void *output) + attribute_hidden; + +// Dumps XVBADestroyContext() +void dump_XVBADestroyContext(void *context) + attribute_hidden; + +// Dumps XVBACreateSurface() +void dump_XVBACreateSurface(void *input) + attribute_hidden; +void dump_XVBACreateSurface_output(void *output) + attribute_hidden; + +// Dumps XVBACreateGLSharedSurface() +void dump_XVBACreateGLSharedSurface(void *input) + attribute_hidden; +void dump_XVBACreateGLSharedSurface_output(void *output) + attribute_hidden; + +// Dumps XVBADestroySurface() +void dump_XVBADestroySurface(void *surface) + attribute_hidden; + +// Dumps XVBAGetCapDecode() +void dump_XVBAGetCapDecode(void *input) + attribute_hidden; + +void +dump_XVBAGetCapDecode_output_decode_caps( + unsigned int num_decode_caps, + XVBADecodeCap *decode_caps +) attribute_hidden; + +void +dump_XVBAGetCapDecode_output_surface_caps( + unsigned int num_surface_caps, + XVBASurfaceCap *surface_caps +) attribute_hidden; + +// Dumps XVBACreateDecode() +void dump_XVBACreateDecode(void *input) + attribute_hidden; +void dump_XVBACreateDecode_output(void *output) + attribute_hidden; + +// Dumps XVBADestroyDecode() +void dump_XVBADestroyDecode(void *session) + attribute_hidden; + +// Dumps XVBACreateDecodeBuffers() +void dump_XVBACreateDecodeBuffers(void *input) + attribute_hidden; +void dump_XVBACreateDecodeBuffers_output(void *output) + attribute_hidden; + +// Dumps XVBADestroyDecodeBuffers() +void dump_XVBADestroyDecodeBuffers(void *input) + attribute_hidden; + +// Dumps XVBAStartDecodePicture() +void dump_XVBAStartDecodePicture(void *input) + attribute_hidden; + +// Dumps XVBADecodePicture() +void dump_XVBADecodePicture(void *input) + attribute_hidden; + +// Dumps XVBAEndDecodePicture() +void dump_XVBAEndDecodePicture(void *input) + attribute_hidden; + +// Dumps XVBAUpdateSurface() +void dump_XVBAUpdateSurface(void *input) + attribute_hidden; + +// Dumps XVBAGetSurface() +void dump_XVBAGetSurface(void *input) + attribute_hidden; + +// Dumps XVBATransferSurface() +void dump_XVBATransferSurface(void *input) + attribute_hidden; + +#endif /* XVBA_DUMP_H */ diff --git a/src/xvba_gate.c b/src/xvba_gate.c new file mode 100644 index 0000000..bb66975 --- /dev/null +++ b/src/xvba_gate.c @@ -0,0 +1,956 @@ +/* + * xvba_gate.c - XvBA hooks + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "xvba_gate.h" +#include "xvba_dump.h" +#include "utils.h" +#include "fglrxinfo.h" + +#define DEBUG 1 +#include "debug.h" + +#if USE_DLOPEN +# include <dlfcn.h> +#endif + +#define OPENGL_LIBRARY "libGL.so.1" +#define XVBA_LIBRARY "libXvBAW.so.1" +//#define XVBA_LIBRARY "libAMDXvBA.so.1" + +/* Only call into the tracer if tracer is enabled */ +#if USE_TRACER +# define TRACE(func, args) dump_##func args +#else +# define TRACE(func, args) do { } while (0) +#endif + +/* Defined to 1 if lazy XvBA context destruction is used */ +#define USE_REFCOUNT 1 + + +/* ====================================================================== */ +/* === XvBA VTable === */ +/* ====================================================================== */ + +typedef Bool (*XVBAQueryExtensionProc) (Display *dpy, int *vers); +typedef Status (*XVBACreateContextProc) (void *input, void *output); +typedef Status (*XVBADestroyContextProc) (void *context); +typedef Bool (*XVBAGetSessionInfoProc) (void *input, void *output); +typedef Status (*XVBACreateSurfaceProc) (void *input, void *output); +typedef Status (*XVBACreateGLSharedSurfaceProc)(void *input, void *output); +typedef Status (*XVBADestroySurfaceProc) (void *surface); +typedef Status (*XVBACreateDecodeBuffersProc) (void *input, void *output); +typedef Status (*XVBADestroyDecodeBuffersProc) (void *input); +typedef Status (*XVBAGetCapDecodeProc) (void *input, void *output); +typedef Status (*XVBACreateDecodeProc) (void *input, void *output); +typedef Status (*XVBADestroyDecodeProc) (void *session); +typedef Status (*XVBAStartDecodePictureProc) (void *input); +typedef Status (*XVBADecodePictureProc) (void *input); +typedef Status (*XVBAEndDecodePictureProc) (void *input); +typedef Status (*XVBASyncSurfaceProc) (void *input, void *output); +typedef Status (*XVBAGetSurfaceProc) (void *input); +typedef Status (*XVBATransferSurfaceProc) (void *input); + +static struct { + XVBAQueryExtensionProc QueryExtension; + XVBACreateContextProc CreateContext; + XVBADestroyContextProc DestroyContext; + XVBAGetSessionInfoProc GetSessionInfo; + XVBACreateSurfaceProc CreateSurface; + XVBACreateGLSharedSurfaceProc CreateGLSharedSurface; + XVBADestroySurfaceProc DestroySurface; + XVBACreateDecodeBuffersProc CreateDecodeBuffers; + XVBADestroyDecodeBuffersProc DestroyDecodeBuffers; + XVBAGetCapDecodeProc GetCapDecode; + XVBACreateDecodeProc CreateDecode; + XVBADestroyDecodeProc DestroyDecode; + XVBAStartDecodePictureProc StartDecodePicture; + XVBADecodePictureProc DecodePicture; + XVBAEndDecodePictureProc EndDecodePicture; + XVBASyncSurfaceProc SyncSurface; + XVBAGetSurfaceProc GetSurface; + XVBATransferSurfaceProc TransferSurface; +} g_XVBA_vtable; + +static unsigned int g_init_count; +static void *g_gl_lib_handle; +static void *g_XVBA_lib_handle; + +int xvba_gate_init(void) +{ + void *handle; + + if (g_init_count > 0) { + g_init_count++; + return 0; + } + +#if USE_DLOPEN + /* XXX: workaround broken XvBA libraries */ + dlerror(); + if ((handle = dlopen(OPENGL_LIBRARY, RTLD_LAZY|RTLD_GLOBAL)) == NULL) { + xvba_error_message("dlopen(%s): %s\n", OPENGL_LIBRARY, dlerror()); + return -1; + } + g_gl_lib_handle = handle; + + dlerror(); + if ((handle = dlopen(XVBA_LIBRARY, RTLD_LAZY)) == NULL) { + xvba_error_message("dlopen(%s): %s\n", XVBA_LIBRARY, dlerror()); + return -1; + } + g_XVBA_lib_handle = handle; + +#define INIT_PROC(PREFIX, PROC) do { \ + g_##PREFIX##_vtable.PROC = (PREFIX##PROC##Proc) \ + dlsym(g_##PREFIX##_lib_handle, #PREFIX #PROC); \ + } while (0) + +#define INIT_PROC_CHECK(PREFIX, PROC) do { \ + dlerror(); \ + INIT_PROC(PREFIX, PROC); \ + if (dlerror()) { \ + dlclose(g_##PREFIX##_lib_handle); \ + g_##PREFIX##_lib_handle = NULL; \ + return -1; \ + } \ + } while (0) + +#define XVBA_INIT_PROC(PROC) INIT_PROC_CHECK(XVBA, PROC) + + XVBA_INIT_PROC(QueryExtension); + XVBA_INIT_PROC(CreateContext); + XVBA_INIT_PROC(DestroyContext); + XVBA_INIT_PROC(GetSessionInfo); + XVBA_INIT_PROC(CreateSurface); + XVBA_INIT_PROC(CreateGLSharedSurface); + XVBA_INIT_PROC(DestroySurface); + XVBA_INIT_PROC(CreateDecodeBuffers); + XVBA_INIT_PROC(DestroyDecodeBuffers); + XVBA_INIT_PROC(GetCapDecode); + XVBA_INIT_PROC(CreateDecode); + XVBA_INIT_PROC(DestroyDecode); + XVBA_INIT_PROC(StartDecodePicture); + XVBA_INIT_PROC(DecodePicture); + XVBA_INIT_PROC(EndDecodePicture); + XVBA_INIT_PROC(SyncSurface); + + /* Optional hooks (XvBA >= 0.74) */ + INIT_PROC(XVBA, GetSurface); + INIT_PROC(XVBA, TransferSurface); + +#undef XVBA_INIT_PROC +#undef INIT_PROC + +#endif + + g_init_count++; + return 0; +} + +void xvba_gate_exit(void) +{ +#if USE_DLOPEN + if (--g_init_count > 0) + return; + + if (g_XVBA_lib_handle) { + dlclose(g_XVBA_lib_handle); + g_XVBA_lib_handle = NULL; + } + + if (g_gl_lib_handle) { + dlclose(g_gl_lib_handle); + g_gl_lib_handle = NULL; + } +#endif +} + +#if USE_DLOPEN +#define DEFINE_VTABLE_ENTRY(PREFIX, PROC, RETVAL, DECL_ARGS, CALL_ARGS) \ +static inline RETVAL PREFIX##_##PROC DECL_ARGS \ +{ \ + ASSERT(g_##PREFIX##_vtable.PROC); \ + return g_##PREFIX##_vtable.PROC CALL_ARGS; \ +} +#else +#define DEFINE_VTABLE_ENTRY(PREFIX, PROC, RETVAL, DECL_ARGS, CALL_ARGS) \ +static inline RETVAL PREFIX##_##PROC DECL_ARGS \ +{ \ + return PREFIX##PROC CALL_ARGS; \ +} +#endif + +DEFINE_VTABLE_ENTRY( + XVBA, QueryExtension, + Bool, (Display *dpy, int *version), + (dpy, version)); +DEFINE_VTABLE_ENTRY( + XVBA, CreateContext, + Status, (void *input, void *output), + (input, output)); +DEFINE_VTABLE_ENTRY( + XVBA, DestroyContext, + Status, (void *context), + (context)); +DEFINE_VTABLE_ENTRY( + XVBA, GetSessionInfo, + Bool, (void *input, void *output), + (input, output)); +DEFINE_VTABLE_ENTRY( + XVBA, CreateSurface, + Status, (void *input, void *output), + (input, output)); +DEFINE_VTABLE_ENTRY( + XVBA, CreateGLSharedSurface, + Status, (void *input, void *output), + (input, output)); +DEFINE_VTABLE_ENTRY( + XVBA, DestroySurface, + Status, (void *surface), + (surface)); +DEFINE_VTABLE_ENTRY( + XVBA, CreateDecodeBuffers, + Status, (void *input, void *output), + (input, output)); +DEFINE_VTABLE_ENTRY( + XVBA, DestroyDecodeBuffers, + Status, (void *input), + (input)); +DEFINE_VTABLE_ENTRY( + XVBA, GetCapDecode, + Status, (void *input, void *output), + (input, output)); +DEFINE_VTABLE_ENTRY( + XVBA, CreateDecode, + Status, (void *input, void *output), + (input, output)); +DEFINE_VTABLE_ENTRY( + XVBA, DestroyDecode, + Status, (void *session), + (session)); +DEFINE_VTABLE_ENTRY( + XVBA, StartDecodePicture, + Status, (void *input), + (input)); +DEFINE_VTABLE_ENTRY( + XVBA, DecodePicture, + Status, (void *input), + (input)); +DEFINE_VTABLE_ENTRY( + XVBA, EndDecodePicture, + Status, (void *input), + (input)); +DEFINE_VTABLE_ENTRY( + XVBA, SyncSurface, + Status, (void *input, void *output), + (input, output)); +#if XVBA_CHECK_VERSION(0,74) +DEFINE_VTABLE_ENTRY( + XVBA, GetSurface, + Status, (void *input), + (input)); +DEFINE_VTABLE_ENTRY( + XVBA, TransferSurface, + Status, (void *input), + (input)); +#endif + +#undef DEFINE_VTABLE_ENTRY + +static int g_version_major = -1; +static int g_version_minor = -1; + +int xvba_get_version(int *pmajor, int *pminor) +{ + /* XXX: call XVBAQueryExtension() with a dummy display here? */ + if (g_version_major < 0 || g_version_minor < 0) + return -1; + + if (pmajor) + *pmajor = g_version_major; + if (pminor) + *pminor = g_version_minor; + return 0; +} + +static inline int check_version(int major, int minor) +{ + /* Compile-time checks */ + if (XVBA_VERSION_MAJOR < major) + return 0; + if (XVBA_VERSION_MAJOR == major && XVBA_VERSION_MINOR < minor) + return 0; + + /* Run-time checks */ + if (g_version_major < major) + return 0; + if (g_version_major == major && g_version_minor < minor) + return 0; + + return 1; +} + +int xvba_check_version(int major, int minor) +{ + if (xvba_get_version(NULL, NULL) < 0) /* XXX: ensure global version init */ + return 0; + return check_version(major, minor); +} + + +/* ====================================================================== */ +/* === XvBA Wrappers === */ +/* ====================================================================== */ + +static int xvba_destroy_context_real(XVBAContext *context); +static int xvba_destroy_decode_session_real(XVBASession *session); + +static inline XVBAContext *xvba_context_ref(XVBAContext *context) +{ + ++context->refcount; + return context; +} + +static inline void xvba_context_unref(XVBAContext *context) +{ + if (--context->refcount == 0) + xvba_destroy_context_real(context); +} + +static inline XVBASession *xvba_session_ref(XVBASession *session) +{ + ++session->refcount; + return session; +} + +static inline void xvba_session_unref(XVBASession *session) +{ + if (--session->refcount == 0) + session->destroy(session); +} + +static int xvba_check_status(Status status, const char *msg) +{ + if (status != Success) { + xvba_information_message("%s: status %d\n", msg, status); + return 0; + } + return 1; +} + +int xvba_query_extension(Display *display, int *pversion) +{ + int version; + + if (pversion) + *pversion = 0; + + if (!XVBA_QueryExtension(display, &version)) + return -1; + + if (pversion) + *pversion = version; + + g_version_major = (version >> 16) & 0xffff; + g_version_minor = version & 0xffff; + return 0; +} + +XVBAContext *xvba_create_context(Display *display, Drawable drawable) +{ + XVBA_Create_Context_Input input; + XVBA_Create_Context_Output output; + XVBAContext *context = NULL; + Status status; + + context = malloc(sizeof(*context)); + if (!context) + goto error; + + input.size = sizeof(input); + input.display = display; + input.draw = drawable; + output.size = sizeof(output); + output.context = NULL; + + TRACE(XVBACreateContext, (&input)); + + status = XVBA_CreateContext(&input, &output); + + if (!xvba_check_status(status, "XVBA_CreateContext()")) + goto error; + if (!output.context) + goto error; + + context->context = output.context; + context->refcount = 1; + + TRACE(XVBACreateContext_output, (&output)); + return context; + +error: + free(context); + return NULL; +} + +static int xvba_destroy_context_real(XVBAContext *context) +{ + TRACE(XVBADestroyContext, (context->context)); + + Status status = XVBA_DestroyContext(context->context); + free(context); + if (!xvba_check_status(status, "XVBA_DestroyContext()")) + return -1; + return 0; +} + +int xvba_destroy_context(XVBAContext *context) +{ + if (USE_REFCOUNT) { + xvba_context_unref(context); + return 0; + } + return xvba_destroy_context_real(context); +} + +XVBASurface * +xvba_create_surface( + XVBASession *session, + unsigned int width, + unsigned int height, + XVBA_SURFACE_FORMAT format +) +{ + XVBA_Create_Surface_Input input; + XVBA_Create_Surface_Output output; + XVBASurface *surface; + Status status; + + surface = malloc(sizeof(*surface)); + if (!surface) + return NULL; + + input.size = sizeof(input); + input.session = session->session; + input.width = width; + input.height = height; + input.surface_type = format; + output.size = sizeof(output); + output.surface = NULL; + + TRACE(XVBACreateSurface, (&input)); + + status = XVBA_CreateSurface(&input, &output); + if (!xvba_check_status(status, "XVBA_CreateSurface()")) + goto error; + if (!output.surface) + goto error; + + TRACE(XVBACreateSurface_output, (&output)); + + surface->session = xvba_session_ref(session); + surface->type = XVBA_SURFACETYPE_NORMAL; + surface->surface = output.surface; + surface->info.normal.width = width; + surface->info.normal.height = height; + surface->info.normal.format = format; + return surface; + +error: + free(surface); + return NULL; +} + +#if USE_GLX +XVBASurface * +xvba_create_surface_gl( + XVBASession *session, + GLXContext gl_context, + unsigned int gl_texture +) +{ + XVBA_Create_GLShared_Surface_Input input; + XVBA_Create_GLShared_Surface_Output output; + XVBASurface *surface; + Status status; + + surface = malloc(sizeof(*surface)); + if (!surface) + return NULL; + + input.size = sizeof(input); + input.session = session->session; + input.glcontext = gl_context; + input.gltexture = gl_texture; + output.size = sizeof(output); + output.surface = NULL; + + TRACE(XVBACreateGLSharedSurface, (&input)); + + status = XVBA_CreateGLSharedSurface(&input, &output); + if (!xvba_check_status(status, "XVBA_CreateGLSharedSurface()")) + goto error; + if (!output.surface) + goto error; + + TRACE(XVBACreateGLSharedSurface_output, (&output)); + + surface->session = xvba_session_ref(session); + surface->type = XVBA_SURFACETYPE_GLSHARED; + surface->surface = output.surface; + surface->info.glshared.glcontext = gl_context; + surface->info.glshared.gltexture = gl_texture; + return surface; + +error: + free(surface); + return NULL; +} +#endif + +int xvba_destroy_surface(XVBASurface *surface) +{ + TRACE(XVBADestroySurface, (surface->surface)); + + Status status = XVBA_DestroySurface(surface->surface); + xvba_session_unref(surface->session); + free(surface); + if (!xvba_check_status(status, "XVBA_DestroySurface()")) + return -1; + return 0; +} + +int +xvba_get_session_info( + XVBAContext *context, + unsigned int *getcapdecode_size +) +{ + XVBA_GetSessionInfo_Input input; + XVBA_GetSessionInfo_Output output; + Status status; + + if (getcapdecode_size) + *getcapdecode_size = 0; + + input.size = sizeof(input); + input.context = context->context; + output.size = sizeof(output); + + status = XVBA_GetSessionInfo(&input, &output); + if (!xvba_check_status(status, "XVBA_GetSessionInfo()")) + return -1; + + if (getcapdecode_size) + *getcapdecode_size = output.getcapdecode_output_size; + return 0; +} + +/* This data structure is compatible with XvBA >= 0.73 */ +typedef struct { + unsigned int size; + unsigned int num_decode_caps; + XVBADecodeCap decode_caps_list[]; +} XVBA_GetCapDecode_Output_Base; + +int +xvba_get_decode_caps( + XVBAContext *context, + unsigned int *pdecode_caps_count, + XVBADecodeCap **pdecode_caps +) +{ + XVBA_GetCapDecode_Input input; + XVBA_GetCapDecode_Output_Base *output; + XVBADecodeCap *decode_caps; + unsigned int output_size, num_decode_caps; + Status status; + + if (pdecode_caps_count) + *pdecode_caps_count = 0; + if (pdecode_caps) + *pdecode_caps = NULL; + + input.size = sizeof(input); + input.context = context->context; + if (xvba_get_session_info(context, &output_size) < 0) + return -1; + output_size = MAX(output_size, sizeof(XVBA_GetCapDecode_Output)); + output = alloca(output_size); + output->size = output_size; + + TRACE(XVBAGetCapDecode, (&input)); + + status = XVBA_GetCapDecode(&input, output); + if (!xvba_check_status(status, "XVBA_GetCapDecode()")) + return -1; + + num_decode_caps = output->num_decode_caps; + decode_caps = malloc(num_decode_caps * sizeof(decode_caps[0])); + if (!decode_caps) + return -1; + memcpy(decode_caps, output->decode_caps_list, + num_decode_caps * sizeof(decode_caps[0])); + + if (pdecode_caps_count) + *pdecode_caps_count = num_decode_caps; + if (pdecode_caps) + *pdecode_caps = decode_caps; + + TRACE(XVBAGetCapDecode_output_decode_caps, (num_decode_caps, decode_caps)); + return 0; +} + +int +xvba_get_surface_caps( + XVBAContext *context, + unsigned int *psurface_caps_count, + XVBASurfaceCap **psurface_caps +) +{ + if (psurface_caps_count) + *psurface_caps_count = 0; + if (psurface_caps) + *psurface_caps = NULL; + + if (!check_version(0,74)) + return 0; + +#if XVBA_CHECK_VERSION(0,74) + XVBA_GetCapDecode_Input input; + XVBA_GetCapDecode_Output *output; + XVBASurfaceCap *surface_caps; + unsigned int i, output_size, num_surface_caps; + Status status; + + input.size = sizeof(input); + input.context = context->context; + if (xvba_get_session_info(context, &output_size) < 0) + return -1; + output_size = MAX(output_size, sizeof(XVBA_GetCapDecode_Output)); + output = alloca(output_size); + output->size = output_size; + + TRACE(XVBAGetCapDecode, (&input)); + + status = XVBA_GetCapDecode(&input, output); + if (!xvba_check_status(status, "XVBA_GetCapDecode()")) + return -1; + + num_surface_caps = output->num_of_getsurface_target; + surface_caps = malloc(num_surface_caps * sizeof(surface_caps[0])); + if (!surface_caps) + return -1; + for (i = 0; i < num_surface_caps; i++) { + surface_caps[i].format = output->getsurface_target_list[i].surfaceType; + surface_caps[i].flag = output->getsurface_target_list[i].flag; + } + + if (psurface_caps_count) + *psurface_caps_count = num_surface_caps; + if (psurface_caps) + *psurface_caps = surface_caps; + + TRACE(XVBAGetCapDecode_output_surface_caps, (num_surface_caps, surface_caps)); + return 0; +#endif + return -1; +} + +XVBASession * +xvba_create_decode_session( + XVBAContext *context, + unsigned int width, + unsigned int height, + XVBADecodeCap *decode_cap +) +{ + XVBA_Create_Decode_Session_Input input; + XVBA_Create_Decode_Session_Output output; + XVBASession *session = NULL; + Status status; + + session = malloc(sizeof(*session)); + if (!session) + goto error; + + input.size = sizeof(input); + input.width = width; + input.height = height; + input.context = context->context; + input.decode_cap = decode_cap; + output.size = sizeof(output); + output.session = NULL; + + TRACE(XVBACreateDecode, (&input)); + + status = XVBA_CreateDecode(&input, &output); + if (!xvba_check_status(status, "XVBA_CreateDecode()")) + return NULL; + if (!output.session) + return NULL; + + session->context = xvba_context_ref(context); + session->session = output.session; + session->refcount = 1; + session->destroy = xvba_destroy_decode_session_real; + + TRACE(XVBACreateDecode_output, (&output)); + return session; + +error: + free(session); + return NULL; +} + +static int xvba_destroy_decode_session_real(XVBASession *session) +{ + TRACE(XVBADestroyDecode, (session->session)); + + Status status = XVBA_DestroyDecode(session->session); + xvba_context_unref(session->context); + free(session); + if (!xvba_check_status(status, "XVBA_DestroyDecode()")) + return -1; + return 0; +} + +int xvba_destroy_decode_session(XVBASession *session) +{ + if (USE_REFCOUNT) { + xvba_session_unref(session); + return 0; + } + return xvba_destroy_decode_session_real(session); +} + +void * +xvba_create_decode_buffers( + XVBASession *session, + int type, + unsigned int num_buffers +) +{ + XVBA_Create_DecodeBuff_Input input; + XVBA_Create_DecodeBuff_Output output; + Status status; + + input.size = sizeof(input); + input.session = session->session; + input.buffer_type = type; + input.num_of_buffers = num_buffers; + output.size = sizeof(output); + + TRACE(XVBACreateDecodeBuffers, (&input)); + + status = XVBA_CreateDecodeBuffers(&input, &output); + if (!xvba_check_status(status, "XVBA_CreateDecodeBuffers()")) + return NULL; + if (output.num_of_buffers_in_list != num_buffers) + return NULL; + + TRACE(XVBACreateDecodeBuffers_output, (&output)); + return output.buffer_list; +} + +int +xvba_destroy_decode_buffers( + XVBASession *session, + void *buffers, + unsigned int num_buffers +) +{ + XVBA_Destroy_Decode_Buffers_Input input; + Status status; + + if (buffers == NULL || num_buffers == 0) + return 0; + + input.size = sizeof(input); + input.session = session->session; + input.num_of_buffers_in_list = num_buffers; + input.buffer_list = buffers; + + TRACE(XVBADestroyDecodeBuffers, (&input)); + + status = XVBA_DestroyDecodeBuffers(&input); + if (!xvba_check_status(status, "XVBA_DestroyDecodeBuffers()")) + return -1; + return 0; +} + +int xvba_decode_picture_start(XVBASession *session, XVBASurface *surface) +{ + XVBA_Decode_Picture_Start_Input input; + Status status; + + input.size = sizeof(input); + input.session = session->session; + input.target_surface = surface->surface; + + TRACE(XVBAStartDecodePicture, (&input)); + + status = XVBA_StartDecodePicture(&input); + if (!xvba_check_status(status, "XVBA_StartDecodePicture()")) + return -1; + return 0; +} + +int +xvba_decode_picture( + XVBASession *session, + XVBABufferDescriptor **buffers, + unsigned int num_buffers +) +{ + XVBA_Decode_Picture_Input input; + Status status; + + input.size = sizeof(input); + input.session = session->session; + input.num_of_buffers_in_list= num_buffers; + input.buffer_list = buffers; + + TRACE(XVBADecodePicture, (&input)); + + status = XVBA_DecodePicture(&input); + if (!xvba_check_status(status, "XVBA_DecodePicture()")) + return -1; + return 0; +} + +int xvba_decode_picture_end(XVBASession *session) +{ + XVBA_Decode_Picture_End_Input input; + Status status; + + input.size = sizeof(input); + input.session = session->session; + + TRACE(XVBAEndDecodePicture, (&input)); + + status = XVBA_EndDecodePicture(&input); + if (!xvba_check_status(status, "XVBA_EndDecodePicture()")) + return -1; + return 0; +} + +int xvba_sync_surface(XVBASession *session, XVBASurface *surface, int query) +{ + XVBA_Surface_Sync_Input input; + XVBA_Surface_Sync_Output output; + int status; + + input.size = sizeof(input); + input.session = session->session; + input.surface = surface->surface; + input.query_status = query; + output.size = sizeof(output); + + status = XVBA_SyncSurface(&input, &output); + if (!xvba_check_status(status, "XVBA_SyncSurface()")) + return -1; + + switch (query) { + case XVBA_GET_SURFACE_STATUS: + status = output.status_flags; + break; + case XVBA_GET_DECODE_ERRORS: + status = output.decode_error.type; + break; + default: + status = -1; + break; + } + + return status; +} + +int +xvba_get_surface( + XVBASession *session, + XVBASurface *surface, + XVBA_SURFACE_FORMAT format, + uint8_t *target, + unsigned int pitch, + unsigned int width, + unsigned int height +) +{ + if (!check_version(0,74)) + return -1; + +#if XVBA_CHECK_VERSION(0,74) + XVBA_Get_Surface_Input input; + Status status; + + input.size = sizeof(input); + input.session = session->session; + input.src_surface = surface->surface; + input.target_buffer = target; + input.target_pitch = pitch; + input.target_width = width; + input.target_height = height; + input.target_parameter.size = sizeof(input.target_parameter); + input.target_parameter.surfaceType = format; + input.target_parameter.flag = XVBA_FRAME; + + TRACE(XVBAGetSurface, (&input)); + + status = XVBA_GetSurface(&input); + if (!xvba_check_status(status, "XVBA_GetSurface()")) + return -1; + return 0; +#endif + return -1; +} + +int +xvba_transfer_surface( + XVBASession *session, + XVBASurface *dst_surface, + const XVBASurface *src_surface, + unsigned int flags +) +{ + if (!check_version(0,74)) + return -1; + +#if XVBA_CHECK_VERSION(0,74) + XVBA_Transfer_Surface_Input input; + Status status; + + input.size = sizeof(input); + input.session = session->session; + input.src_surface = src_surface->surface; + input.target_surface = dst_surface->surface; + input.flag = flags; + + TRACE(XVBATransferSurface, (&input)); + + status = XVBA_TransferSurface(&input); + if (!xvba_check_status(status, "XVBA_TransferSurface()")) + return -1; + return 0; +#endif + return -1; +} diff --git a/src/xvba_gate.h b/src/xvba_gate.h new file mode 100644 index 0000000..ca06bb9 --- /dev/null +++ b/src/xvba_gate.h @@ -0,0 +1,218 @@ +/* + * xvba_gate.h - XvBA hooks + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef XVBA_GATE_H +#define XVBA_GATE_H + +#include <X11/Xlib.h> +#include <amdxvba.h> + +#if USE_GLX +#include <GL/glx.h> +#endif + +#define XVBA_FAKE XVBA_FOURCC('F','A','K','E') +#define XVBA_MAX_SUBPICTURES 16 +#define XVBA_MAX_SUBPICTURE_FORMATS 3 + +typedef struct _XVBAContext { + void *context; + unsigned int refcount; +} XVBAContext; + +typedef struct _XVBASession { + XVBAContext *context; + void *session; + unsigned int refcount; + int (*destroy)(struct _XVBASession *); +} XVBASession; + +typedef struct _XVBASurfaceCap { + XVBA_SURFACE_FORMAT format; + XVBA_SURFACE_FLAG flag; +} XVBASurfaceCap; + +typedef enum { + XVBA_SURFACETYPE_NORMAL = 0, + XVBA_SURFACETYPE_GLSHARED +} XVBASurfaceType; + +typedef struct { + XVBASession *session; + XVBASurfaceType type; + void *surface; + union { + struct { + unsigned int width; + unsigned int height; + XVBA_SURFACE_FORMAT format; + } normal; + struct { + void *glcontext; + unsigned int gltexture; + } glshared; + } info; +} XVBASurface; + +/** Initialize XvBA API hooks */ +int xvba_gate_init(void) + attribute_hidden; + +/** Deinitialize XvBA API hooks */ +void xvba_gate_exit(void) + attribute_hidden; + +/** Get version major, minor */ +int xvba_get_version(int *major, int *minor) + attribute_hidden; + +/** Check the minimal version requirement is met */ +int xvba_check_version(int major, int minor) + attribute_hidden; + +/** XVBAQueryExtension */ +int xvba_query_extension(Display *display, int *version) + attribute_hidden; + +/** XVBACreateContext */ +XVBAContext *xvba_create_context(Display *display, Drawable drawable) + attribute_hidden; + +/** XVBADestroyContext */ +int xvba_destroy_context(XVBAContext *context) + attribute_hidden; + +/** XVBACreateSurface */ +XVBASurface * +xvba_create_surface( + XVBASession *session, + unsigned int width, + unsigned int height, + XVBA_SURFACE_FORMAT format +) attribute_hidden; + +#if USE_GLX +/** XVBACreateGLSharedSurface */ +XVBASurface * +xvba_create_surface_gl( + XVBASession *session, + GLXContext gl_context, + unsigned int gl_texture +) attribute_hidden; +#endif + +/** XVBADestroySurface */ +int xvba_destroy_surface(XVBASurface *surface) + attribute_hidden; + +/** XVBAGetSessionInfo */ +int +xvba_get_session_info( + XVBAContext *context, + unsigned int *getcapdecode_size +) attribute_hidden; + +/** XVBAGetCapDecode (XVBA_GetCapDecode_Output.decode_caps_list) */ +int +xvba_get_decode_caps( + XVBAContext *context, + unsigned int *pdecode_caps_count, + XVBADecodeCap **pdecode_caps +) attribute_hidden; + +/** XVBAGetCapDecode (XVBA_GetCapDecode_Output.getsurface_target_list) */ +int +xvba_get_surface_caps( + XVBAContext *context, + unsigned int *psurface_caps_count, + XVBASurfaceCap **psurface_caps +) attribute_hidden; + +/** XVBACreateDecode */ +XVBASession * +xvba_create_decode_session( + XVBAContext *context, + unsigned int width, + unsigned int height, + XVBADecodeCap *decode_cap +) attribute_hidden; + +/** XVBADestroyDecode */ +int xvba_destroy_decode_session(XVBASession *session) + attribute_hidden; + +/** XVBACreateDecodeBuffers */ +void * +xvba_create_decode_buffers( + XVBASession *session, + int type, + unsigned int num_buffers +) attribute_hidden; + +/** XVBADestroyDecodeBuffers */ +int +xvba_destroy_decode_buffers( + XVBASession *session, + void *buffers, + unsigned int num_buffers +) attribute_hidden; + +/** XVBAStartDecodePicture */ +int xvba_decode_picture_start(XVBASession *session, XVBASurface *surface) + attribute_hidden; + +/** XVBADecodePicture */ +int +xvba_decode_picture( + XVBASession *session, + XVBABufferDescriptor **buffers, + unsigned int num_buffers +) attribute_hidden; + +/** XVBAEndDecodePicture */ +int xvba_decode_picture_end(XVBASession *session) + attribute_hidden; + +/** XVBASyncSurface */ +int xvba_sync_surface(XVBASession *session, XVBASurface *surface, int query) + attribute_hidden; + +/** XVBAGetSurface */ +int +xvba_get_surface( + XVBASession *session, + XVBASurface *surface, + XVBA_SURFACE_FORMAT format, + uint8_t *target, + unsigned int pitch, + unsigned int width, + unsigned int height +) attribute_hidden; + +/** XVBATransferSurface */ +int +xvba_transfer_surface( + XVBASession *session, + XVBASurface *dst_surface, + const XVBASurface *src_surface, + unsigned int flags +) attribute_hidden; + +#endif /* XVBA_GATE_H */ diff --git a/src/xvba_image.c b/src/xvba_image.c new file mode 100644 index 0000000..7e0ae50 --- /dev/null +++ b/src/xvba_image.c @@ -0,0 +1,822 @@ +/* + * xvba_image.c - XvBA backend for VA-API (VAImage) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "xvba_image.h" +#include "xvba_video.h" +#include "xvba_buffer.h" +#include "xvba_decode.h" +#include "xvba_dump.h" + +#define DEBUG 1 +#include "debug.h" + + +// List of supported image formats +typedef struct { + uint32_t format; + VAImageFormat va_format; +} xvba_image_format_map_t; + +static const xvba_image_format_map_t xvba_image_formats_map[] = { + { XVBA_NV12, { VA_FOURCC('N','V','1','2'), VA_LSB_FIRST, 12 } }, + { XVBA_YV12, { VA_FOURCC('Y','V','1','2'), VA_LSB_FIRST, 12 } }, + { XVBA_YV12, { VA_FOURCC('I','4','2','0'), VA_LSB_FIRST, 12 } }, + { XVBA_ARGB, { VA_FOURCC('B','G','R','A'), VA_LSB_FIRST, 32, + 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 } }, + { XVBA_FAKE, { VA_FOURCC('R','G','B','A'), VA_LSB_FIRST, 32, + 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 } }, + { 0, } +}; + +// Initializes XvBA decode caps +static int ensure_surface_caps(xvba_driver_data_t *driver_data) +{ + int status; + + if (driver_data->xvba_surface_caps && driver_data->xvba_surface_caps_count) + return 1; + + if (driver_data->xvba_surface_caps) { + free(driver_data->xvba_surface_caps); + driver_data->xvba_surface_caps = NULL; + } + driver_data->xvba_surface_caps_count = 0; + + status = xvba_get_surface_caps( + driver_data->xvba_context, + &driver_data->xvba_surface_caps_count, + &driver_data->xvba_surface_caps + ); + if (status < 0) + return 0; + return 1; +} + +// Check VA image format represents RGB +int is_rgb_format(const VAImageFormat *image_format) +{ + switch (image_format->fourcc) { + case VA_FOURCC('B','G','R','A'): + case VA_FOURCC('R','G','B','A'): + case VA_FOURCC('A','R','G','B'): + case VA_FOURCC('A','B','G','R'): + return 1; + } + return 0; +} + +// Check VA image formats are the same +int compare_image_formats(const VAImageFormat *a, const VAImageFormat *b) +{ + return (a->fourcc == b->fourcc && + a->byte_order == b->byte_order && + a->bits_per_pixel == b->bits_per_pixel && + (is_rgb_format(a) + ? (a->depth == b->depth && + a->red_mask == b->red_mask && + a->green_mask == b->green_mask && + a->blue_mask == b->blue_mask && + a->alpha_mask == b->alpha_mask) + : 1)); +} + +// Checks whether the XvBA implementation supports the specified image format +static inline int +is_supported_format(xvba_driver_data_t *driver_data, uint32_t format) +{ + unsigned int i; + + if (!ensure_surface_caps(driver_data)) + return 0; + + for (i = 0; i < driver_data->xvba_surface_caps_count; i++) { + XVBASurfaceCap * const surface_cap = &driver_data->xvba_surface_caps[i]; + if (surface_cap->format == format && surface_cap->flag == XVBA_FRAME) + return 1; + } + return 0; +} + +// Enable "PutImage hacks", this reinitializes the XvBA state +static VAStatus +putimage_hacks_enable( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface, + object_image_p obj_image, + PutImageHacksType type +) +{ + ASSERT(type == PUTIMAGE_HACKS_SURFACE || + type == PUTIMAGE_HACKS_IMAGE); + + PutImageHacks *h = obj_surface->putimage_hacks; + if (!h) { + h = malloc(sizeof(*h)); + if (!h) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + h->type = type; + h->xvba_surface = NULL; + h->obj_image = NULL; + obj_surface->putimage_hacks = h; + } + + if (type == PUTIMAGE_HACKS_IMAGE) { + if (h->obj_image && + !compare_image_formats(&h->obj_image->image.format, &obj_image->image.format)) { + destroy_image(driver_data, h->obj_image); + h->obj_image = NULL; + } + + if (!h->obj_image) { + h->obj_image = create_image( + driver_data, + obj_surface->width, + obj_surface->height, + &obj_image->image.format + ); + if (!h->obj_image) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + } + ASSERT(h->obj_image->image.width == obj_surface->width); + ASSERT(h->obj_image->image.height == obj_surface->height); + + h->type = PUTIMAGE_HACKS_IMAGE; + return VA_STATUS_SUCCESS; + } + + if (h->xvba_surface) { + if (h->xvba_surface->info.normal.format == obj_image->xvba_format) + return VA_STATUS_SUCCESS; + xvba_destroy_surface(h->xvba_surface); + h->xvba_surface = NULL; + } + + object_context_p obj_context = XVBA_CONTEXT(obj_surface->va_context); + if (!obj_context) + return VA_STATUS_ERROR_INVALID_CONTEXT; + + h->xvba_surface = xvba_create_surface( + obj_context->xvba_session, + obj_surface->width, + obj_surface->height, + obj_image->xvba_format + ); + if (!h->xvba_surface) + return VA_STATUS_ERROR_OPERATION_FAILED; + + return VA_STATUS_SUCCESS; +} + +// Disable "PutImage hacks", flush pending pictures +void +putimage_hacks_disable( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface +) +{ + if (!obj_surface->putimage_hacks) + return; + + if (obj_surface->putimage_hacks->obj_image) { + destroy_image(driver_data, obj_surface->putimage_hacks->obj_image); + obj_surface->putimage_hacks->obj_image = NULL; + } + + if (obj_surface->putimage_hacks->xvba_surface) { + xvba_destroy_surface(obj_surface->putimage_hacks->xvba_surface); + obj_surface->putimage_hacks->xvba_surface = NULL; + } + + free(obj_surface->putimage_hacks); + obj_surface->putimage_hacks = NULL; +} + +// Check we need the "PutImage hacks" +static VAStatus +putimage_hacks_check( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface, + object_image_p obj_image +) +{ + object_context_p obj_context = XVBA_CONTEXT(obj_surface->va_context); + + PutImageHacksType type; + /* If the surface was never used for decoding, it's safe to be + aliased to plain pixels data (represented via a VAImage) */ + if (!obj_surface->used_for_decoding) + type = PUTIMAGE_HACKS_IMAGE; + else + type = PUTIMAGE_HACKS_SURFACE; + + if (obj_surface->putimage_hacks && + obj_surface->putimage_hacks->type != type) + putimage_hacks_disable(driver_data, obj_surface); + + VAStatus status; + switch (type) { + case PUTIMAGE_HACKS_NONE: + status = VA_STATUS_SUCCESS; + break; + case PUTIMAGE_HACKS_SURFACE: + if (!obj_context) + return VA_STATUS_ERROR_INVALID_CONTEXT; + // fall-through + default: + status = putimage_hacks_enable( + driver_data, + obj_surface, + obj_image, + type + ); + break; + } + return status; +} + +// vaQueryImageFormats +VAStatus +xvba_QueryImageFormats( + VADriverContextP ctx, + VAImageFormat *format_list, + int *num_formats +) +{ + XVBA_DRIVER_DATA_INIT; + + if (!format_list) + return VA_STATUS_SUCCESS; + + int i, n = 0; + for (i = 0; xvba_image_formats_map[i].format != 0; i++) { + const xvba_image_format_map_t * const f = &xvba_image_formats_map[i]; + /* XXX: XvBA is really too stupid: the set of supported formats for + XVBAGetSurface() and for XVBAUploadSurface() do NOT intersect... */ + if (1 || is_supported_format(driver_data, f->format)) + format_list[n++] = f->va_format; + } + + /* If the assert fails then XVBA_MAX_IMAGE_FORMATS needs to be bigger */ + ASSERT(n <= XVBA_MAX_IMAGE_FORMATS); + if (num_formats) + *num_formats = n; + + return VA_STATUS_SUCCESS; +} + +// Create image +object_image_p +create_image( + xvba_driver_data_t *driver_data, + unsigned int width, + unsigned int height, + VAImageFormat *format +) +{ + VAImageID image_id = object_heap_allocate(&driver_data->image_heap); + if (image_id == VA_INVALID_ID) + return NULL; + + object_image_p obj_image = XVBA_IMAGE(image_id); + if (!obj_image) + return NULL; + + VAImage * const image = &obj_image->image; + image->image_id = image_id; + image->buf = VA_INVALID_ID; + + /* XXX: we align size to 16-pixel boundaries because the image may + be used to retrieve XvBA surface pixels and this requires exact + match of the dimensions. And since yet another stupid AMD bug + requires 16-pixel alignment for surfaces... */ + unsigned int width2, height2, size2, awidth, aheight, size; + awidth = (width + 15) & -16U; + aheight = (height + 15) & -16U; + size = awidth * aheight; + width2 = (awidth + 1) / 2; + height2 = (aheight + 1) / 2; + size2 = width2 * height2; + + XVBA_SURFACE_FORMAT xvba_format; + switch (format->fourcc) { + case VA_FOURCC('N','V','1','2'): + xvba_format = XVBA_NV12; + image->num_planes = 2; + image->pitches[0] = awidth; + image->offsets[0] = 0; + image->pitches[1] = awidth; + image->offsets[1] = size; + image->data_size = size + 2 * size2; + break; + case VA_FOURCC('I','4','2','0'): + xvba_format = XVBA_YV12; + image->num_planes = 3; + image->pitches[0] = awidth; + image->offsets[0] = 0; + image->pitches[1] = width2; + image->offsets[1] = size + size2; + image->pitches[2] = width2; + image->offsets[2] = size; + image->data_size = size + 2 * size2; + break; + case VA_FOURCC('Y','V','1','2'): + xvba_format = XVBA_YV12; + image->num_planes = 3; + image->pitches[0] = awidth; + image->offsets[0] = 0; + image->pitches[1] = width2; + image->offsets[1] = size; + image->pitches[2] = width2; + image->offsets[2] = size + size2; + image->data_size = size + 2 * size2; + break; + case VA_FOURCC('Y','U','Y','2'): + xvba_format = XVBA_YUY2; + image->num_planes = 1; + image->pitches[0] = awidth * 2; + image->offsets[0] = 0; + image->data_size = image->pitches[0] * aheight; + break; + case VA_FOURCC('B','G','R','A'): + if (format->bits_per_pixel != 32) + goto error; + xvba_format = XVBA_ARGB; + image->num_planes = 1; + image->pitches[0] = awidth * 4; + image->offsets[0] = 0; + image->data_size = image->pitches[0] * aheight; + break; + case VA_FOURCC('R','G','B','A'): + if (format->bits_per_pixel != 32) + goto error; + xvba_format = XVBA_NONE; + image->num_planes = 1; + image->pitches[0] = awidth * 4; + image->offsets[0] = 0; + image->data_size = image->pitches[0] * aheight; + break; + default: + goto error; + } + obj_image->xvba_format = xvba_format; + obj_image->xvba_width = awidth; + obj_image->xvba_height = aheight; + obj_image->hw.mtime = 0; + obj_image->hw.xvba = NULL; + obj_image->hw.glx = NULL; + + object_buffer_p obj_buffer; + obj_buffer = create_va_buffer( + driver_data, + VA_INVALID_ID, + VAImageBufferType, + 1, image->data_size + ); + if (!obj_buffer) + goto error; + + image->image_id = image_id; + image->buf = obj_buffer->base.id; + image->format = *format; + image->width = width; + image->height = height; + + /* XXX: no paletted formats supported yet */ + image->num_palette_entries = 0; + image->entry_bytes = 0; + return obj_image; + +error: + destroy_image(driver_data, obj_image); + return NULL; +} + +// Destroy image +void +destroy_image( + xvba_driver_data_t *driver_data, + object_image_p obj_image +) +{ + obj_image->image.image_id = VA_INVALID_ID; + destroy_hw_image(driver_data, obj_image); + destroy_va_buffer(driver_data, XVBA_BUFFER(obj_image->image.buf)); + object_heap_free(&driver_data->image_heap, (object_base_p)obj_image); +} + +#if USE_GLX +const HWImageHooks hw_image_hooks_glx attribute_hidden; +#endif + +// Commit image to the HW +VAStatus +commit_hw_image( + xvba_driver_data_t *driver_data, + object_image_p obj_image, + XVBASession *session, + unsigned int flags +) +{ + VAStatus status; + + object_buffer_p const obj_buffer = XVBA_BUFFER(obj_image->image.buf); + if (!obj_buffer) + return VA_STATUS_ERROR_INVALID_BUFFER; + + /* Update video surface only if the image (hence its buffer) was + updated since our last synchronisation. + + NOTE: this assumes the user really unmaps the buffer when he is + done with it, as it is actually required */ + if (obj_image->hw.mtime < obj_buffer->mtime) { +#if USE_GLX + if (flags & HWIMAGE_TYPE_GLX) { + if (!obj_image->hw.glx) { + ASSERT(hw_image_hooks_glx.create); + status = hw_image_hooks_glx.create( + driver_data, + obj_image, + session + ); + if (status != VA_STATUS_SUCCESS) + return status; + } + ASSERT(hw_image_hooks_glx.commit); + status = hw_image_hooks_glx.commit( + driver_data, + obj_image, + obj_buffer, + session + ); + if (status != VA_STATUS_SUCCESS) + return status; + } +#endif + obj_image->hw.mtime = obj_buffer->mtime; + } + return VA_STATUS_SUCCESS; +} + +// Destroy HW image +void +destroy_hw_image( + xvba_driver_data_t *driver_data, + object_image_p obj_image +) +{ + if (!obj_image) + return; + +#if USE_GLX + if (obj_image->hw.glx) { + if (hw_image_hooks_glx.destroy) + hw_image_hooks_glx.destroy(driver_data, obj_image); + obj_image->hw.glx = NULL; + } +#endif +} + +// Copy image +static VAStatus +copy_image( + xvba_driver_data_t *driver_data, + object_image_p dst_obj_image, + object_image_p src_obj_image +) +{ + if (!compare_image_formats(&dst_obj_image->image.format, + &src_obj_image->image.format)) + return VA_STATUS_ERROR_UNKNOWN; + + object_buffer_p dst_obj_buffer = XVBA_BUFFER(dst_obj_image->image.buf); + if (!dst_obj_buffer) + return VA_STATUS_ERROR_INVALID_BUFFER; + + object_buffer_p src_obj_buffer = XVBA_BUFFER(src_obj_image->image.buf); + if (!src_obj_buffer) + return VA_STATUS_ERROR_INVALID_BUFFER; + + /* XXX: use map/unmap functions */ + ASSERT(dst_obj_image->image.data_size == src_obj_image->image.data_size); + memcpy( + dst_obj_buffer->buffer_data, + src_obj_buffer->buffer_data, + dst_obj_image->image.data_size + ); + dst_obj_buffer->mtime = src_obj_buffer->mtime; + return VA_STATUS_SUCCESS; +} + +// vaCreateImage +VAStatus +xvba_CreateImage( + VADriverContextP ctx, + VAImageFormat *format, + int width, + int height, + VAImage *out_image +) +{ + XVBA_DRIVER_DATA_INIT; + + if (!format || !out_image) + return VA_STATUS_ERROR_INVALID_PARAMETER; + + out_image->image_id = VA_INVALID_ID; + out_image->buf = VA_INVALID_ID; + + object_image_p obj_image; + obj_image = create_image(driver_data, width, height, format); + if (!obj_image) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + + *out_image = obj_image->image; + return VA_STATUS_SUCCESS; +} + +// vaDestroyImage +VAStatus +xvba_DestroyImage( + VADriverContextP ctx, + VAImageID image_id +) +{ + XVBA_DRIVER_DATA_INIT; + + object_image_p obj_image = XVBA_IMAGE(image_id); + if (!obj_image) + return VA_STATUS_ERROR_INVALID_IMAGE; + + destroy_image(driver_data, obj_image); + return VA_STATUS_SUCCESS; +} + +// vaDeriveImage +VAStatus +xvba_DeriveImage( + VADriverContextP ctx, + VASurfaceID surface, + VAImage *image +) +{ + /* TODO */ + return VA_STATUS_ERROR_OPERATION_FAILED; +} + +// vaSetImagePalette +VAStatus +xvba_SetImagePalette( + VADriverContextP ctx, + VAImageID image, + unsigned char *palette +) +{ + /* TODO */ + return VA_STATUS_ERROR_OPERATION_FAILED; +} + +// Get image from surface +static VAStatus +get_image( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_surface_p obj_surface, + object_image_p obj_image, + const VARectangle *rect +) +{ + /* XVBAGetSurface() API appeared in XvBA 0.74 */ + if (!xvba_check_version(0,74)) + return VA_STATUS_ERROR_OPERATION_FAILED; + + object_buffer_p obj_buffer = XVBA_BUFFER(obj_image->image.buf); + if (!obj_buffer) + return VA_STATUS_ERROR_INVALID_BUFFER; + + /* XXX: only support full surface readback for now */ + if (rect->x != 0 || + rect->y != 0 || + rect->width != obj_surface->width || + rect->height != obj_surface->height) + return VA_STATUS_ERROR_OPERATION_FAILED; + + /* Make sure the surface is decoded prior to extracting it */ + /* XXX: API doc mentions this as implicit in XVBAGetSurface() though... */ + if (sync_surface(driver_data, obj_context, obj_surface) < 0) + return VA_STATUS_ERROR_OPERATION_FAILED; + + /* XXX: stupid driver requires 16-pixels alignment */ + ASSERT(obj_surface->xvba_surface->type == XVBA_SURFACETYPE_NORMAL); + if (obj_image->xvba_width != obj_surface->xvba_surface->info.normal.width || + obj_image->xvba_height != obj_surface->xvba_surface->info.normal.height) + return VA_STATUS_ERROR_OPERATION_FAILED; + + if (xvba_get_surface(obj_context->xvba_decoder, + obj_surface->xvba_surface, + obj_image->xvba_format, + obj_buffer->buffer_data, + obj_image->image.pitches[0], + obj_image->xvba_width, + obj_image->xvba_height) < 0) + return VA_STATUS_ERROR_OPERATION_FAILED; + + return VA_STATUS_SUCCESS; +} + +// vaGetImage +VAStatus +xvba_GetImage( + VADriverContextP ctx, + VASurfaceID surface, + int x, + int y, + unsigned int width, + unsigned int height, + VAImageID image +) +{ + XVBA_DRIVER_DATA_INIT; + + object_surface_p obj_surface = XVBA_SURFACE(surface); + if (!obj_surface) + return VA_STATUS_ERROR_INVALID_SURFACE; + + object_context_p obj_context = XVBA_CONTEXT(obj_surface->va_context); + if (!obj_context) + return VA_STATUS_ERROR_INVALID_CONTEXT; + + object_image_p obj_image = XVBA_IMAGE(image); + if (!obj_image) + return VA_STATUS_ERROR_INVALID_IMAGE; + + VARectangle rect; + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + return get_image(driver_data, obj_context, obj_surface, obj_image, &rect); +} + +// Put image to surface +static VAStatus +put_image( + xvba_driver_data_t *driver_data, + object_image_p obj_image, + object_surface_p obj_surface, + const VARectangle *src_rect, + const VARectangle *dst_rect +) +{ +#if 0 + /* Don't do anything if the surface is used for rendering for example */ + if (obj_surface->va_surface_status != VASurfaceReady) + return VA_STATUS_ERROR_SURFACE_BUSY; +#endif + + /* Only support upload to normal video surfaces */ + if (obj_surface->xvba_surface && + obj_surface->xvba_surface->type != XVBA_SURFACETYPE_NORMAL) + return VA_STATUS_ERROR_INVALID_SURFACE; + + /* Check we are not overriding the whole surface */ + if (dst_rect->x == 0 && + dst_rect->y == 0 && + dst_rect->width == obj_surface->width && + dst_rect->height == obj_surface->height) { + obj_surface->used_for_decoding = 0; + obj_surface->va_surface_status = VASurfaceReady; + } + + /* XXX: XvBA does not have a real PutImage API. It requires + image and surface to have the same dimensions... */ + if (obj_image->image.width != obj_surface->width || + obj_image->image.height != obj_surface->height) + return VA_STATUS_ERROR_OPERATION_FAILED; + + /* XXX: XvBA implementation cannot do partial surface uploads, though + the API permits it... Neither can it scale the data for us */ + if (src_rect->x != 0 || + src_rect->y != 0 || + src_rect->width != obj_image->image.width || + src_rect->height != obj_image->image.height) + return VA_STATUS_ERROR_OPERATION_FAILED; + if (dst_rect->x != 0 || + dst_rect->y != 0 || + dst_rect->width != obj_surface->width || + dst_rect->height != obj_surface->height) + return VA_STATUS_ERROR_OPERATION_FAILED; + if (src_rect->width != dst_rect->width || + src_rect->height != dst_rect->height) + return VA_STATUS_ERROR_OPERATION_FAILED; + + VAStatus status = putimage_hacks_check(driver_data, obj_surface, obj_image); + if (status != VA_STATUS_SUCCESS) + return status; + + PutImageHacks * const h = obj_surface->putimage_hacks; + if (h && h->type == PUTIMAGE_HACKS_IMAGE) { + status = copy_image( + driver_data, + obj_surface->putimage_hacks->obj_image, + obj_image + ); + return status; + } + return VA_STATUS_ERROR_UNIMPLEMENTED; +} + +// vaPutImage +VAStatus +xvba_PutImage( + VADriverContextP ctx, + VASurfaceID surface, + VAImageID image, + int src_x, + int src_y, + unsigned int width, + unsigned int height, + int dest_x, + int dest_y +) +{ + XVBA_DRIVER_DATA_INIT; + + object_surface_p obj_surface = XVBA_SURFACE(surface); + if (!obj_surface) + return VA_STATUS_ERROR_INVALID_SURFACE; + + object_context_p obj_context = XVBA_CONTEXT(obj_surface->va_context); + if (!obj_context) + return VA_STATUS_ERROR_INVALID_CONTEXT; + + object_image_p obj_image = XVBA_IMAGE(image); + if (!obj_image) + return VA_STATUS_ERROR_INVALID_IMAGE; + + VARectangle src_rect, dst_rect; + src_rect.x = src_x; + src_rect.y = src_y; + src_rect.width = width; + src_rect.height = height; + dst_rect.x = dest_x; + dst_rect.y = dest_y; + dst_rect.width = width; + dst_rect.height = height; + return put_image(driver_data, obj_image, obj_surface, &src_rect, &dst_rect); +} + +// vaPutImage2 +VAStatus +xvba_PutImage_full( + VADriverContextP ctx, + VASurfaceID surface, + VAImageID image, + int src_x, + int src_y, + unsigned int src_width, + unsigned int src_height, + int dest_x, + int dest_y, + unsigned int dest_width, + unsigned int dest_height +) +{ + XVBA_DRIVER_DATA_INIT; + + object_surface_p obj_surface = XVBA_SURFACE(surface); + if (!obj_surface) + return VA_STATUS_ERROR_INVALID_SURFACE; + + object_image_p obj_image = XVBA_IMAGE(image); + if (!obj_image) + return VA_STATUS_ERROR_INVALID_IMAGE; + + VARectangle src_rect, dst_rect; + src_rect.x = src_x; + src_rect.y = src_y; + src_rect.width = src_width; + src_rect.height = src_height; + dst_rect.x = dest_x; + dst_rect.y = dest_y; + dst_rect.width = dest_width; + dst_rect.height = dest_height; + return put_image(driver_data, obj_image, obj_surface, &src_rect, &dst_rect); +} diff --git a/src/xvba_image.h b/src/xvba_image.h new file mode 100644 index 0000000..415122f --- /dev/null +++ b/src/xvba_image.h @@ -0,0 +1,234 @@ +/* + * xvba_image.h - XvBA backend for VA-API (VAImage) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef XVBA_IMAGE_H +#define XVBA_IMAGE_H + +#include "xvba_driver.h" + +typedef struct object_image_glx *object_image_glx_p; +typedef struct object_image_xvba *object_image_xvba_p; +struct object_image_xvba { + XVBASurface *surface; + unsigned int width; + unsigned int height; +}; + +enum { + HWIMAGE_TYPE_XVBA = 1, + HWIMAGE_TYPE_GLX = 2 +}; + +typedef struct _HWImageHooks HWImageHooks; +struct _HWImageHooks { + VAStatus (*create)( + xvba_driver_data_t *driver_data, + object_image_p obj_image, + XVBASession *session + ); + + void (*destroy)( + xvba_driver_data_t *driver_data, + object_image_p obj_image + ); + + VAStatus (*commit)( + xvba_driver_data_t *driver_data, + object_image_p obj_image, + object_buffer_p obj_buffer, + XVBASession *session + ); +}; + +typedef struct _HWImage HWImage; +struct _HWImage { + uint64_t mtime; + object_image_xvba_p xvba; + object_image_glx_p glx; +}; + +typedef struct object_image object_image_t; +struct object_image { + struct object_base base; + VAImage image; + XVBA_SURFACE_FORMAT xvba_format; + unsigned int xvba_width; + unsigned int xvba_height; + HWImage hw; +}; + +typedef struct GetImageHacks { + unsigned int picnum; /* Current index into delayed_pictures[] */ +} GetImageHacks; + +typedef enum { + PUTIMAGE_HACKS_NONE, + PUTIMAGE_HACKS_SURFACE, + PUTIMAGE_HACKS_IMAGE +} PutImageHacksType; + +typedef struct PutImageHacks { + PutImageHacksType type; + XVBASurface *xvba_surface; + object_image_p obj_image; +} PutImageHacks; + +void +getimage_hacks_disable( + xvba_driver_data_t *driver_data, + object_context_p obj_context +) attribute_hidden; + +void +putimage_hacks_disable( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface +) attribute_hidden; + +// Check VA image format represents RGB +int is_rgb_format(const VAImageFormat *image_format) + attribute_hidden; + +// Check VA image format represents YUV +static inline int is_yuv_format(const VAImageFormat *image_format) +{ + return !is_rgb_format(image_format); +} + +// Check VA image formats are the same +int compare_image_formats(const VAImageFormat *a, const VAImageFormat *b) + attribute_hidden; + +// Create image +object_image_p +create_image( + xvba_driver_data_t *driver_data, + unsigned int width, + unsigned int height, + VAImageFormat *format +) attribute_hidden; + +// Destroy image +void +destroy_image( + xvba_driver_data_t *driver_data, + object_image_p obj_image +) attribute_hidden; + +// Destroy HW image +void +destroy_hw_image( + xvba_driver_data_t *driver_data, + object_image_p obj_image +) attribute_hidden; + +// Commit image to the HW +VAStatus +commit_hw_image( + xvba_driver_data_t *driver_data, + object_image_p obj_image, + XVBASession *session, + unsigned int flags +) attribute_hidden; + +// vaQueryImageFormats +VAStatus +xvba_QueryImageFormats( + VADriverContextP ctx, + VAImageFormat *format_list, + int *num_formats +) attribute_hidden; + +// vaCreateImage +VAStatus +xvba_CreateImage( + VADriverContextP ctx, + VAImageFormat *format, + int width, + int height, + VAImage *image +) attribute_hidden; + +// vaDestroyImage +VAStatus +xvba_DestroyImage( + VADriverContextP ctx, + VAImageID image_id +) attribute_hidden; + +// vaDeriveImage +VAStatus +xvba_DeriveImage( + VADriverContextP ctx, + VASurfaceID surface, + VAImage *image +) attribute_hidden; + +// vaSetImagePalette +VAStatus +xvba_SetImagePalette( + VADriverContextP ctx, + VAImageID image, + unsigned char *palette +) attribute_hidden; + +// vaGetImage +VAStatus +xvba_GetImage( + VADriverContextP ctx, + VASurfaceID surface, + int x, + int y, + unsigned int width, + unsigned int height, + VAImageID image_id +) attribute_hidden; + +// vaPutImage +VAStatus +xvba_PutImage( + VADriverContextP ctx, + VASurfaceID surface, + VAImageID image, + int src_x, + int src_y, + unsigned int width, + unsigned int height, + int dest_x, + int dest_y +) attribute_hidden; + +// vaPutImage2 +VAStatus +xvba_PutImage_full( + VADriverContextP ctx, + VASurfaceID surface, + VAImageID image, + int src_x, + int src_y, + unsigned int src_width, + unsigned int src_height, + int dest_x, + int dest_y, + unsigned int dest_width, + unsigned int dest_height +) attribute_hidden; + +#endif /* XVBA_IMAGE_H */ diff --git a/src/xvba_subpic.c b/src/xvba_subpic.c new file mode 100644 index 0000000..0cb6e88 --- /dev/null +++ b/src/xvba_subpic.c @@ -0,0 +1,585 @@ +/* + * xvba_subpic.c - XvBA backend for VA-API (VA subpictures) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "xvba_subpic.h" +#include "xvba_video.h" +#include "xvba_image.h" +#include "xvba_buffer.h" +#include "utils.h" + +#define DEBUG 1 +#include "debug.h" + + +// List of supported subpicture formats +typedef struct { + uint32_t format; + VAImageFormat va_format; + unsigned int va_flags; +} xvba_subpic_format_map_t; + +static const xvba_subpic_format_map_t xvba_subpic_formats_map[] = { + { XVBA_ARGB, { VA_FOURCC('B','G','R','A'), VA_LSB_FIRST, 32, + 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 }, + VA_SUBPICTURE_GLOBAL_ALPHA }, + { XVBA_FAKE, { VA_FOURCC('R','G','B','A'), VA_LSB_FIRST, 32, + 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }, + VA_SUBPICTURE_GLOBAL_ALPHA }, + { 0, } +}; + +// Append association to the subpicture +static int +subpicture_add_association( + object_subpicture_p obj_subpicture, + SubpictureAssociationP assoc +) +{ + SubpictureAssociationP *assocs; + assocs = realloc_buffer(&obj_subpicture->assocs, + &obj_subpicture->assocs_count_max, + 1 + obj_subpicture->assocs_count, + sizeof(obj_subpicture->assocs[0])); + if (!assocs) + return -1; + + assocs[obj_subpicture->assocs_count++] = assoc; + return 0; +} + +// Remove association at the specified index from the subpicture +static inline int +subpicture_remove_association_at(object_subpicture_p obj_subpicture, int index) +{ + ASSERT(obj_subpicture->assocs && obj_subpicture->assocs_count > 0); + if (!obj_subpicture->assocs || obj_subpicture->assocs_count == 0) + return -1; + + /* Replace with the last association */ + const unsigned int last = obj_subpicture->assocs_count - 1; + obj_subpicture->assocs[index] = obj_subpicture->assocs[last]; + obj_subpicture->assocs[last] = NULL; + --obj_subpicture->assocs_count; + return 0; +} + +// Remove association from the subpicture +static int +subpicture_remove_association( + object_subpicture_p obj_subpicture, + SubpictureAssociationP assoc +) +{ + ASSERT(obj_subpicture->assocs && obj_subpicture->assocs_count > 0); + if (!obj_subpicture->assocs || obj_subpicture->assocs_count == 0) + return -1; + + unsigned int i; + for (i = 0; i < obj_subpicture->assocs_count; i++) { + if (obj_subpicture->assocs[i] == assoc) + return subpicture_remove_association_at(obj_subpicture, i); + } + return -1; +} + +// Associate one surface to the subpicture +VAStatus +subpicture_associate_1( + object_subpicture_p obj_subpicture, + object_surface_p obj_surface, + const VARectangle *src_rect, + const VARectangle *dst_rect, + unsigned int flags +) +{ + /* XXX: some flags are not supported */ + static const unsigned int supported_flags = VA_SUBPICTURE_GLOBAL_ALPHA; + if (flags & ~supported_flags) + return VA_STATUS_ERROR_FLAG_NOT_SUPPORTED; + + SubpictureAssociationP assoc = malloc(sizeof(*assoc)); + if (!assoc) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + + assoc->subpicture = obj_subpicture->base.id; + assoc->surface = obj_surface->base.id; + assoc->src_rect = *src_rect; + assoc->dst_rect = *dst_rect; + assoc->flags = flags; + + if (surface_add_association(obj_surface, assoc) < 0) { + free(assoc); + return VA_STATUS_ERROR_UNKNOWN; + } + + if (subpicture_add_association(obj_subpicture, assoc) < 0) { + surface_remove_association(obj_surface, assoc); + free(assoc); + return VA_STATUS_ERROR_ALLOCATION_FAILED; + } + return VA_STATUS_SUCCESS; +} + +// Associate surfaces to the subpicture +static VAStatus +associate_subpicture( + xvba_driver_data_t *driver_data, + object_subpicture_p obj_subpicture, + VASurfaceID *surfaces, + unsigned int num_surfaces, + const VARectangle *src_rect, + const VARectangle *dst_rect, + unsigned int flags +) +{ + VAStatus status; + unsigned int i; + + object_image_p obj_image = XVBA_IMAGE(obj_subpicture->image_id); + if (!obj_image) + return VA_STATUS_ERROR_INVALID_IMAGE; + + /* Check subpicture association bounds */ + if (src_rect->x < 0 || + src_rect->y < 0 || + src_rect->x + src_rect->width > obj_image->image.width || + src_rect->y + src_rect->height > obj_image->image.height) + return VA_STATUS_ERROR_INVALID_PARAMETER; + if (dst_rect->x < 0 || + dst_rect->y < 0) + return VA_STATUS_ERROR_INVALID_PARAMETER; + + for (i = 0; i < num_surfaces; i++) { + object_surface_p const obj_surface = XVBA_SURFACE(surfaces[i]); + if (!obj_surface) + return VA_STATUS_ERROR_INVALID_SURFACE; + + if (dst_rect->x + dst_rect->width > obj_surface->width || + dst_rect->y + dst_rect->height > obj_surface->height) + return VA_STATUS_ERROR_INVALID_PARAMETER; + + status = subpicture_associate_1( + obj_subpicture, + obj_surface, + src_rect, + dst_rect, + flags + ); + if (status != VA_STATUS_SUCCESS) + return status; + } + return VA_STATUS_SUCCESS; +} + +// Deassociate one surface from the subpicture +VAStatus +subpicture_deassociate_1( + object_subpicture_p obj_subpicture, + object_surface_p obj_surface +) +{ + ASSERT(obj_subpicture->assocs && obj_subpicture->assocs_count > 0); + if (!obj_subpicture->assocs || obj_subpicture->assocs_count == 0) + return VA_STATUS_ERROR_OPERATION_FAILED; + + unsigned int i; + for (i = 0; i < obj_subpicture->assocs_count; i++) { + SubpictureAssociationP const assoc = obj_subpicture->assocs[i]; + ASSERT(assoc); + if (assoc && assoc->surface == obj_surface->base.id) { + surface_remove_association(obj_surface, assoc); + subpicture_remove_association_at(obj_subpicture, i); + free(assoc); + return VA_STATUS_SUCCESS; + } + } + return VA_STATUS_ERROR_OPERATION_FAILED; +} + +// Deassociate surfaces from the subpicture +static VAStatus +deassociate_subpicture( + xvba_driver_data_t *driver_data, + object_subpicture_p obj_subpicture, + VASurfaceID *surfaces, + unsigned int num_surfaces +) +{ + VAStatus status, error = VA_STATUS_SUCCESS; + unsigned int i; + + for (i = 0; i < num_surfaces; i++) { + object_surface_p const obj_surface = XVBA_SURFACE(surfaces[i]); + if (!obj_surface) + return VA_STATUS_ERROR_INVALID_SURFACE; + status = subpicture_deassociate_1(obj_subpicture, obj_surface); + if (status != VA_STATUS_SUCCESS) { + /* Simply report the first error to the user */ + if (error == VA_STATUS_SUCCESS) + error = status; + } + } + return error; +} + +// Commit subpicture to video surface +VAStatus +commit_subpicture( + xvba_driver_data_t *driver_data, + object_subpicture_p obj_subpicture, + void *session, + unsigned int flags +) +{ + object_image_p obj_image = XVBA_IMAGE(obj_subpicture->image_id); + if (!obj_image) + return VA_STATUS_ERROR_INVALID_IMAGE; + + return commit_hw_image(driver_data, obj_image, session, flags); +} + +// Create subpicture with image +static object_subpicture_p +create_subpicture( + xvba_driver_data_t *driver_data, + object_image_p obj_image +) +{ + VASubpictureID subpic_id; + subpic_id = object_heap_allocate(&driver_data->subpicture_heap); + if (subpic_id == VA_INVALID_ID) + return NULL; + + object_subpicture_p obj_subpicture = XVBA_SUBPICTURE(subpic_id); + ASSERT(obj_subpicture); + if (!obj_subpicture) + return NULL; + + obj_subpicture->image_id = obj_image->base.id; + obj_subpicture->assocs = NULL; + obj_subpicture->assocs_count = 0; + obj_subpicture->assocs_count_max = 0; + obj_subpicture->chromakey_min = 0; + obj_subpicture->chromakey_max = 0; + obj_subpicture->chromakey_mask = 0; + obj_subpicture->alpha = 1.0f; + return obj_subpicture; +} + +// Destroy XvBA surface bound to VA subpicture +void +destroy_subpicture_surface( + xvba_driver_data_t *driver_data, + object_subpicture_p obj_subpicture +) +{ + if (!obj_subpicture) + return; + + object_image_p const obj_image = XVBA_IMAGE(obj_subpicture->image_id); + if (obj_image) + destroy_hw_image(driver_data, obj_image); +} + +// Destroy subpicture +static void +destroy_subpicture( + xvba_driver_data_t *driver_data, + object_subpicture_p obj_subpicture +) +{ + object_surface_p obj_surface; + VAStatus status; + unsigned int i, n; + + destroy_subpicture_surface(driver_data, obj_subpicture); + + if (obj_subpicture->assocs) { + const unsigned int n_assocs = obj_subpicture->assocs_count; + for (i = 0, n = 0; i < n_assocs; i++) { + SubpictureAssociationP const assoc = obj_subpicture->assocs[0]; + if (!assoc) + continue; + obj_surface = XVBA_SURFACE(assoc->surface); + ASSERT(obj_surface); + if (!obj_surface) + continue; + status = subpicture_deassociate_1(obj_subpicture, obj_surface); + if (status == VA_STATUS_SUCCESS) + ++n; + } + if (n != n_assocs) + xvba_error_message("vaDestroySubpicture(): subpicture 0x%08x still " + "has %d surfaces associated to it\n", + obj_subpicture->base.id, n_assocs - n); + free(obj_subpicture->assocs); + obj_subpicture->assocs = NULL; + } + obj_subpicture->assocs_count = 0; + obj_subpicture->assocs_count_max = 0; + + obj_subpicture->image_id = VA_INVALID_ID; + object_heap_free(&driver_data->subpicture_heap, + (object_base_p)obj_subpicture); +} + +// vaQuerySubpictureFormats +VAStatus +xvba_QuerySubpictureFormats( + VADriverContextP ctx, + VAImageFormat *format_list, + unsigned int *flags, + unsigned int *num_formats +) +{ + int n; + + for (n = 0; xvba_subpic_formats_map[n].format != 0; n++) { + if (format_list) + format_list[n] = xvba_subpic_formats_map[n].va_format; + if (flags) + flags[n] = xvba_subpic_formats_map[n].va_flags; + } + + if (num_formats) + *num_formats = n; + + return VA_STATUS_SUCCESS; +} + +// vaCreateSubpicture +VAStatus +xvba_CreateSubpicture( + VADriverContextP ctx, + VAImageID image, + VASubpictureID *subpicture +) +{ + XVBA_DRIVER_DATA_INIT; + + if (!subpicture) + return VA_STATUS_ERROR_INVALID_PARAMETER; + + object_image_p obj_image = XVBA_IMAGE(image); + if (!obj_image) + return VA_STATUS_ERROR_INVALID_IMAGE; + + object_subpicture_p obj_subpicture; + obj_subpicture = create_subpicture(driver_data, obj_image); + if (!obj_subpicture) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + + *subpicture = obj_subpicture->base.id; + return VA_STATUS_SUCCESS; +} + +// vaDestroySubpicture +VAStatus +xvba_DestroySubpicture( + VADriverContextP ctx, + VASubpictureID subpicture +) +{ + XVBA_DRIVER_DATA_INIT; + + object_subpicture_p obj_subpicture = XVBA_SUBPICTURE(subpicture); + if (!obj_subpicture) + return VA_STATUS_ERROR_INVALID_SUBPICTURE; + + destroy_subpicture(driver_data, obj_subpicture); + return VA_STATUS_SUCCESS; +} + +// vaSetSubpictureImage +VAStatus +xvba_SetSubpictureImage( + VADriverContextP ctx, + VASubpictureID subpicture, + VAImageID image +) +{ + XVBA_DRIVER_DATA_INIT; + + object_subpicture_p obj_subpicture = XVBA_SUBPICTURE(subpicture); + if (!obj_subpicture) + return VA_STATUS_ERROR_INVALID_SUBPICTURE; + + object_image_p obj_image = XVBA_IMAGE(image); + if (!obj_image) + return VA_STATUS_ERROR_INVALID_IMAGE; + + obj_subpicture->image_id = obj_image->base.id; + return VA_STATUS_SUCCESS; +} + +// vaSetSubpicturePalette (not a PUBLIC interface) +VAStatus +xvba_SetSubpicturePalette( + VADriverContextP ctx, + VASubpictureID subpicture, + unsigned char *palette +) +{ + /* TODO */ + return VA_STATUS_ERROR_OPERATION_FAILED; +} + +// vaSetSubpictureChromaKey +VAStatus +xvba_SetSubpictureChromakey( + VADriverContextP ctx, + VASubpictureID subpicture, + unsigned int chromakey_min, + unsigned int chromakey_max, + unsigned int chromakey_mask +) +{ + XVBA_DRIVER_DATA_INIT; + + object_subpicture_p obj_subpicture = XVBA_SUBPICTURE(subpicture); + if (!obj_subpicture) + return VA_STATUS_ERROR_INVALID_SUBPICTURE; + + obj_subpicture->chromakey_min = chromakey_min; + obj_subpicture->chromakey_max = chromakey_max; + obj_subpicture->chromakey_mask = chromakey_mask; + return VA_STATUS_SUCCESS; +} + +// vaSetSubpictureGlobalAlpha +VAStatus +xvba_SetSubpictureGlobalAlpha( + VADriverContextP ctx, + VASubpictureID subpicture, + float global_alpha +) +{ + XVBA_DRIVER_DATA_INIT; + + object_subpicture_p obj_subpicture = XVBA_SUBPICTURE(subpicture); + if (!obj_subpicture) + return VA_STATUS_ERROR_INVALID_SUBPICTURE; + + obj_subpicture->alpha = global_alpha; + return VA_STATUS_SUCCESS; +} + +// vaAssociateSubpicture +VAStatus +xvba_AssociateSubpicture( + VADriverContextP ctx, + VASubpictureID subpicture, + VASurfaceID *target_surfaces, + int num_surfaces, + short src_x, + short src_y, + short dest_x, + short dest_y, + unsigned short width, + unsigned short height, + unsigned int flags +) +{ + XVBA_DRIVER_DATA_INIT; + + if (!target_surfaces || num_surfaces == 0) + return VA_STATUS_SUCCESS; + + object_subpicture_p obj_subpicture = XVBA_SUBPICTURE(subpicture); + if (!obj_subpicture) + return VA_STATUS_ERROR_INVALID_SUBPICTURE; + + VARectangle src_rect, dst_rect; + src_rect.x = src_x; + src_rect.y = src_y; + src_rect.width = width; + src_rect.height = height; + dst_rect.x = dest_x; + dst_rect.y = dest_y; + dst_rect.width = width; + dst_rect.height = height; + return associate_subpicture(driver_data, obj_subpicture, + target_surfaces, num_surfaces, + &src_rect, &dst_rect, flags); +} + +// vaAssociateSubpicture2 +VAStatus +xvba_AssociateSubpicture_full( + VADriverContextP ctx, + VASubpictureID subpicture, + VASurfaceID *target_surfaces, + int num_surfaces, + short src_x, + short src_y, + unsigned short src_width, + unsigned short src_height, + short dest_x, + short dest_y, + unsigned short dest_width, + unsigned short dest_height, + unsigned int flags +) +{ + XVBA_DRIVER_DATA_INIT; + + if (!target_surfaces || num_surfaces == 0) + return VA_STATUS_SUCCESS; + + object_subpicture_p obj_subpicture = XVBA_SUBPICTURE(subpicture); + if (!obj_subpicture) + return VA_STATUS_ERROR_INVALID_SUBPICTURE; + + VARectangle src_rect, dst_rect; + src_rect.x = src_x; + src_rect.y = src_y; + src_rect.width = src_width; + src_rect.height = src_height; + dst_rect.x = dest_x; + dst_rect.y = dest_y; + dst_rect.width = dest_width; + dst_rect.height = dest_height; + return associate_subpicture(driver_data, obj_subpicture, + target_surfaces, num_surfaces, + &src_rect, &dst_rect, flags); +} + +// vaDeassociateSubpicture +VAStatus +xvba_DeassociateSubpicture( + VADriverContextP ctx, + VASubpictureID subpicture, + VASurfaceID *target_surfaces, + int num_surfaces +) +{ + XVBA_DRIVER_DATA_INIT; + + if (!target_surfaces || num_surfaces == 0) + return VA_STATUS_SUCCESS; + + object_subpicture_p obj_subpicture = XVBA_SUBPICTURE(subpicture); + if (!obj_subpicture) + return VA_STATUS_ERROR_INVALID_SUBPICTURE; + + return deassociate_subpicture(driver_data, obj_subpicture, + target_surfaces, num_surfaces); +} diff --git a/src/xvba_subpic.h b/src/xvba_subpic.h new file mode 100644 index 0000000..6796993 --- /dev/null +++ b/src/xvba_subpic.h @@ -0,0 +1,175 @@ +/* + * xvba_subpic.h - XvBA backend for VA-API (VA subpictures) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef XVBA_SUBPIC_H +#define XVBA_SUBPIC_H + +#include "xvba_video.h" + +typedef struct object_subpicture object_subpicture_t; +typedef struct object_subpicture *object_subpicture_p; + +struct object_subpicture { + struct object_base base; + VAImageID image_id; + SubpictureAssociationP *assocs; + unsigned int assocs_count; + unsigned int assocs_count_max; + unsigned int chromakey_min; + unsigned int chromakey_max; + unsigned int chromakey_mask; + float alpha; +}; + +// Destroy XvBA surface bound to VA subpictures +void +destroy_subpicture_surface( + xvba_driver_data_t *driver_data, + object_subpicture_p obj_subpicture +) attribute_hidden; + +// Associate one surface to the subpicture +VAStatus +subpicture_associate_1( + object_subpicture_p obj_subpicture, + object_surface_p obj_surface, + const VARectangle *src_rect, + const VARectangle *dst_rect, + unsigned int flags +) attribute_hidden; + +// Deassociate one surface from the subpicture +VAStatus +subpicture_deassociate_1( + object_subpicture_p obj_subpicture, + object_surface_p obj_surface +) attribute_hidden; + +// Commit subpicture to video surface +VAStatus +commit_subpicture( + xvba_driver_data_t *driver_data, + object_subpicture_p obj_subpicture, + void *session, + unsigned int flags +) attribute_hidden; + +// vaQuerySubpictureFormats +VAStatus +xvba_QuerySubpictureFormats( + VADriverContextP ctx, + VAImageFormat *format_list, + unsigned int *flags, + unsigned int *num_formats +) attribute_hidden; + +// vaCreateSubpicture +VAStatus +xvba_CreateSubpicture( + VADriverContextP ctx, + VAImageID image, + VASubpictureID *subpicture +) attribute_hidden; + +// vaDestroySubpicture +VAStatus +xvba_DestroySubpicture( + VADriverContextP ctx, + VASubpictureID subpicture +) attribute_hidden; + +// vaSetSubpictureImage +VAStatus +xvba_SetSubpictureImage( + VADriverContextP ctx, + VASubpictureID subpicture, + VAImageID image +) attribute_hidden; + +// vaSetSubpicturePalette (not a PUBLIC interface) +VAStatus +xvba_SetSubpicturePalette( + VADriverContextP ctx, + VASubpictureID subpicture, + unsigned char *palette +) attribute_hidden; + +// vaSetSubpictureChromaKey +VAStatus +xvba_SetSubpictureChromakey( + VADriverContextP ctx, + VASubpictureID subpicture, + unsigned int chromakey_min, + unsigned int chromakey_max, + unsigned int chromakey_mask +) attribute_hidden; + +// vaSetSubpictureGlobalAlpha +VAStatus +xvba_SetSubpictureGlobalAlpha( + VADriverContextP ctx, + VASubpictureID subpicture, + float global_alpha +) attribute_hidden; + +// vaAssociateSubpicture +VAStatus +xvba_AssociateSubpicture( + VADriverContextP ctx, + VASubpictureID subpicture, + VASurfaceID *target_surfaces, + int num_surfaces, + short src_x, + short src_y, + short dest_x, + short dest_y, + unsigned short width, + unsigned short height, + unsigned int flags +) attribute_hidden; + +// vaAssociateSubpicture2 +VAStatus +xvba_AssociateSubpicture_full( + VADriverContextP ctx, + VASubpictureID subpicture, + VASurfaceID *target_surfaces, + int num_surfaces, + short src_x, + short src_y, + unsigned short src_width, + unsigned short src_height, + short dest_x, + short dest_y, + unsigned short dest_width, + unsigned short dest_height, + unsigned int flags +) attribute_hidden; + +// vaDeassociateSubpicture +VAStatus +xvba_DeassociateSubpicture( + VADriverContextP ctx, + VASubpictureID subpicture, + VASurfaceID *target_surfaces, + int num_surfaces +) attribute_hidden; + +#endif /* XVBA_SUBPIC_H */ diff --git a/src/xvba_video.c b/src/xvba_video.c new file mode 100644 index 0000000..b47b2ba --- /dev/null +++ b/src/xvba_video.c @@ -0,0 +1,1131 @@ +/* + * xvba_video.c - XvBA backend for VA-API (VA context, config, surfaces) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "xvba_video.h" +#include "xvba_dump.h" +#include "xvba_buffer.h" +#include "xvba_decode.h" +#include "xvba_image.h" +#include "xvba_subpic.h" +#include "xvba_video_x11.h" +#if USE_GLX +#include "xvba_video_glx.h" +#endif +#include "utils.h" +#include "utils_x11.h" + +#define DEBUG 1 +#include "debug.h" + + +// Translates VAProfile to XVBACodec +static XVBACodec get_XVBACodec(VAProfile profile) +{ + switch (profile) { + case VAProfileMPEG2Simple: + case VAProfileMPEG2Main: + return XVBA_CODEC_MPEG2; + case VAProfileMPEG4Simple: + case VAProfileMPEG4AdvancedSimple: + case VAProfileMPEG4Main: + break; +#if VA_CHECK_VERSION(0,30,0) + case VAProfileH263Baseline: + break; +#endif + case VAProfileH264Baseline: + case VAProfileH264Main: + case VAProfileH264High: + return XVBA_CODEC_H264; + case VAProfileVC1Simple: + case VAProfileVC1Main: + case VAProfileVC1Advanced: + return XVBA_CODEC_VC1; +#if VA_CHECK_VERSION(0,31,0) + case VAProfileJPEGBaseline: + break; +#endif + } + return 0; +} + +// Destroys XvBA subpictures bound to a VA surface +static void +destroy_subpictures( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface +) +{ + unsigned int i; + for (i = 0; i < obj_surface->assocs_count; i++) { + SubpictureAssociationP const assoc = obj_surface->assocs[i]; + destroy_subpicture_surface( + driver_data, + XVBA_SUBPICTURE(assoc->subpicture) + ); + } +} + +// Creates XvBA surface +static VAStatus +ensure_surface_size( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface, + unsigned int width, + unsigned int height +) +{ + if (obj_surface->xvba_surface && + obj_surface->xvba_surface_width == width && + obj_surface->xvba_surface_height == height) + return VA_STATUS_SUCCESS; + + if (obj_surface->xvba_surface) { + xvba_destroy_surface(obj_surface->xvba_surface); + obj_surface->xvba_surface = NULL; + } + + object_context_p obj_context = XVBA_CONTEXT(obj_surface->va_context); + if (!obj_context) + return VA_STATUS_ERROR_INVALID_CONTEXT; + + obj_surface->xvba_surface = xvba_create_surface( + obj_context->xvba_session, + width, + height, + XVBA_NV12 + ); + if (!obj_surface->xvba_surface) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + + obj_surface->xvba_surface_width = width; + obj_surface->xvba_surface_height = height; + return VA_STATUS_SUCCESS; +} + +// Destroys XvBA surface and associated buffers +static void +destroy_surface(xvba_driver_data_t *driver_data, object_surface_p obj_surface) +{ + destroy_subpictures(driver_data, obj_surface); + destroy_surface_buffers(driver_data, obj_surface); + + if (obj_surface->xvba_surface) { + xvba_destroy_surface(obj_surface->xvba_surface); + obj_surface->xvba_surface = NULL; + } +} + +// Query surface status +int +query_surface_status( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_surface_p obj_surface, + VASurfaceStatus *surface_status +) +{ + int status; + + if (surface_status) + *surface_status = VASurfaceReady; + + if (!obj_surface) + return 0; + + switch (obj_surface->va_surface_status) { + case VASurfaceRendering: /* Rendering (XvBA level) */ + ASSERT(obj_surface->used_for_decoding); + if (!obj_context || !obj_context->xvba_decoder) + return 0; + if (!obj_surface->xvba_surface) + return 0; + status = xvba_sync_surface( + obj_context->xvba_decoder, + obj_surface->xvba_surface, + XVBA_GET_SURFACE_STATUS + ); + if (status < 0) + return -1; + if (status == XVBA_COMPLETED) + obj_surface->va_surface_status = VASurfaceReady; + break; + case VASurfaceDisplaying: + status = query_surface_status_glx(driver_data, obj_surface); + if (status < 0) + return -1; + if (status == XVBA_COMPLETED) + obj_surface->va_surface_status = VASurfaceReady; + break; + } + + if (surface_status) + *surface_status = obj_surface->va_surface_status; + return 0; +} + +// Synchronize surface +int +sync_surface( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_surface_p obj_surface +) +{ + VASurfaceStatus surface_status; + int status; + + while ((status = query_surface_status(driver_data, obj_context, obj_surface, &surface_status)) == 0 && + surface_status != VASurfaceReady) + delay_usec(XVBA_SYNC_DELAY); + return status; +} + +// Add subpicture association to surface +// NOTE: the subpicture owns the SubpictureAssociation object +int surface_add_association( + object_surface_p obj_surface, + SubpictureAssociationP assoc +) +{ + /* Check that we don't already have this association */ + if (obj_surface->assocs) { + unsigned int i; + for (i = 0; i < obj_surface->assocs_count; i++) { + if (obj_surface->assocs[i] == assoc) + return 0; + if (obj_surface->assocs[i]->subpicture == assoc->subpicture) { + /* XXX: this should not happen, but replace it in the interim */ + ASSERT(obj_surface->assocs[i]->surface == assoc->surface); + obj_surface->assocs[i] = assoc; + return 0; + } + } + } + + /* Check that we have not reached the maximum subpictures capacity yet */ + if (obj_surface->assocs_count >= XVBA_MAX_SUBPICTURES) + return -1; + + /* Append this subpicture association */ + SubpictureAssociationP *assocs; + assocs = realloc_buffer( + &obj_surface->assocs, + &obj_surface->assocs_count_max, + 1 + obj_surface->assocs_count, + sizeof(obj_surface->assocs[0]) + ); + if (!assocs) + return -1; + + assocs[obj_surface->assocs_count++] = assoc; + return 0; +} + +// Remove subpicture association from surface +// NOTE: the subpicture owns the SubpictureAssociation object +int surface_remove_association( + object_surface_p obj_surface, + SubpictureAssociationP assoc +) +{ + if (!obj_surface->assocs || obj_surface->assocs_count == 0) + return -1; + + unsigned int i; + const unsigned int last = obj_surface->assocs_count - 1; + for (i = 0; i <= last; i++) { + if (obj_surface->assocs[i] == assoc) { + /* Swap with the last subpicture */ + obj_surface->assocs[i] = obj_surface->assocs[last]; + obj_surface->assocs[last] = NULL; + obj_surface->assocs_count--; + return 0; + } + } + return -1; +} + +// vaGetConfigAttributes +VAStatus +xvba_GetConfigAttributes( + VADriverContextP ctx, + VAProfile profile, + VAEntrypoint entrypoint, + VAConfigAttrib *attrib_list, + int num_attribs +) +{ + XVBA_DRIVER_DATA_INIT; + + if (!has_decoder(driver_data, profile, entrypoint)) + return VA_STATUS_ERROR_UNSUPPORTED_PROFILE; + + int i; + for (i = 0; i < num_attribs; i++) { + switch (attrib_list[i].type) { + case VAConfigAttribRTFormat: + attrib_list[i].value = VA_RT_FORMAT_YUV420; + break; + default: + attrib_list[i].value = VA_ATTRIB_NOT_SUPPORTED; + break; + } + } + return VA_STATUS_SUCCESS; +} + +static VAStatus +xvba_update_attribute(object_config_p obj_config, VAConfigAttrib *attrib) +{ + int i; + + /* Check existing attributes */ + for (i = 0; obj_config->attrib_count < i; i++) { + if (obj_config->attrib_list[i].type == attrib->type) { + /* Update existing attribute */ + obj_config->attrib_list[i].value = attrib->value; + return VA_STATUS_SUCCESS; + } + } + if (obj_config->attrib_count < XVBA_MAX_CONFIG_ATTRIBUTES) { + i = obj_config->attrib_count; + obj_config->attrib_list[i].type = attrib->type; + obj_config->attrib_list[i].value = attrib->value; + obj_config->attrib_count++; + return VA_STATUS_SUCCESS; + } + return VA_STATUS_ERROR_MAX_NUM_EXCEEDED; +} + +// vaCreateConfig +VAStatus +xvba_CreateConfig( + VADriverContextP ctx, + VAProfile profile, + VAEntrypoint entrypoint, + VAConfigAttrib *attrib_list, + int num_attribs, + VAConfigID *config_id +) +{ + XVBA_DRIVER_DATA_INIT; + + VAStatus va_status; + int configID; + object_config_p obj_config; + int i; + + /* Validate profile and entrypoint */ + switch (profile) { + case VAProfileMPEG2Simple: + case VAProfileMPEG2Main: + if (entrypoint == VAEntrypointIDCT || entrypoint == VAEntrypointVLD) + va_status = VA_STATUS_SUCCESS; + else + va_status = VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT; + break; + case VAProfileH264Baseline: + case VAProfileH264Main: + case VAProfileH264High: + if (entrypoint == VAEntrypointVLD) + va_status = VA_STATUS_SUCCESS; + else + va_status = VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT; + break; + case VAProfileVC1Simple: + case VAProfileVC1Main: + case VAProfileVC1Advanced: + if (entrypoint == VAEntrypointVLD) + va_status = VA_STATUS_SUCCESS; + else + va_status = VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT; + break; + default: + va_status = VA_STATUS_ERROR_UNSUPPORTED_PROFILE; + break; + } + + if (va_status != VA_STATUS_SUCCESS) + return va_status; + + if (!has_decoder(driver_data, profile, entrypoint)) + return VA_STATUS_ERROR_UNSUPPORTED_PROFILE; + + configID = object_heap_allocate(&driver_data->config_heap); + obj_config = XVBA_CONFIG(configID); + if (!obj_config) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + + obj_config->profile = profile; + obj_config->entrypoint = entrypoint; + obj_config->attrib_list[0].type = VAConfigAttribRTFormat; + obj_config->attrib_list[0].value = VA_RT_FORMAT_YUV420; + obj_config->attrib_count = 1; + + for (i = 0; i < num_attribs; i++) { + va_status = xvba_update_attribute(obj_config, &attrib_list[i]); + if (va_status != VA_STATUS_SUCCESS) { + xvba_DestroyConfig(ctx, configID); + return va_status; + } + } + + if (config_id) + *config_id = configID; + + return va_status; +} + +// vaDestroyConfig +VAStatus +xvba_DestroyConfig( + VADriverContextP ctx, + VAConfigID config_id +) +{ + XVBA_DRIVER_DATA_INIT; + + object_config_p obj_config = XVBA_CONFIG(config_id); + if (!obj_config) + return VA_STATUS_ERROR_INVALID_CONFIG; + + object_heap_free(&driver_data->config_heap, (object_base_p)obj_config); + return VA_STATUS_SUCCESS; +} + +// vaQueryConfigAttributes +VAStatus +xvba_QueryConfigAttributes( + VADriverContextP ctx, + VAConfigID config_id, + VAProfile *profile, + VAEntrypoint *entrypoint, + VAConfigAttrib *attrib_list, + int *num_attribs +) +{ + XVBA_DRIVER_DATA_INIT; + + VAStatus va_status = VA_STATUS_SUCCESS; + object_config_p obj_config; + int i; + + obj_config = XVBA_CONFIG(config_id); + if (!obj_config) + return VA_STATUS_ERROR_INVALID_CONFIG; + + if (profile) + *profile = obj_config->profile; + + if (entrypoint) + *entrypoint = obj_config->entrypoint; + + if (num_attribs) + *num_attribs = obj_config->attrib_count; + + if (attrib_list) { + for (i = 0; i < obj_config->attrib_count; i++) + attrib_list[i] = obj_config->attrib_list[i]; + } + + return va_status; +} + +// vaCreateSurfaces +VAStatus +xvba_CreateSurfaces( + VADriverContextP ctx, + int width, + int height, + int format, + int num_surfaces, + VASurfaceID *surfaces +) +{ + XVBA_DRIVER_DATA_INIT; + + D(bug("vaCreateSurfaces(): size %dx%d, format %s\n", width, height, + string_of_VAConfigAttribRTFormat(format))); + + VAStatus va_status = VA_STATUS_SUCCESS; + int i; + + /* We only support one format */ + if (format != VA_RT_FORMAT_YUV420) + return VA_STATUS_ERROR_UNSUPPORTED_RT_FORMAT; + + for (i = 0; i < num_surfaces; i++) { + int va_surface = object_heap_allocate(&driver_data->surface_heap); + object_surface_p obj_surface = XVBA_SURFACE(va_surface); + if (!obj_surface) { + va_status = VA_STATUS_ERROR_ALLOCATION_FAILED; + break; + } + D(bug(" surface 0x%08x\n", va_surface)); + obj_surface->va_context = VA_INVALID_ID; + obj_surface->va_surface_status = VASurfaceReady; + obj_surface->xvba_surface = NULL; + obj_surface->xvba_surface_width = width; + obj_surface->xvba_surface_height = height; + obj_surface->output_surfaces = NULL; + obj_surface->output_surfaces_count = 0; + obj_surface->output_surfaces_count_max = 0; + obj_surface->width = width; + obj_surface->height = height; + obj_surface->gl_surface = NULL; + obj_surface->pic_desc_buffer = NULL; + obj_surface->iq_matrix_buffer = NULL; + obj_surface->data_buffer = NULL; + obj_surface->data_ctrl_buffers = NULL; + obj_surface->data_ctrl_buffers_count = 0; + obj_surface->data_ctrl_buffers_count_max = 0; + obj_surface->assocs = NULL; + obj_surface->assocs_count = 0; + obj_surface->assocs_count_max = 0; + obj_surface->putimage_hacks = NULL; + surfaces[i] = va_surface; + } + + /* Error recovery */ + if (va_status != VA_STATUS_SUCCESS) + xvba_DestroySurfaces(ctx, surfaces, i); + + return va_status; +} + +// vaDestroySurfaces +VAStatus +xvba_DestroySurfaces( + VADriverContextP ctx, + VASurfaceID *surface_list, + int num_surfaces +) +{ + XVBA_DRIVER_DATA_INIT; + + D(bug("vaDestroySurfaces()\n")); + + int i, j, n; + for (i = num_surfaces - 1; i >= 0; i--) { + object_surface_p obj_surface = XVBA_SURFACE(surface_list[i]); + if (!obj_surface) + continue; + + D(bug(" surface 0x%08x\n", obj_surface->base.id)); + destroy_surface(driver_data, obj_surface); + +#if USE_GLX + if (obj_surface->gl_surface) { + glx_surface_unref(driver_data, obj_surface->gl_surface); + obj_surface->gl_surface = NULL; + } +#endif + + for (j = 0; j < obj_surface->output_surfaces_count; j++) { + output_surface_unref(driver_data, obj_surface->output_surfaces[j]); + obj_surface->output_surfaces[j] = NULL; + } + free(obj_surface->output_surfaces); + obj_surface->output_surfaces_count = 0; + obj_surface->output_surfaces_count_max = 0; + + if (obj_surface->assocs) { + object_subpicture_p obj_subpicture; + VAStatus status; + const unsigned int n_assocs = obj_surface->assocs_count; + + for (j = 0, n = 0; j < n_assocs; j++) { + SubpictureAssociationP const assoc = obj_surface->assocs[0]; + if (!assoc) + continue; + obj_subpicture = XVBA_SUBPICTURE(assoc->subpicture); + if (!obj_subpicture) + continue; + status = subpicture_deassociate_1(obj_subpicture, obj_surface); + if (status == VA_STATUS_SUCCESS) + ++n; + } + if (n != n_assocs) + xvba_error_message("vaDestroySurfaces(): surface 0x%08x still " + "has %d subpictures associated to it\n", + obj_surface->base.id, n_assocs - n); + free(obj_surface->assocs); + obj_surface->assocs = NULL; + } + obj_surface->assocs_count = 0; + obj_surface->assocs_count_max = 0; + + putimage_hacks_disable(driver_data, obj_surface); + + object_heap_free(&driver_data->surface_heap, (object_base_p)obj_surface); + } + return VA_STATUS_SUCCESS; +} + +// vaCreateContext +VAStatus +xvba_CreateContext( + VADriverContextP ctx, + VAConfigID config_id, + int picture_width, + int picture_height, + int flag, + VASurfaceID *render_targets, + int num_render_targets, + VAContextID *context +) +{ + XVBA_DRIVER_DATA_INIT; + + if (context) + *context = VA_INVALID_ID; + + D(bug("vaCreateContext(): config 0x%08x, size %dx%d\n", config_id, + picture_width, picture_height)); + + object_config_p const obj_config = XVBA_CONFIG(config_id); + if (!obj_config) + return VA_STATUS_ERROR_INVALID_CONFIG; + + /* XXX: validate flag */ + + int i; + for (i = 0; i < num_render_targets; i++) { + object_surface_p const obj_surface = XVBA_SURFACE(render_targets[i]); + if (!obj_surface) + return VA_STATUS_ERROR_INVALID_SURFACE; + if (obj_surface->va_context != VA_INVALID_ID) { + xvba_error_message("vaCreateContext(): surface 0x%08x is already " + "bound to context 0x%08x\n", + obj_surface->base.id, obj_surface->va_context); + return VA_STATUS_ERROR_INVALID_SURFACE; + } + if (obj_surface->width != picture_width || + obj_surface->height != picture_height) + return VA_STATUS_ERROR_INVALID_SURFACE; + } + + VAContextID context_id = object_heap_allocate(&driver_data->context_heap); + object_context_p const obj_context = XVBA_CONTEXT(context_id); + if (!obj_context) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + + /* XXX: workaround XvBA internal bugs. Round up so that to create + surfaces of the "expected" size */ + picture_width = (picture_width + 15) & -16; + picture_height = (picture_height + 15) & -16; + + obj_context->va_config = config_id; + obj_context->picture_width = picture_width; + obj_context->picture_height = picture_height; + obj_context->flags = flag; + obj_context->num_render_targets = num_render_targets; + obj_context->render_targets = (VASurfaceID *) + calloc(num_render_targets, sizeof(VASurfaceID)); + obj_context->current_render_target = VA_INVALID_SURFACE; + obj_context->xvba_codec = get_XVBACodec(obj_config->profile); + obj_context->xvba_session = NULL; + obj_context->xvba_decoder = NULL; + obj_context->va_buffers = NULL; + obj_context->va_buffers_count = 0; + obj_context->va_buffers_count_max = 0; + obj_context->data_buffer = NULL; + obj_context->slice_count = 0; + + if (!obj_context->render_targets) { + xvba_DestroyContext(ctx, context_id); + return VA_STATUS_ERROR_ALLOCATION_FAILED; + } + + VAStatus va_status = create_decoder(driver_data, obj_context); + if (va_status != VA_STATUS_SUCCESS) { + xvba_DestroyContext(ctx, context_id); + return va_status; + } + + for (i = 0; i < num_render_targets; i++) { + object_surface_t * const obj_surface = XVBA_SURFACE(render_targets[i]); + obj_context->render_targets[i] = render_targets[i]; + obj_surface->va_context = context_id; + + D(bug(" surface 0x%08x\n", render_targets[i])); + va_status = ensure_surface_size( + driver_data, + obj_surface, + picture_width, + picture_height + ); + if (va_status != VA_STATUS_SUCCESS) { + xvba_DestroyContext(ctx, context_id); + return va_status; + } + } + + D(bug(" context 0x%08x\n", context_id)); + if (context) + *context = context_id; + return VA_STATUS_SUCCESS; +} + +// vaDestroyContext +VAStatus +xvba_DestroyContext( + VADriverContextP ctx, + VAContextID context +) +{ + XVBA_DRIVER_DATA_INIT; + + D(bug("vaDestroyContext(): context 0x%08x\n", context)); + + object_context_p obj_context = XVBA_CONTEXT(context); + if (!obj_context) + return VA_STATUS_ERROR_INVALID_CONTEXT; + + destroy_decoder(driver_data, obj_context); + + if (obj_context->va_buffers) { + destroy_va_buffers(driver_data, obj_context); + free(obj_context->va_buffers); + obj_context->va_buffers = NULL; + } + + if (obj_context->render_targets) { + int i; + for (i = 0; i < obj_context->num_render_targets; i++) { + object_surface_p obj_surface; + obj_surface = XVBA_SURFACE(obj_context->render_targets[i]); + if (obj_surface) + obj_surface->va_context = VA_INVALID_ID; + } + free(obj_context->render_targets); + obj_context->render_targets = NULL; + } + + obj_context->va_config = VA_INVALID_ID; + obj_context->current_render_target = VA_INVALID_SURFACE; + obj_context->picture_width = 0; + obj_context->picture_height = 0; + obj_context->num_render_targets = 0; + obj_context->flags = 0; + + object_heap_free(&driver_data->context_heap, (object_base_p)obj_context); + return VA_STATUS_SUCCESS; +} + +// vaQuerySurfaceStatus +VAStatus +xvba_QuerySurfaceStatus( + VADriverContextP ctx, + VASurfaceID render_target, + VASurfaceStatus *status +) +{ + XVBA_DRIVER_DATA_INIT; + + object_surface_p obj_surface = XVBA_SURFACE(render_target); + if (!obj_surface) + return VA_STATUS_ERROR_INVALID_SURFACE; + + object_context_p obj_context = XVBA_CONTEXT(obj_surface->va_context); + if (!obj_context) + return VA_STATUS_ERROR_INVALID_CONTEXT; + + if (query_surface_status(driver_data, obj_context, obj_surface, status) < 0) + return VA_STATUS_ERROR_UNKNOWN; + + return VA_STATUS_SUCCESS; +} + +// vaSyncSurface +VAStatus +xvba_SyncSurface2( + VADriverContextP ctx, + VASurfaceID render_target +) +{ + XVBA_DRIVER_DATA_INIT; + + object_surface_p obj_surface = XVBA_SURFACE(render_target); + if (!obj_surface) + return VA_STATUS_ERROR_INVALID_SURFACE; + + if (sync_surface(driver_data, NULL, obj_surface) < 0) + return VA_STATUS_ERROR_UNKNOWN; + + return VA_STATUS_SUCCESS; +} + +VAStatus +xvba_SyncSurface3( + VADriverContextP ctx, + VAContextID context, + VASurfaceID render_target +) +{ + XVBA_DRIVER_DATA_INIT; + + object_context_p obj_context = XVBA_CONTEXT(context); + if (!obj_context) + return VA_STATUS_ERROR_INVALID_CONTEXT; + + object_surface_p obj_surface = XVBA_SURFACE(render_target); + if (!obj_surface) + return VA_STATUS_ERROR_INVALID_SURFACE; + + if (sync_surface(driver_data, obj_context, obj_surface) < 0) + return VA_STATUS_ERROR_UNKNOWN; + + return VA_STATUS_SUCCESS; +} + + +// Ensure VA Display Attributes are initialized +static int ensure_display_attributes(xvba_driver_data_t *driver_data) +{ + VADisplayAttribute *attr; + + if (driver_data->va_display_attrs_count > 0) + return 0; + + memset(driver_data->va_display_attrs_mtime, 0, + sizeof(driver_data->va_display_attrs_mtime)); + + cm_set_identity(driver_data->cm_brightness); + cm_set_identity(driver_data->cm_contrast); + cm_set_identity(driver_data->cm_saturation); + cm_set_identity(driver_data->cm_hue); + driver_data->cm_composite_ok = 0; + + attr = &driver_data->va_display_attrs[0]; + + attr->type = VADisplayAttribDirectSurface; + attr->value = 1; /* GLX: async transfer */ + attr->min_value = attr->value; + attr->max_value = attr->value; + attr->flags = VA_DISPLAY_ATTRIB_GETTABLE; + attr++; + + attr->type = VADisplayAttribBackgroundColor; + attr->value = WhitePixel(driver_data->x11_dpy, driver_data->x11_screen); + attr->min_value = 0; + attr->max_value = 0xffffff; + attr->flags = VA_DISPLAY_ATTRIB_GETTABLE|VA_DISPLAY_ATTRIB_SETTABLE; + attr++; + + attr->type = VADisplayAttribBrightness; + attr->value = 0; + attr->min_value = -100; + attr->max_value = 100; + attr->flags = VA_DISPLAY_ATTRIB_GETTABLE|VA_DISPLAY_ATTRIB_SETTABLE; + attr++; + + attr->type = VADisplayAttribContrast; + attr->value = 0; + attr->min_value = -100; + attr->max_value = 100; + attr->flags = VA_DISPLAY_ATTRIB_GETTABLE|VA_DISPLAY_ATTRIB_SETTABLE; + attr++; + + attr->type = VADisplayAttribHue; + attr->value = 0; + attr->min_value = -100; + attr->max_value = 100; + attr->flags = VA_DISPLAY_ATTRIB_GETTABLE|VA_DISPLAY_ATTRIB_SETTABLE; + attr++; + + attr->type = VADisplayAttribSaturation; + attr->value = 0; + attr->min_value = -100; + attr->max_value = 100; + attr->flags = VA_DISPLAY_ATTRIB_GETTABLE|VA_DISPLAY_ATTRIB_SETTABLE; + attr++; + + driver_data->va_display_attrs_count = attr - driver_data->va_display_attrs; + ASSERT(driver_data->va_display_attrs_count <= XVBA_MAX_DISPLAY_ATTRIBUTES); + return 0; +} + +// Look up for the specified VA display attribute +static VADisplayAttribute * +get_display_attribute( + xvba_driver_data_t *driver_data, + VADisplayAttribType type +) +{ + if (ensure_display_attributes(driver_data) < 0) + return NULL; + + unsigned int i; + for (i = 0; i < driver_data->va_display_attrs_count; i++) { + if (driver_data->va_display_attrs[i].type == type) + return &driver_data->va_display_attrs[i]; + } + return NULL; +} + +// vaQueryDisplayAttributes +VAStatus +xvba_QueryDisplayAttributes( + VADriverContextP ctx, + VADisplayAttribute *attr_list, + int *num_attributes +) +{ + XVBA_DRIVER_DATA_INIT; + + if (ensure_display_attributes(driver_data) < 0) + return VA_STATUS_ERROR_OPERATION_FAILED; + + if (attr_list) + memcpy(attr_list, driver_data->va_display_attrs, + driver_data->va_display_attrs_count * sizeof(attr_list[0])); + + if (num_attributes) + *num_attributes = driver_data->va_display_attrs_count; + + return VA_STATUS_SUCCESS; +} + +// vaGetDisplayAttributes +VAStatus +xvba_GetDisplayAttributes( + VADriverContextP ctx, + VADisplayAttribute *attr_list, + int num_attributes +) +{ + XVBA_DRIVER_DATA_INIT; + + unsigned int i; + for (i = 0; i < num_attributes; i++) { + VADisplayAttribute * const dst_attr = &attr_list[i]; + VADisplayAttribute *src_attr; + + src_attr = get_display_attribute(driver_data, dst_attr->type); + if (src_attr && (src_attr->flags & VA_DISPLAY_ATTRIB_GETTABLE) != 0) { + dst_attr->min_value = src_attr->min_value; + dst_attr->max_value = src_attr->max_value; + dst_attr->value = src_attr->value; + } + else + dst_attr->flags &= ~VA_DISPLAY_ATTRIB_GETTABLE; + } + return VA_STATUS_SUCCESS; +} + +// Normalize -100..100 value into the specified range +static float get_normalized_value( + const VADisplayAttribute *attr, + float vstart, + float vend, + float vdefault +) +{ + return (vdefault + (attr->value / 100.0f) * + (attr->value >= 0 ? (vend - vdefault) : (vdefault - vstart))); +} + +// vaSetDisplayAttributes +VAStatus +xvba_SetDisplayAttributes( + VADriverContextP ctx, + VADisplayAttribute *attr_list, + int num_attributes +) +{ + XVBA_DRIVER_DATA_INIT; + + unsigned int i; + for (i = 0; i < num_attributes; i++) { + VADisplayAttribute * const src_attr = &attr_list[i]; + VADisplayAttribute *dst_attr; + + dst_attr = get_display_attribute(driver_data, src_attr->type); + if (!dst_attr) + return VA_STATUS_ERROR_ATTR_NOT_SUPPORTED; + + if ((dst_attr->flags & VA_DISPLAY_ATTRIB_SETTABLE) != 0) { + int value_changed = dst_attr->value != src_attr->value; + dst_attr->value = src_attr->value; + + int procamp_changed = 0; + float value, start, end, def; + switch (dst_attr->type) { + case VADisplayAttribBackgroundColor: + driver_data->va_background_color = dst_attr; + break; + case VADisplayAttribBrightness: + if (value_changed) { + cm_get_brightness_range(&start, &end, &def); + value = get_normalized_value(dst_attr, start, end, def); + cm_set_brightness(driver_data->cm_brightness, value); + procamp_changed = 1; + } + break; + case VADisplayAttribContrast: + if (value_changed) { + cm_get_contrast_range(&start, &end, &def); + value = get_normalized_value(dst_attr, start, end, def); + cm_set_contrast(driver_data->cm_contrast, value); + procamp_changed = 1; + } + break; + case VADisplayAttribSaturation: + if (value_changed) { + cm_get_saturation_range(&start, &end, &def); + value = get_normalized_value(dst_attr, start, end, def); + cm_set_saturation(driver_data->cm_saturation, value); + procamp_changed = 1; + } + break; + case VADisplayAttribHue: + if (value_changed) { + cm_get_hue_range(&start, &end, &def); + value = get_normalized_value(dst_attr, start, end, def); + cm_set_hue(driver_data->cm_hue, value); + procamp_changed = 1; + } + break; + default: + break; + } + + if (procamp_changed) { + cm_composite( + driver_data->cm_composite, + driver_data->cm_brightness, + driver_data->cm_contrast, + driver_data->cm_saturation, + driver_data->cm_hue + ); + driver_data->cm_composite_ok = 1; + } + + static uint64_t mtime; + const int dst_attr_index = dst_attr - driver_data->va_display_attrs; + ASSERT(dst_attr_index < XVBA_MAX_DISPLAY_ATTRIBUTES); + driver_data->va_display_attrs_mtime[dst_attr_index] = ++mtime; + } + } + return VA_STATUS_SUCCESS; +} + +// vaDbgCopySurfaceToBuffer (not a PUBLIC interface) +VAStatus +xvba_DbgCopySurfaceToBuffer( + VADriverContextP ctx, + VASurfaceID surface, + void **buffer, + unsigned int *stride +) +{ + /* TODO */ + return VA_STATUS_ERROR_UNKNOWN; +} + +#if VA_CHECK_VERSION(0,30,0) +// vaCreateSurfaceFromCIFrame +VAStatus +xvba_CreateSurfaceFromCIFrame( + VADriverContextP ctx, + unsigned long frame_id, + VASurfaceID *surface +) +{ + /* TODO */ + return VA_STATUS_ERROR_UNKNOWN; +} + +// vaCreateSurfaceFromV4L2Buf +VAStatus +xvba_CreateSurfaceFromV4L2Buf( + VADriverContextP ctx, + int v4l2_fd, + struct v4l2_format *v4l2_fmt, + struct v4l2_buffer *v4l2_buf, + VASurfaceID *surface +) +{ + /* TODO */ + return VA_STATUS_ERROR_UNKNOWN; +} + +// vaCopySurfaceToBuffer +VAStatus +xvba_CopySurfaceToBuffer( + VADriverContextP ctx, + VASurfaceID surface, + unsigned int *fourcc, + unsigned int *luma_stride, + unsigned int *chroma_u_stride, + unsigned int *chroma_v_stride, + unsigned int *luma_offset, + unsigned int *chroma_u_offset, + unsigned int *chroma_v_offset, + void **buffer +) +{ + /* TODO */ + return VA_STATUS_ERROR_UNKNOWN; +} +#endif + +#if VA_CHECK_VERSION(0,31,1) +// vaLockSurface +VAStatus +xvba_LockSurface( + VADriverContextP ctx, + VASurfaceID surface, + unsigned int *fourcc, + unsigned int *luma_stride, + unsigned int *chroma_u_stride, + unsigned int *chroma_v_stride, + unsigned int *luma_offset, + unsigned int *chroma_u_offset, + unsigned int *chroma_v_offset, + unsigned int *buffer_name, + void **buffer +) +{ + if (fourcc) *fourcc = VA_FOURCC('N','V','1','2'); + if (luma_stride) *luma_stride = 0; + if (chroma_u_stride) *chroma_u_stride = 0; + if (chroma_v_stride) *chroma_v_stride = 0; + if (luma_offset) *luma_offset = 0; + if (chroma_u_offset) *chroma_u_offset = 0; + if (chroma_v_offset) *chroma_v_offset = 0; + if (buffer_name) *buffer_name = 0; + if (buffer) *buffer = NULL; + return VA_STATUS_SUCCESS; +} + +// vaUnlockSurface +VAStatus +xvba_UnlockSurface( + VADriverContextP ctx, + VASurfaceID surface +) +{ + return VA_STATUS_SUCCESS; +} +#endif diff --git a/src/xvba_video.h b/src/xvba_video.h new file mode 100644 index 0000000..e9daa8b --- /dev/null +++ b/src/xvba_video.h @@ -0,0 +1,329 @@ +/* + * xvba_video.h - XvBA backend for VA-API (VA context, config, surfaces) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef XVBA_VIDEO_H +#define XVBA_VIDEO_H + +#include "xvba_driver.h" + +/* Define wait delay (in microseconds) between two XVBASyncSurface() calls */ +#define XVBA_SYNC_DELAY 10 + +typedef enum { + XVBA_CODEC_MPEG1 = 1, + XVBA_CODEC_MPEG2, + XVBA_CODEC_MPEG4, + XVBA_CODEC_H264, + XVBA_CODEC_VC1 +} XVBACodec; + +typedef struct SubpictureAssociation *SubpictureAssociationP; +struct SubpictureAssociation { + VASubpictureID subpicture; + VASurfaceID surface; + VARectangle src_rect; + VARectangle dst_rect; + unsigned int flags; +}; + +typedef struct object_config object_config_t; +struct object_config { + struct object_base base; + VAProfile profile; + VAEntrypoint entrypoint; + VAConfigAttrib attrib_list[XVBA_MAX_CONFIG_ATTRIBUTES]; + unsigned int attrib_count; +}; + +typedef struct object_context object_context_t; +struct object_context { + struct object_base base; + VAConfigID va_config; + unsigned int picture_width; + unsigned int picture_height; + unsigned int flags; + unsigned int num_render_targets; + VASurfaceID *render_targets; + VASurfaceID current_render_target; + XVBACodec xvba_codec; + XVBASession *xvba_session; + XVBASession *xvba_decoder; + VABufferID *va_buffers; + unsigned int va_buffers_count; + unsigned int va_buffers_count_max; + + /* Temporary data */ + void *data_buffer; /* commit_picture() */ + unsigned int slice_count; /* commit_picture() */ +}; + +typedef struct object_surface object_surface_t; +struct object_surface { + struct object_base base; + VAContextID va_context; + VASurfaceID va_surface_status; + XVBASurface *xvba_surface; + unsigned int xvba_surface_width; + unsigned int xvba_surface_height; + object_output_p *output_surfaces; + unsigned int output_surfaces_count; + unsigned int output_surfaces_count_max; + unsigned int width; + unsigned int height; + struct object_glx_surface *gl_surface; + XVBABufferDescriptor *pic_desc_buffer; + XVBABufferDescriptor *iq_matrix_buffer; + XVBABufferDescriptor *data_buffer; + XVBABufferDescriptor **data_ctrl_buffers; + unsigned int data_ctrl_buffers_count; + unsigned int data_ctrl_buffers_count_max; + SubpictureAssociationP *assocs; + unsigned int assocs_count; + unsigned int assocs_count_max; + struct PutImageHacks *putimage_hacks; /* vaPutImage() hacks */ + unsigned int used_for_decoding : 1; +}; + +// Add subpicture association to surface +// NOTE: the subpicture owns the SubpictureAssociation object +int surface_add_association( + object_surface_p obj_surface, + SubpictureAssociationP assoc +) attribute_hidden; + +// Remove subpicture association from surface +// NOTE: the subpicture owns the SubpictureAssociation object +int surface_remove_association( + object_surface_p obj_surface, + SubpictureAssociationP assoc +) attribute_hidden; + +// Query surface status +int +query_surface_status( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_surface_p obj_surface, + VASurfaceStatus *surface_status +) attribute_hidden; + +// Synchronize surface +int +sync_surface( + xvba_driver_data_t *driver_data, + object_context_p obj_context, + object_surface_p obj_surface +) attribute_hidden; + +// vaGetConfigAttributes +VAStatus +xvba_GetConfigAttributes( + VADriverContextP ctx, + VAProfile profile, + VAEntrypoint entrypoint, + VAConfigAttrib *attrib_list, + int num_attribs +) attribute_hidden; + +// vaCreateConfig +VAStatus +xvba_CreateConfig( + VADriverContextP ctx, + VAProfile profile, + VAEntrypoint entrypoint, + VAConfigAttrib *attrib_list, + int num_attribs, + VAConfigID *config_id +) attribute_hidden; + +// vaDestroyConfig +VAStatus +xvba_DestroyConfig( + VADriverContextP ctx, + VAConfigID config_id +) attribute_hidden; + +// vaQueryConfigAttributes +VAStatus +xvba_QueryConfigAttributes( + VADriverContextP ctx, + VAConfigID config_id, + VAProfile *profile, + VAEntrypoint *entrypoint, + VAConfigAttrib *attrib_list, + int *num_attribs +) attribute_hidden; + +// vaCreateSurfaces +VAStatus +xvba_CreateSurfaces( + VADriverContextP ctx, + int width, + int height, + int format, + int num_surfaces, + VASurfaceID *surfaces +) attribute_hidden; + +// vaDestroySurfaces +VAStatus +xvba_DestroySurfaces( + VADriverContextP ctx, + VASurfaceID *surface_list, + int num_surfaces +) attribute_hidden; + +// vaCreateContext +VAStatus +xvba_CreateContext( + VADriverContextP ctx, + VAConfigID config_id, + int picture_width, + int picture_height, + int flag, + VASurfaceID *render_targets, + int num_render_targets, + VAContextID *context +) attribute_hidden; + +// vaDestroyContext +VAStatus +xvba_DestroyContext( + VADriverContextP ctx, + VAContextID context +) attribute_hidden; + +// vaQuerySurfaceStatus +VAStatus +xvba_QuerySurfaceStatus( + VADriverContextP ctx, + VASurfaceID render_target, + VASurfaceStatus *status +) attribute_hidden; + +// vaSyncSurface 2-args variant (>= 0.31) +VAStatus +xvba_SyncSurface2( + VADriverContextP ctx, + VASurfaceID render_target +) attribute_hidden; + +// vaSyncSurface 3-args variant (<= 0.30) +VAStatus +xvba_SyncSurface3( + VADriverContextP ctx, + VAContextID context, + VASurfaceID render_target +) attribute_hidden; + +// vaQueryDisplayAttributes +VAStatus +xvba_QueryDisplayAttributes( + VADriverContextP ctx, + VADisplayAttribute *attr_list, + int *num_attributes +) attribute_hidden; + +// vaGetDisplayAttributes +VAStatus +xvba_GetDisplayAttributes( + VADriverContextP ctx, + VADisplayAttribute *attr_list, + int num_attributes +) attribute_hidden; + +// vaSetDisplayAttributes +VAStatus +xvba_SetDisplayAttributes( + VADriverContextP ctx, + VADisplayAttribute *attr_list, + int num_attributes +) attribute_hidden; + +// vaDbgCopySurfaceToBuffer (not a PUBLIC interface) +VAStatus +xvba_DbgCopySurfaceToBuffer( + VADriverContextP ctx, + VASurfaceID surface, + void **buffer, + unsigned int *stride +) attribute_hidden; + +#if VA_CHECK_VERSION(0,30,0) +// vaCreateSurfaceFromCIFrame +VAStatus +xvba_CreateSurfaceFromCIFrame( + VADriverContextP ctx, + unsigned long frame_id, + VASurfaceID *surface +) attribute_hidden; + +// vaCreateSurfaceFromV4L2Buf +VAStatus +xvba_CreateSurfaceFromV4L2Buf( + VADriverContextP ctx, + int v4l2_fd, + struct v4l2_format *v4l2_fmt, + struct v4l2_buffer *v4l2_buf, + VASurfaceID *surface +) attribute_hidden; + +// vaCopySurfaceToBuffer +VAStatus +xvba_CopySurfaceToBuffer( + VADriverContextP ctx, + VASurfaceID surface, + unsigned int *fourcc, + unsigned int *luma_stride, + unsigned int *chroma_u_stride, + unsigned int *chroma_v_stride, + unsigned int *luma_offset, + unsigned int *chroma_u_offset, + unsigned int *chroma_v_offset, + void **buffer +) attribute_hidden; +#endif + +#if VA_CHECK_VERSION(0,31,1) +// vaLockSurface +VAStatus +xvba_LockSurface( + VADriverContextP ctx, + VASurfaceID surface, + unsigned int *fourcc, + unsigned int *luma_stride, + unsigned int *chroma_u_stride, + unsigned int *chroma_v_stride, + unsigned int *luma_offset, + unsigned int *chroma_u_offset, + unsigned int *chroma_v_offset, + unsigned int *buffer_name, + void **buffer +) attribute_hidden; + +// vaUnlockSurface +VAStatus +xvba_UnlockSurface( + VADriverContextP ctx, + VASurfaceID surface +) attribute_hidden; +#endif + +#endif /* XVBA_VIDEO_H */ diff --git a/src/xvba_video_glx.c b/src/xvba_video_glx.c new file mode 100644 index 0000000..550a434 --- /dev/null +++ b/src/xvba_video_glx.c @@ -0,0 +1,2349 @@ +/* + * xvba_video_glx.c - XvBA backend for VA-API (rendering to GLX) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define _GNU_SOURCE 1 /* RTLD_NEXT */ +#include "sysdeps.h" +#include "fglrxinfo.h" +#include "xvba_video.h" +#include "xvba_video_glx.h" +#include "xvba_video_x11.h" +#include "xvba_decode.h" +#include "xvba_image.h" +#include "xvba_subpic.h" +#include "xvba_buffer.h" +#include "utils.h" +#include "utils_x11.h" +#include "utils_glx.h" +#include <dlfcn.h> +#include <GL/glext.h> +#include <GL/glxext.h> +#include "shaders/Evergreen_mix_1.h" +#include "shaders/Evergreen_mix_2.h" +#include "shaders/Evergreen_mix_3.h" +#include "shaders/Evergreen_mix_4.h" +#include "shaders/YV12.h" +#include "shaders/NV12.h" +#include "shaders/ProcAmp.h" +#include "shaders/Bicubic.h" +#include "shaders/Bicubic_FLOAT.h" + +#define DEBUG 1 +#include "debug.h" + + +/* Define which Evergreen rendering workaround to use */ +#define EVERGREEN_WORKAROUND (EVERGREEN_WORKAROUND_AUTODETECT) + +enum { + EVERGREEN_WORKAROUND_SWAP8_X = 1 << 0, + EVERGREEN_WORKAROUND_SWAP8_Y = 1 << 1, + EVERGREEN_WORKAROUND_SWAP16_X = 1 << 2, + EVERGREEN_WORKAROUND_SWAP16_Y = 1 << 3, + EVERGREEN_WORKAROUND_SWAP32_X = 1 << 4, + EVERGREEN_WORKAROUND_SWAP32_Y = 1 << 5, + EVERGREEN_WORKAROUND_SWAP64_X = 1 << 6, + EVERGREEN_WORKAROUND_SWAP64_Y = 1 << 7, + EVERGREEN_WORKAROUND_COPY = 1 << 8, + EVERGREEN_WORKAROUND_AUTODETECT = 1 << 31, +}; + +/* Defined to 1 to use a multi-threaded vaPutSurface() implementation */ +#define USE_PUTSURFACE_FAST 0 + +static int get_use_putsurface_fast_env(void) +{ + int use_putsurface_fast; + if (getenv_yesno("XVBA_VIDEO_PUTSURFACE_FAST", &use_putsurface_fast) < 0) + use_putsurface_fast = USE_PUTSURFACE_FAST; + return use_putsurface_fast; +} + +static inline int use_putsurface_fast(void) +{ + static int g_use_putsurface_fast = -1; + if (g_use_putsurface_fast < 0) + g_use_putsurface_fast = get_use_putsurface_fast_env(); + return g_use_putsurface_fast; +} + +static int get_evergreen_workaround_env(void) +{ + int evergreen_workaround; + if (getenv_int("XVBA_VIDEO_EVERGREEN_WORKAROUND", + &evergreen_workaround) < 0) { + const char *evergreen_workaround_str; + evergreen_workaround_str = getenv("XVBA_VIDEO_EVERGREEN_WORKAROUND"); + if (evergreen_workaround_str && + strcmp(evergreen_workaround_str, "auto") == 0) + evergreen_workaround = EVERGREEN_WORKAROUND_AUTODETECT; + else + evergreen_workaround = EVERGREEN_WORKAROUND; + } + return evergreen_workaround; +} + +static inline int get_evergreen_workaround(void) +{ + static int g_evergreen_workaround = -1; + if (g_evergreen_workaround < 0) + g_evergreen_workaround = get_evergreen_workaround_env(); + return g_evergreen_workaround; +} + +// Prototypes +static VAStatus +do_put_surface_glx( + xvba_driver_data_t *driver_data, + object_glx_output_p obj_output, + object_surface_p obj_surface, + const VARectangle *src_rect, + const VARectangle *dst_rect, + const VARectangle *cliprects, + unsigned int num_cliprects, + unsigned int flags +); + +static VAStatus +flip_surface( + xvba_driver_data_t *driver_data, + object_glx_output_p obj_output +); + +static void +glx_output_surface_lock(object_glx_output_p obj_output); + +static void +glx_output_surface_unlock(object_glx_output_p obj_output); + +// Renderer thread messenger +#define MSG2PTR(v) ((void *)(uintptr_t)(v)) +#define PTR2MSG(v) ((uintptr_t)(void *)(v)) +enum { + MSG_TYPE_QUIT = 1, + MSG_TYPE_FLIP +}; + +typedef struct { + object_surface_p obj_surface; + VARectangle src_rect; + VARectangle dst_rect; + unsigned int flags; +} PutSurfaceMsg; + +// Renderer thread +typedef struct { + xvba_driver_data_t *driver_data; + object_glx_output_p obj_output; +} RenderThreadArgs; + +static const unsigned int VIDEO_REFRESH = 1000000 / 60; + +static void *render_thread(void *arg) +{ + RenderThreadArgs * const args = arg; + xvba_driver_data_t * const driver_data = args->driver_data; + object_glx_output_p const obj_output = args->obj_output; + unsigned int stop = 0, num_surfaces = 0; + uint64_t next; + + /* RenderThreadArgs were allocated in the main thread and the + render thread is responsible for deallocating them */ + free(args); + +#if 0 + /* Create a new X connection so that glXSwapBuffers() doesn't get + through the main thread X queue that probably wasn't set up as + MT-safe [XInitThreads()]. + + XXX: this assumes the Catalyst driver can still share GLX + contexts from another Display struct, though actually the very + same underlying X11 display (XDisplayString() shall match). */ + Display *x11_dpy; + x11_dpy = XOpenDisplay(driver_data->x11_dpy_name); + if (!x11_dpy) { + obj_output->render_thread_ok = 0; + return NULL; + } +#else + /* Use the xvba-video global X11 display */ + Display * const x11_dpy = driver_data->x11_dpy_local; +#endif + + GLContextState old_cs; + obj_output->render_context = gl_create_context( + x11_dpy, + driver_data->x11_screen, + obj_output->gl_context + ); + if (!obj_output->render_context) { + obj_output->render_thread_ok = 0; + return NULL; + } + gl_set_current_context(obj_output->render_context, &old_cs); + gl_init_context(obj_output->render_context); + + while (!stop) { + PutSurfaceMsg *msg; + + // Handle message + next = get_ticks_usec() + VIDEO_REFRESH; + msg = async_queue_timed_pop(obj_output->render_comm, next); + if (!msg) { + /* No new surface received during this video time slice, + make an explicit flip with what was received so far */ + goto do_flip; + } + + switch (PTR2MSG(msg)) { + case MSG_TYPE_QUIT: + stop = 1; + break; + case MSG_TYPE_FLIP: + do_flip: + if (num_surfaces > 0) { + glx_output_surface_lock(obj_output); + gl_resize(obj_output->window.width, obj_output->window.height); + flip_surface(driver_data, obj_output); + gl_bind_framebuffer_object(obj_output->gl_surface->fbo); + glClear(GL_COLOR_BUFFER_BIT); + gl_unbind_framebuffer_object(obj_output->gl_surface->fbo); + glClear(GL_COLOR_BUFFER_BIT); + glx_output_surface_unlock(obj_output); + num_surfaces = 0; + } + break; + default: + glx_output_surface_lock(obj_output); + do_put_surface_glx( + driver_data, + obj_output, + msg->obj_surface, + &msg->src_rect, + &msg->dst_rect, + NULL, 0, + msg->flags + ); + glx_output_surface_unlock(obj_output); + free(msg); + num_surfaces++; + break; + } + } + gl_set_current_context(&old_cs, NULL); + return NULL; +} + +// Ensure FBO and shader extensions are available +static inline int ensure_extensions(void) +{ + GLVTable * const gl_vtable = gl_get_vtable(); + + return (gl_vtable && + gl_vtable->has_framebuffer_object && + gl_vtable->has_fragment_program && + gl_vtable->has_multitexture); +} + +// Ensure FBO surface is create +static int fbo_ensure(object_glx_surface_p obj_glx_surface) +{ + if (!obj_glx_surface->fbo) { + obj_glx_surface->fbo = gl_create_framebuffer_object( + obj_glx_surface->target, + obj_glx_surface->texture, + obj_glx_surface->width, + obj_glx_surface->height + ); + if (!obj_glx_surface->fbo) + return 0; + } + ASSERT(obj_glx_surface->fbo); + return 1; +} + +// Destroy HW image +static void destroy_hw_image_glx( + xvba_driver_data_t *driver_data, + object_image_p obj_image +) +{ + unsigned int i; + + if (!obj_image || !obj_image->hw.glx) + return; + + object_image_glx_p const hwi = obj_image->hw.glx; + + if (hwi->num_textures > 0) { + glDeleteTextures(hwi->num_textures, hwi->textures); + for (i = 0; i < hwi->num_textures; i++) { + hwi->formats[i] = GL_NONE; + hwi->textures[i] = 0; + } + hwi->num_textures = 0; + } + + if (hwi->shader) { + gl_destroy_shader_object(hwi->shader); + hwi->shader = NULL; + } + + free(hwi); + obj_image->hw.glx = NULL; +} + +// Create HW image +static VAStatus +create_hw_image_glx( + xvba_driver_data_t *driver_data, + object_image_p obj_image, + XVBASession *session +) +{ + object_image_glx_p hwi = calloc(1, sizeof(*hwi)); + if (!hwi) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + obj_image->hw.glx = hwi; + + const char **shader_fp = NULL; + unsigned int shader_fp_length = 0; + switch (obj_image->image.format.fourcc) { + case VA_FOURCC('B','G','R','A'): + hwi->num_textures = 1; + hwi->formats[0] = GL_BGRA; + break; + case VA_FOURCC('R','G','B','A'): + hwi->num_textures = 1; + hwi->formats[0] = GL_RGBA; + break; + case VA_FOURCC('Y','V','1','2'): + case VA_FOURCC('I','4','2','0'): + hwi->num_textures = 3; + hwi->formats[0] = GL_LUMINANCE; + hwi->formats[1] = GL_LUMINANCE; + hwi->formats[2] = GL_LUMINANCE; + shader_fp = YV12_fp; + shader_fp_length = YV12_FP_SZ; + break; + case VA_FOURCC('N','V','1','2'): + hwi->num_textures = 2; + hwi->formats[0] = GL_LUMINANCE; + hwi->formats[1] = GL_LUMINANCE_ALPHA; + shader_fp = NV12_fp; + shader_fp_length = NV12_FP_SZ; + break; + default: + hwi->num_textures = 0; + break; + } + ASSERT(hwi->num_textures > 0); + if (hwi->num_textures == 0) { + destroy_hw_image_glx(driver_data, obj_image); + return VA_STATUS_ERROR_INVALID_IMAGE; + } + + unsigned int i; + hwi->target = GL_TEXTURE_2D; + for (i = 0; i < hwi->num_textures; i++) { + hwi->textures[i] = gl_create_texture( + hwi->target, + hwi->formats[i], + obj_image->xvba_width >> (i > 0), + obj_image->xvba_height >> (i > 0) + ); + if (!hwi->textures[i]) { + destroy_hw_image_glx(driver_data, obj_image); + return VA_STATUS_ERROR_ALLOCATION_FAILED; + } + } + + if (hwi->num_textures > 1) { + ASSERT(shader_fp); + ASSERT(shader_fp_length > 0); + + hwi->shader = gl_create_shader_object(shader_fp, shader_fp_length); + if (!hwi->shader) + return VA_STATUS_ERROR_OPERATION_FAILED; + } + + hwi->width = obj_image->xvba_width; + hwi->height = obj_image->xvba_height; + return VA_STATUS_SUCCESS; +} + +// Commit HW image +static VAStatus +commit_hw_image_glx( + xvba_driver_data_t *driver_data, + object_image_p obj_image, + object_buffer_p obj_buffer, + XVBASession *session +) +{ + object_image_glx_p const hwi = obj_image->hw.glx; + + const int is_I420 = obj_image->image.format.fourcc == VA_FOURCC('I','4','2','0'); + unsigned int offsets[3]; + switch (obj_image->image.num_planes) { + case 3: + offsets[2] = obj_image->image.offsets[is_I420 ? 1 : 2]; + case 2: + offsets[1] = obj_image->image.offsets[is_I420 ? 2 : 1]; + case 1: + offsets[0] = obj_image->image.offsets[0]; + } + + unsigned int i; + for (i = 0; i < hwi->num_textures; i++) { + glBindTexture(hwi->target, hwi->textures[i]); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glTexSubImage2D( + hwi->target, + 0, + 0, + 0, + hwi->width >> (i > 0), + hwi->height >> (i > 0), + hwi->formats[i], GL_UNSIGNED_BYTE, + (uint8_t *)obj_buffer->buffer_data + offsets[i] + ); + glBindTexture(hwi->target, 0); + } + return VA_STATUS_SUCCESS; +} + +const HWImageHooks hw_image_hooks_glx = { + create_hw_image_glx, + destroy_hw_image_glx, + commit_hw_image_glx +}; + +// Render subpictures +static VAStatus +render_subpicture( + xvba_driver_data_t *driver_data, + object_subpicture_p obj_subpicture, + object_surface_p obj_surface, + const VARectangle *surface_rect, + const SubpictureAssociationP assoc +) +{ + VAStatus status = commit_subpicture( + driver_data, + obj_subpicture, + NULL, + HWIMAGE_TYPE_GLX + ); + if (status != VA_STATUS_SUCCESS) + return status; + + object_image_p const obj_image = XVBA_IMAGE(obj_subpicture->image_id); + if (!obj_image) + return VA_STATUS_ERROR_INVALID_IMAGE; + + object_image_glx_p const hwi = obj_image->hw.glx; + if (!hwi) + return VA_STATUS_ERROR_INVALID_IMAGE; + + float alpha = 1.0; + if (assoc->flags & VA_SUBPICTURE_GLOBAL_ALPHA) + alpha = obj_subpicture->alpha; + + /* XXX: we only support RGBA and BGRA subpictures */ + if (hwi->num_textures != 1 && + hwi->formats[0] != GL_RGBA && hwi->formats[0] != GL_BGRA) + return VA_STATUS_ERROR_INVALID_IMAGE; + + glBindTexture(hwi->target, hwi->textures[0]); + glColor4f(1.0f, 1.0f, 1.0f, alpha); + glBegin(GL_QUADS); + { + VARectangle const * src_rect = &assoc->src_rect; + VARectangle const * dst_rect = &assoc->dst_rect; + int x1, x2, y1, y2; + float tx1, tx2, ty1, ty2; + + /* Clip source area by visible area */ + const unsigned int subpic_width = hwi->width; + const unsigned int subpic_height = hwi->height; + tx1 = src_rect->x / (float)subpic_width; + ty1 = src_rect->y / (float)subpic_height; + tx2 = (src_rect->x + src_rect->width) / (float)subpic_width; + ty2 = (src_rect->y + src_rect->height) / (float)subpic_height; + + const float spx = src_rect->width / ((float)subpic_width * (float)obj_surface->width); + const float spy = src_rect->height / ((float)subpic_height * (float)obj_surface->height); + const float srx1 = tx1 + surface_rect->x * spx; + const float srx2 = srx1 + surface_rect->width * spx; + const float sry1 = ty1 + surface_rect->y * spy; + const float sry2 = sry1 + surface_rect->height * spy; + + if (tx1 < srx1) + tx1 = srx1; + if (ty1 < sry1) + ty1 = sry1; + if (tx2 > srx2) + tx2 = srx2; + if (ty2 > sry2) + ty2 = sry2; + + /* Clip dest area by visible area */ + x1 = dst_rect->x; + y1 = dst_rect->y; + x2 = dst_rect->x + dst_rect->width; + y2 = dst_rect->y + dst_rect->height; + + if (x1 < surface_rect->x) + x1 = surface_rect->x; + if (y1 < surface_rect->y) + y1 = surface_rect->y; + if (x2 > surface_rect->x + surface_rect->width) + x2 = surface_rect->x + surface_rect->width; + if (y2 > surface_rect->y + surface_rect->height) + y2 = surface_rect->y + surface_rect->height; + + /* Translate and scale to fit surface size */ + const float sx = obj_surface->width / (float)surface_rect->width; + const float sy = obj_surface->height / (float)surface_rect->height; + x1 = (float)(x1 - surface_rect->x) * sx; + x2 = (float)(x2 - surface_rect->x) * sx; + y1 = (float)(y1 - surface_rect->y) * sy; + y2 = (float)(y2 - surface_rect->y) * sy; + + switch (hwi->target) { + case GL_TEXTURE_RECTANGLE_ARB: + tx1 *= subpic_width; + tx2 *= subpic_width; + ty1 *= subpic_height; + ty2 *= subpic_height; + break; + } + + glTexCoord2f(tx1, ty1); glVertex2i(x1, y1); + glTexCoord2f(tx1, ty2); glVertex2i(x1, y2); + glTexCoord2f(tx2, ty2); glVertex2i(x2, y2); + glTexCoord2f(tx2, ty1); glVertex2i(x2, y1); + } + glEnd(); + glBindTexture(hwi->target, 0); + return VA_STATUS_SUCCESS; +} + +static VAStatus +render_subpictures( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface, + const VARectangle *surface_rect +) +{ + unsigned int i; + for (i = 0; i < obj_surface->assocs_count; i++) { + SubpictureAssociationP const assoc = obj_surface->assocs[i]; + ASSERT(assoc); + if (!assoc) + continue; + + object_subpicture_p obj_subpicture = XVBA_SUBPICTURE(assoc->subpicture); + ASSERT(obj_subpicture); + if (!obj_subpicture) + continue; + + VAStatus status = render_subpicture( + driver_data, + obj_subpicture, + obj_surface, + surface_rect, + assoc + ); + if (status != VA_STATUS_SUCCESS) + return status; + } + return VA_STATUS_SUCCESS; +} + +// Destroy VA/GLX surface +static void +destroy_glx_surface( + xvba_driver_data_t *driver_data, + object_glx_surface_p obj_glx_surface +) +{ + if (!obj_glx_surface) + return; + + if (obj_glx_surface->fbo) { + gl_destroy_framebuffer_object(obj_glx_surface->fbo); + obj_glx_surface->fbo = NULL; + } + + if (obj_glx_surface->tx_xvba_surface) { + xvba_destroy_surface(obj_glx_surface->tx_xvba_surface); + obj_glx_surface->tx_xvba_surface = NULL; + } + + if (obj_glx_surface->tx_texture) { + glDeleteTextures(1, &obj_glx_surface->tx_texture); + obj_glx_surface->tx_texture = 0; + } + + if (obj_glx_surface->xvba_surface) { + xvba_destroy_surface(obj_glx_surface->xvba_surface); + obj_glx_surface->xvba_surface = NULL; + } + + if (obj_glx_surface->procamp_shader) { + gl_destroy_shader_object(obj_glx_surface->procamp_shader); + obj_glx_surface->procamp_shader = NULL; + } + + if (obj_glx_surface->evergreen_fbo) { + gl_destroy_framebuffer_object(obj_glx_surface->evergreen_fbo); + obj_glx_surface->evergreen_fbo = NULL; + } + + if (obj_glx_surface->evergreen_texture) { + glDeleteTextures(1, &obj_glx_surface->evergreen_texture); + obj_glx_surface->evergreen_texture = 0; + } + + if (obj_glx_surface->evergreen_shader) { + gl_destroy_shader_object(obj_glx_surface->evergreen_shader); + obj_glx_surface->evergreen_shader = NULL; + } + + if (obj_glx_surface->hqscaler) { + gl_destroy_shader_object(obj_glx_surface->hqscaler); + obj_glx_surface->hqscaler = NULL; + } + + if (obj_glx_surface->hqscaler_texture) { + glDeleteTextures(1, &obj_glx_surface->hqscaler_texture); + obj_glx_surface->hqscaler_texture = 0; + } + free(obj_glx_surface); +} + +// Create VA/GLX surface +static object_glx_surface_p +create_glx_surface( + xvba_driver_data_t *driver_data, + unsigned int width, + unsigned int height +) +{ + object_glx_surface_p obj_glx_surface = calloc(1, sizeof(*obj_glx_surface)); + if (!obj_glx_surface) + return NULL; + + obj_glx_surface->refcount = 1; + obj_glx_surface->target = GL_TEXTURE_2D; + obj_glx_surface->format = GL_BGRA; + obj_glx_surface->texture = gl_create_texture( + obj_glx_surface->target, + obj_glx_surface->format, + width, + height + ); + obj_glx_surface->width = width; + obj_glx_surface->height = height; + obj_glx_surface->evergreen_workaround = -1; + + if (!obj_glx_surface->texture) { + destroy_glx_surface(driver_data, obj_glx_surface); + obj_glx_surface = NULL; + } + return obj_glx_surface; +} + +// Check internal texture format is supported +static int +is_supported_internal_format(GLenum format) +{ + /* XXX: we don't support other textures than RGBA */ + switch (format) { + case 4: + case GL_RGBA: + case GL_RGBA8: + return 1; + } + return 0; +} + +static object_glx_surface_p +create_glx_surface_from_texture( + xvba_driver_data_t *driver_data, + GLenum target, + GLuint texture +) +{ + object_glx_surface_p obj_glx_surface = NULL; + unsigned int iformat, border_width, width, height; + int is_error = 1; + + obj_glx_surface = calloc(1, sizeof(*obj_glx_surface)); + if (!obj_glx_surface) + goto end; + + obj_glx_surface->refcount = 1; + obj_glx_surface->target = target; + obj_glx_surface->format = GL_NONE; + obj_glx_surface->texture = texture; + obj_glx_surface->evergreen_workaround = -1; + + /* XXX: we don't support other textures than RGBA */ + glBindTexture(target, texture); + if (!gl_get_texture_param(target, GL_TEXTURE_INTERNAL_FORMAT, &iformat)) + goto end; + if (!is_supported_internal_format(iformat)) + goto end; + + /* Check texture format */ + /* XXX: huh, there does not seem to exist any way to achieve this... */ + + /* Check texture dimensions */ + if (!gl_get_texture_param(target, GL_TEXTURE_BORDER, &border_width)) + goto end; + if (!gl_get_texture_param(target, GL_TEXTURE_WIDTH, &width)) + goto end; + if (!gl_get_texture_param(target, GL_TEXTURE_HEIGHT, &height)) + goto end; + + width -= 2 * border_width; + height -= 2 * border_width; + if (width == 0 || height == 0) + goto end; + + obj_glx_surface->width = width; + obj_glx_surface->height = height; + + is_error = 0; +end: + glBindTexture(target, 0); + if (is_error && obj_glx_surface) { + destroy_glx_surface(driver_data, obj_glx_surface); + obj_glx_surface = NULL; + } + return obj_glx_surface; +} + +// Unreferences GLX surface +void +glx_surface_unref( + xvba_driver_data_t *driver_data, + object_glx_surface_p obj_glx_surface +) +{ + if (obj_glx_surface && --obj_glx_surface->refcount == 0) + destroy_glx_surface(driver_data, obj_glx_surface); +} + +// References GLX surface +object_glx_surface_p +glx_surface_ref( + xvba_driver_data_t *driver_data, + object_glx_surface_p obj_glx_surface +) +{ + if (!obj_glx_surface) + return NULL; + ++obj_glx_surface->refcount; + return obj_glx_surface; +} + +static object_glx_surface_p +glx_surface_lookup( + xvba_driver_data_t *driver_data, + unsigned int width, + unsigned int height +) +{ + object_base_p obj; + object_heap_iterator iter; + obj = object_heap_first(&driver_data->surface_heap, &iter); + while (obj) { + object_surface_p const obj_surface = (object_surface_p)obj; + if (obj_surface->gl_surface && + obj_surface->gl_surface->width == width && + obj_surface->gl_surface->height == height) + return obj_surface->gl_surface; + obj = object_heap_next(&driver_data->surface_heap, &iter); + } + return NULL; +} + +static object_glx_surface_p +glx_surface_ensure( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface, + object_glx_output_p obj_output +) +{ + object_glx_surface_p gl_surface = obj_surface->gl_surface; + + /* Try to find a VA/GLX surface with the same dimensions */ + if (!gl_surface) { + gl_surface = glx_surface_lookup( + driver_data, + obj_surface->xvba_surface_width, + obj_surface->xvba_surface_height + ); + if (gl_surface) + obj_surface->gl_surface = glx_surface_ref(driver_data, gl_surface); + } + + /* Allocate a new VA/GLX surface */ + if (!gl_surface) { + gl_surface = create_glx_surface( + driver_data, + obj_surface->xvba_surface_width, + obj_surface->xvba_surface_height + ); + if (gl_surface) { + gl_surface->gl_context = obj_output->gl_context; + obj_surface->gl_surface = gl_surface; + } + } + return gl_surface; +} + +// Translates vaPutSurface flags to XVBA_SURFACE_FLAG +static inline XVBA_SURFACE_FLAG get_XVBA_SURFACE_FLAG(unsigned int flags) +{ + switch (flags & (VA_TOP_FIELD|VA_BOTTOM_FIELD)) { + case VA_TOP_FIELD: return XVBA_TOP_FIELD; + case VA_BOTTOM_FIELD: return XVBA_BOTTOM_FIELD; + } + return XVBA_FRAME; +} + +// Check whether surface actually has contents to be displayed +static inline int is_empty_surface(object_surface_p obj_surface) +{ + return !obj_surface->used_for_decoding && !obj_surface->putimage_hacks; +} + +static inline void +fill_evergreen_params(float **pparams, int n) +{ + float * const params = *pparams; + params[0] = 1.0f / (n * 2); + params[2] = (float)n; + *pparams += 4; +} + +// Transfer XvBA surface to GLX surface +static VAStatus +transfer_surface_native( + xvba_driver_data_t *driver_data, + object_glx_surface_p obj_glx_surface, + object_surface_p obj_surface, + unsigned int flags +) +{ + GLVTable * const gl_vtable = gl_get_vtable(); + + object_context_p obj_context = XVBA_CONTEXT(obj_surface->va_context); + if (!obj_context || !obj_context->xvba_session) + return VA_STATUS_ERROR_INVALID_CONTEXT; + + /* Create XvBA/GLX surface */ + if (!obj_glx_surface->xvba_surface) { + obj_glx_surface->xvba_surface = xvba_create_surface_gl( + obj_context->xvba_decoder, + obj_glx_surface->gl_context->context, + obj_glx_surface->texture + ); + if (!obj_glx_surface->xvba_surface) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + } + ASSERT(obj_glx_surface->xvba_surface); + + /* Make sure the picture is decoded */ + if (obj_surface->va_surface_status == VASurfaceRendering) { + if (sync_surface(driver_data, obj_context, obj_surface) < 0) + return VA_STATUS_ERROR_UNKNOWN; + } + + XVBASurface *dst_xvba_surface, *src_xvba_surface; + dst_xvba_surface = obj_glx_surface->xvba_surface; + src_xvba_surface = (obj_surface->putimage_hacks ? + obj_surface->putimage_hacks->xvba_surface : + obj_surface->xvba_surface); + + /* Check for Evergreen workaround */ + int needs_evergreen_texture = 0; + int evergreen_workaround = obj_glx_surface->evergreen_workaround; + if (evergreen_workaround < 0) { + evergreen_workaround = get_evergreen_workaround(); + + if (evergreen_workaround == EVERGREEN_WORKAROUND_AUTODETECT) { + evergreen_workaround = 0; + if (driver_data->is_evergreen_gpu) { + switch (driver_data->va_display_type) { + case VA_DISPLAY_X11: + if (fglrx_check_version(0,80,5)) + evergreen_workaround = 0; + else if (driver_data->is_fusion_igp) + evergreen_workaround = EVERGREEN_WORKAROUND_COPY; + else if (fglrx_check_version(8,78,6)) + evergreen_workaround = (EVERGREEN_WORKAROUND_SWAP8_X | + EVERGREEN_WORKAROUND_SWAP8_Y | + EVERGREEN_WORKAROUND_SWAP16_X | + EVERGREEN_WORKAROUND_SWAP16_Y); + break; + case VA_DISPLAY_GLX: + if (fglrx_check_version(8,80,5)) + evergreen_workaround = 0; + else if (fglrx_check_version(8,79,4) && + driver_data->is_fusion_igp) + evergreen_workaround = (EVERGREEN_WORKAROUND_SWAP8_X | + EVERGREEN_WORKAROUND_SWAP8_Y | + EVERGREEN_WORKAROUND_SWAP16_X); + else if (fglrx_check_version(8,78,6) && + driver_data->is_fusion_igp) + evergreen_workaround = (EVERGREEN_WORKAROUND_SWAP8_X | + EVERGREEN_WORKAROUND_SWAP8_Y | + EVERGREEN_WORKAROUND_SWAP16_Y); + else if (fglrx_check_version(8,78,6)) + evergreen_workaround = (EVERGREEN_WORKAROUND_SWAP8_X | + EVERGREEN_WORKAROUND_SWAP16_Y | + EVERGREEN_WORKAROUND_SWAP32_Y | + EVERGREEN_WORKAROUND_SWAP64_Y); + break; + } + } + } + + if (evergreen_workaround & EVERGREEN_WORKAROUND_COPY) + evergreen_workaround = EVERGREEN_WORKAROUND_COPY; + + D(bug("Using Evergreen workaround %d\n", evergreen_workaround)); + obj_glx_surface->evergreen_workaround = evergreen_workaround; + } + + if (evergreen_workaround) { + needs_evergreen_texture = 1; + + if (!obj_glx_surface->evergreen_texture) { + obj_glx_surface->evergreen_texture = gl_create_texture( + GL_TEXTURE_2D, + GL_BGRA, + src_xvba_surface->info.normal.width, + src_xvba_surface->info.normal.height + ); + if (!obj_glx_surface->evergreen_texture) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + + /* XXX: some algorithms work modulo the texture size */ + glBindTexture(GL_TEXTURE_2D, obj_glx_surface->evergreen_texture); + gl_set_texture_wrapping(GL_TEXTURE_2D, GL_REPEAT); + glBindTexture(GL_TEXTURE_2D, 0); + } + + if (!obj_glx_surface->evergreen_fbo) { + obj_glx_surface->evergreen_fbo = gl_create_framebuffer_object( + GL_TEXTURE_2D, + obj_glx_surface->evergreen_texture, + src_xvba_surface->info.normal.width, + src_xvba_surface->info.normal.height + ); + if (!obj_glx_surface->evergreen_fbo) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + } + + if (evergreen_workaround != EVERGREEN_WORKAROUND_COPY && + !obj_glx_surface->evergreen_shader) { + const char **shader_fp = NULL; + unsigned int shader_fp_length = 0; + float *params; + int i, n_params, n_x_params, n_y_params; + + // program.local[0] = textureSize + params = obj_glx_surface->evergreen_params[0]; + params[0] = (float)src_xvba_surface->info.normal.width; + params[1] = (float)src_xvba_surface->info.normal.height; + params[2] = 1.0f / src_xvba_surface->info.normal.width; + params[3] = 1.0f / src_xvba_surface->info.normal.height; + + // program.local[1..4] = mix_params[0..3] + for (i = 1; i <= XVBA_MAX_EVERGREEN_PARAMS; i++) { + params = obj_glx_surface->evergreen_params[i]; + params[0] = 1.0f; + params[1] = 1.0f; + params[2] = 0.0f; + params[3] = 0.0f; + } + + // fill in X params + params = &obj_glx_surface->evergreen_params[1][0]; + if (evergreen_workaround & EVERGREEN_WORKAROUND_SWAP8_X) + fill_evergreen_params(¶ms, 8); + if (evergreen_workaround & EVERGREEN_WORKAROUND_SWAP16_X) + fill_evergreen_params(¶ms, 16); + if (evergreen_workaround & EVERGREEN_WORKAROUND_SWAP32_X) + fill_evergreen_params(¶ms, 32); + if (evergreen_workaround & EVERGREEN_WORKAROUND_SWAP64_X) + fill_evergreen_params(¶ms, 64); + n_x_params = (params - &obj_glx_surface->evergreen_params[1][0])/4; + + // fill in Y params + params = &obj_glx_surface->evergreen_params[1][1]; + if (evergreen_workaround & EVERGREEN_WORKAROUND_SWAP8_Y) + fill_evergreen_params(¶ms, 8); + if (evergreen_workaround & EVERGREEN_WORKAROUND_SWAP16_Y) + fill_evergreen_params(¶ms, 16); + if (evergreen_workaround & EVERGREEN_WORKAROUND_SWAP32_Y) + fill_evergreen_params(¶ms, 32); + if (evergreen_workaround & EVERGREEN_WORKAROUND_SWAP64_Y) + fill_evergreen_params(¶ms, 64); + n_y_params = (params - &obj_glx_surface->evergreen_params[1][1])/4; + + // load shader + n_params = MAX(n_x_params, n_y_params); + switch (n_params) { + case 1: + shader_fp = Evergreen_mix_1_fp; + shader_fp_length = EVERGREEN_MIX_1_FP_SZ; + break; + case 2: + shader_fp = Evergreen_mix_2_fp; + shader_fp_length = EVERGREEN_MIX_2_FP_SZ; + break; + case 3: + shader_fp = Evergreen_mix_3_fp; + shader_fp_length = EVERGREEN_MIX_3_FP_SZ; + break; + case 4: + shader_fp = Evergreen_mix_4_fp; + shader_fp_length = EVERGREEN_MIX_4_FP_SZ; + break; + default: + /* XXX: unsupported combination, disable Evergreen workaround */ + D(bug("ERROR: unsupported Evergreen workaround 0x%x, disabling\n", + evergreen_workaround)); + obj_glx_surface->evergreen_workaround = 0; + break; + } + obj_glx_surface->evergreen_params_count = n_params; + + if (shader_fp && shader_fp_length) { + obj_glx_surface->evergreen_shader = gl_create_shader_object( + shader_fp, + shader_fp_length + ); + if (!obj_glx_surface->evergreen_shader) + return VA_STATUS_ERROR_OPERATION_FAILED; + } + } + } + + /* Make sure GLX texture has the same dimensions as the surface */ + int needs_tx_texture = 0; + if (needs_evergreen_texture || + obj_glx_surface->format == GL_RGBA || // XXX: XvBA bug, no RGBA support + (/*!fglrx_check_version(8,76,7) &&*/ // XXX: #70011.64 supposedly fixed + (obj_glx_surface->width != src_xvba_surface->info.normal.width || + obj_glx_surface->height != src_xvba_surface->info.normal.height))) { + again: + needs_tx_texture = 1; + + if (!obj_glx_surface->tx_texture) { + obj_glx_surface->tx_texture = gl_create_texture( + GL_TEXTURE_2D, + GL_BGRA, + src_xvba_surface->info.normal.width, + src_xvba_surface->info.normal.height + ); + if (!obj_glx_surface->tx_texture) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + } + + if (!obj_glx_surface->tx_xvba_surface) { + obj_glx_surface->tx_xvba_surface = xvba_create_surface_gl( + obj_context->xvba_decoder, + obj_glx_surface->gl_context->context, + obj_glx_surface->tx_texture + ); + if (!obj_glx_surface->tx_xvba_surface) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + } + + if (!fbo_ensure(obj_glx_surface)) + return VA_STATUS_ERROR_OPERATION_FAILED; + + dst_xvba_surface = obj_glx_surface->tx_xvba_surface; + } + + /* Transfer XvBA surface */ + if (xvba_transfer_surface(obj_context->xvba_session, + dst_xvba_surface, + src_xvba_surface, + get_XVBA_SURFACE_FLAG(flags)) < 0) { + /* XXX: the user texture is probably RGBA so, create the tx texture */ + if (!needs_tx_texture && obj_glx_surface->format == GL_NONE) { + obj_glx_surface->format = GL_RGBA; + goto again; + } + return VA_STATUS_ERROR_OPERATION_FAILED; + } + if (!needs_tx_texture && obj_glx_surface->format == GL_NONE) + obj_glx_surface->format = GL_BGRA; + + GLuint alternate_texture; + int needs_alternate_texture = 0; + if (needs_evergreen_texture) { + needs_alternate_texture = 1; + alternate_texture = obj_glx_surface->evergreen_texture; + + gl_bind_framebuffer_object(obj_glx_surface->evergreen_fbo); + glBindTexture(GL_TEXTURE_2D, obj_glx_surface->tx_texture); + if (obj_glx_surface->evergreen_shader) { + gl_bind_shader_object(obj_glx_surface->evergreen_shader); + + int i; + for (i = 0; i <= obj_glx_surface->evergreen_params_count; i++) + gl_vtable->gl_program_local_parameter_4fv( + GL_FRAGMENT_PROGRAM, + i, + obj_glx_surface->evergreen_params[i] + ); + } + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); + { + const unsigned int w = src_xvba_surface->info.normal.width; + const unsigned int h = src_xvba_surface->info.normal.height; + glTexCoord2f(0.0f, 0.0f); glVertex2i(0, 0); + glTexCoord2f(1.0f, 0.0f); glVertex2i(w, 0); + glTexCoord2f(1.0f, 1.0f); glVertex2i(w, h); + glTexCoord2f(0.0f, 1.0f); glVertex2i(0, h); + } + glEnd(); + if (obj_glx_surface->evergreen_shader) + gl_unbind_shader_object(obj_glx_surface->evergreen_shader); + gl_unbind_framebuffer_object(obj_glx_surface->evergreen_fbo); + } + else if (needs_tx_texture) { + needs_alternate_texture = 1; + alternate_texture = obj_glx_surface->tx_texture; + } + + if (needs_alternate_texture) { + gl_bind_framebuffer_object(obj_glx_surface->fbo); + glBindTexture(GL_TEXTURE_2D, alternate_texture); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); + { + /* Both TX and Evergreen textures are GL_TEXTURE_2D */ + const unsigned int w = obj_glx_surface->width; + const unsigned int h = obj_glx_surface->height; + const float tw = obj_surface->width / (float)src_xvba_surface->info.normal.width; + const float th = obj_surface->height / (float)src_xvba_surface->info.normal.height; + glTexCoord2f(0.0f, 0.0f); glVertex2i(0, 0); + glTexCoord2f(tw, 0.0f); glVertex2i(w, 0); + glTexCoord2f(tw, th ); glVertex2i(w, h); + glTexCoord2f(0.0f, th ); glVertex2i(0, h); + } + glEnd(); + gl_unbind_framebuffer_object(obj_glx_surface->fbo); + } + return VA_STATUS_SUCCESS; +} + +// Transfer VA surface to GLX surface +static VAStatus +transfer_surface( + xvba_driver_data_t *driver_data, + object_glx_surface_p obj_glx_surface, + object_surface_p obj_surface, + unsigned int flags +) +{ + PutImageHacks * const h = obj_surface->putimage_hacks; + + if (!h || h->type == PUTIMAGE_HACKS_SURFACE) + return transfer_surface_native(driver_data, + obj_glx_surface, + obj_surface, + flags); + + object_image_p obj_image = h->obj_image; + if (!obj_image) + return VA_STATUS_ERROR_INVALID_IMAGE; + + VAStatus status; + status = commit_hw_image(driver_data, obj_image, NULL, HWIMAGE_TYPE_GLX); + if (status != VA_STATUS_SUCCESS) + return status; + + object_image_glx_p const hwi = obj_image->hw.glx; + if (!hwi) + return VA_STATUS_ERROR_INVALID_IMAGE; + + if (!fbo_ensure(obj_glx_surface)) + return VA_STATUS_ERROR_OPERATION_FAILED; + + object_buffer_p obj_buffer = XVBA_BUFFER(obj_image->image.buf); + if (!obj_buffer) + return VA_STATUS_ERROR_INVALID_BUFFER; + + GLVTable * const gl_vtable = gl_get_vtable(); + unsigned int i; + for (i = 0; i < hwi->num_textures; i++) { + if (hwi->shader) + gl_vtable->gl_active_texture(GL_TEXTURE0 + i); + glBindTexture(hwi->target, hwi->textures[i]); + } + + gl_bind_framebuffer_object(obj_glx_surface->fbo); + if (hwi->shader) + gl_bind_shader_object(hwi->shader); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); + { + float tw, th; + switch (hwi->target) { + case GL_TEXTURE_2D: + tw = obj_image->image.width == hwi->width ? + 1.0f : + (obj_image->image.width - 0.5f) / (float)hwi->width; + th = obj_image->image.height == hwi->height ? + 1.0f : + (obj_image->image.height - 0.5f) / (float)hwi->height; + break; + case GL_TEXTURE_RECTANGLE_ARB: + tw = (float)obj_image->image.width; + th = (float)obj_image->image.height; + break; + default: + tw = 0.0f; + th = 0.0f; + ASSERT(hwi->target == GL_TEXTURE_2D || + hwi->target == GL_TEXTURE_RECTANGLE_ARB); + break; + } + + const unsigned int w = obj_glx_surface->width; + const unsigned int h = obj_glx_surface->height; + glTexCoord2f(0.0f, 0.0f); glVertex2i(0, 0); + glTexCoord2f(0.0f, th ); glVertex2i(0, h); + glTexCoord2f(tw , th ); glVertex2i(w, h); + glTexCoord2f(tw , 0.0f); glVertex2i(w, 0); + } + glEnd(); + if (hwi->shader) + gl_unbind_shader_object(hwi->shader); + gl_unbind_framebuffer_object(obj_glx_surface->fbo); + + i = hwi->num_textures; + do { + --i; + if (hwi->shader) + gl_vtable->gl_active_texture(GL_TEXTURE0 + i); + glBindTexture(hwi->target, 0); + } while (i > 0); + return VA_STATUS_SUCCESS; +} + +static VAStatus +ensure_procamp_shader( + xvba_driver_data_t *driver_data, + object_glx_surface_p obj_glx_surface +) +{ + uint64_t new_mtime = obj_glx_surface->procamp_mtime; + unsigned int i, n_procamp_zeros = 0; + + for (i = 0; i < driver_data->va_display_attrs_count; i++) { + VADisplayAttribute * const attr = &driver_data->va_display_attrs[i]; + + switch (attr->type) { + case VADisplayAttribBrightness: + case VADisplayAttribContrast: + case VADisplayAttribSaturation: + case VADisplayAttribHue: + if (attr->value == 0) + ++n_procamp_zeros; + if (new_mtime < driver_data->va_display_attrs_mtime[i]) + new_mtime = driver_data->va_display_attrs_mtime[i]; + break; + default: + break; + } + } + + /* Check that ProcAmp adjustments were made since the last call */ + if (new_mtime <= obj_glx_surface->procamp_mtime) + return VA_STATUS_SUCCESS; + + /* Check that we really need a shader (ProcAmp with non-default values) */ + if (n_procamp_zeros == 4) { + obj_glx_surface->use_procamp_shader = 0; + obj_glx_surface->procamp_mtime = new_mtime; + return VA_STATUS_SUCCESS; + } + + if (obj_glx_surface->procamp_shader) { + gl_destroy_shader_object(obj_glx_surface->procamp_shader); + obj_glx_surface->procamp_shader = NULL; + } + obj_glx_surface->procamp_shader = gl_create_shader_object( + ProcAmp_fp, + PROCAMP_FP_SZ + ); + if (!obj_glx_surface->procamp_shader) + return VA_STATUS_ERROR_OPERATION_FAILED; + + obj_glx_surface->use_procamp_shader = 1; + obj_glx_surface->procamp_mtime = new_mtime; + return VA_STATUS_SUCCESS; +} + +static GLuint +ensure_hqscaler_texture(void) +{ + const int N = 128; + float *data = NULL; + GLuint tex = 0; + unsigned int i; + + tex = gl_create_texture(GL_TEXTURE_1D, GL_RGBA32F_ARB, N, 0); + if (!tex) + goto error; + + data = malloc(N * 4 * sizeof(*data)); + if (!data) + goto error; + + /* Generate weights and offsets */ + for (i = 0; i < N; i++) { + const float x = (1.0f*i) / N; + const float x2 = x*x; + const float x3 = x2*x; + const float w0 = (1.0f/6.0f) * ( -x3 + 3.0f*x2 - 3.0f*x + 1.0f); + const float w1 = (1.0f/6.0f) * ( 3.0f*x3 - 6.0f*x2 + 4.0f); + const float w2 = (1.0f/6.0f) * (-3.0f*x3 + 3.0f*x2 + 3.0f*x + 1.0f); + const float w3 = (1.0f/6.0f) * ( x3); + const float g0 = w0 + w1; + const float h0 = -1.0f + w1 / g0 + 0.5f; + const float g1 = w2 + w3; + const float h1 = 1.0f + w3 / g1 + 0.5f; + + /* float4 = (h0, h1, g0, g1) */ + data[i*4 + 0] = h0; + data[i*4 + 1] = h1; + data[i*4 + 2] = g0; + data[i*4 + 3] = g1; + } + + glBindTexture(GL_TEXTURE_1D, tex); + gl_set_texture_scaling(GL_TEXTURE_1D, GL_NEAREST); + gl_set_texture_wrapping(GL_TEXTURE_1D, GL_REPEAT); + glTexSubImage1D( + GL_TEXTURE_1D, + 0, + 0, + N, + GL_RGBA, + GL_FLOAT, + data + ); + glBindTexture(GL_TEXTURE_1D, 0); + free(data); + return tex; + + /* ERRORS */ +error: + if (tex) + glDeleteTextures(1, &tex); + if (data) + free(data); + return 0; +} + +static VAStatus +ensure_scaler( + xvba_driver_data_t *driver_data, + object_glx_surface_p obj_glx_surface, + unsigned int flags +) +{ + const unsigned int va_scale = flags & VA_FILTER_SCALING_MASK; + + if (obj_glx_surface->va_scale == va_scale) + return VA_STATUS_SUCCESS; + + if (obj_glx_surface->hqscaler) { + gl_destroy_shader_object(obj_glx_surface->hqscaler); + obj_glx_surface->hqscaler = NULL; + } + + if (obj_glx_surface->hqscaler_texture) { + glDeleteTextures(1, &obj_glx_surface->hqscaler_texture); + obj_glx_surface->hqscaler_texture = 0; + } + + const GLenum target = obj_glx_surface->target; + switch (va_scale) { + case VA_FILTER_SCALING_DEFAULT: + glBindTexture(target, obj_glx_surface->texture); + gl_set_texture_scaling(target, GL_LINEAR); + glBindTexture(target, 0); + break; + case VA_FILTER_SCALING_FAST: + glBindTexture(target, obj_glx_surface->texture); + gl_set_texture_scaling(target, GL_NEAREST); + glBindTexture(target, 0); + break; + case VA_FILTER_SCALING_HQ: { + const char **shader_fp = NULL; + unsigned int shader_fp_length = 0; + + glBindTexture(target, obj_glx_surface->texture); + gl_set_texture_scaling(target, GL_LINEAR); + glBindTexture(target, 0); + + obj_glx_surface->hqscaler_texture = ensure_hqscaler_texture(); + if (obj_glx_surface->hqscaler_texture) { + shader_fp = Bicubic_FLOAT_fp; + shader_fp_length = BICUBIC_FLOAT_FP_SZ; + } + else { + shader_fp = Bicubic_fp; + shader_fp_length = BICUBIC_FP_SZ; + } + + obj_glx_surface->hqscaler = gl_create_shader_object( + shader_fp, + shader_fp_length + ); + if (!obj_glx_surface->hqscaler) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + break; + } + } + + obj_glx_surface->va_scale = va_scale; + return VA_STATUS_SUCCESS; +} + +// vaCreateSurfaceGLX +VAStatus +xvba_CreateSurfaceGLX( + VADriverContextP ctx, + unsigned int target, + unsigned int texture, + void **gl_surface +) +{ + XVBA_DRIVER_DATA_INIT; + + xvba_set_display_type(driver_data, VA_DISPLAY_GLX); + + if (!gl_surface) + return VA_STATUS_ERROR_INVALID_PARAMETER; + + /* Make sure it is a valid GL texture object */ + if (!glIsTexture(texture)) + return VA_STATUS_ERROR_INVALID_PARAMETER; + + /* Make sure we have the necessary GLX extensions */ + if (!ensure_extensions()) + return VA_STATUS_ERROR_OPERATION_FAILED; + + /* Setup new GLX context */ + GLContextState old_cs, *new_cs; + gl_get_current_context(&old_cs); + old_cs.display = driver_data->x11_dpy; + new_cs = gl_create_context(driver_data->x11_dpy, driver_data->x11_screen, &old_cs); + if (!new_cs) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + if (!gl_set_current_context(new_cs, NULL)) + return VA_STATUS_ERROR_OPERATION_FAILED; + + gl_init_context(new_cs); + + object_glx_surface_p obj_glx_surface; + obj_glx_surface = create_glx_surface_from_texture( + driver_data, + target, + texture + ); + if (!obj_glx_surface) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + + *gl_surface = obj_glx_surface; + obj_glx_surface->gl_context = new_cs; + + gl_set_current_context(&old_cs, NULL); + return VA_STATUS_SUCCESS; +} + +// vaDestroySurfaceGLX +VAStatus +xvba_DestroySurfaceGLX( + VADriverContextP ctx, + void *gl_surface +) +{ + XVBA_DRIVER_DATA_INIT; + + xvba_set_display_type(driver_data, VA_DISPLAY_GLX); + + /* Make sure we have the necessary GLX extensions */ + if (!ensure_extensions()) + return VA_STATUS_ERROR_OPERATION_FAILED; + + object_glx_surface_p obj_glx_surface = gl_surface; + if (!obj_glx_surface) + return VA_STATUS_ERROR_INVALID_SURFACE; + + GLContextState old_cs, *new_cs = obj_glx_surface->gl_context; + if (!gl_set_current_context(new_cs, &old_cs)) + return VA_STATUS_ERROR_OPERATION_FAILED; + + destroy_glx_surface(driver_data, obj_glx_surface); + + gl_destroy_context(new_cs); + gl_set_current_context(&old_cs, NULL); + return VA_STATUS_SUCCESS; +} + +// vaCopySurfaceGLX +static VAStatus +do_copy_surface_glx( + xvba_driver_data_t *driver_data, + object_glx_surface_p obj_glx_surface, + object_surface_p obj_surface, + unsigned int flags +) +{ + VAStatus status; + + /* Transfer surface to texture */ + if (!is_empty_surface(obj_surface)) { + status = transfer_surface(driver_data, obj_glx_surface, obj_surface, flags); + if (status != VA_STATUS_SUCCESS) + return status; + } + + /* Make sure color matrix for ProcAmp adjustments is setup */ + status = ensure_procamp_shader(driver_data, obj_glx_surface); + if (status != VA_STATUS_SUCCESS) + return status; + + /* Check if FBO is needed. e.g. for subpictures */ + const int needs_fbo = (obj_surface->assocs_count > 0 || + obj_glx_surface->use_procamp_shader); + if (!needs_fbo) + return VA_STATUS_SUCCESS; + + /* Create framebuffer surface */ + if (!fbo_ensure(obj_glx_surface)) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + gl_bind_framebuffer_object(obj_glx_surface->fbo); + + /* Re-render the video frame with ProcAmp adjustments */ + if (obj_glx_surface->use_procamp_shader) { + GLVTable * const gl_vtable = gl_get_vtable(); + int i; + + gl_bind_shader_object(obj_glx_surface->procamp_shader); + + /* Commit the new ProcAmp color matrix */ + for (i = 0; i < 4; i++) + gl_vtable->gl_program_local_parameter_4fv( + GL_FRAGMENT_PROGRAM, i, + driver_data->cm_composite[i] + ); + + /* Render the picture frame */ + glBindTexture(obj_glx_surface->target, obj_glx_surface->texture); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); + { + float tw, th; + switch (obj_glx_surface->target) { + case GL_TEXTURE_2D: + tw = 1.0f; + th = 1.0f; + break; + case GL_TEXTURE_RECTANGLE_ARB: + tw = (float)obj_glx_surface->width; + th = (float)obj_glx_surface->height; + break; + default: + tw = 0.0f; + th = 0.0f; + ASSERT(obj_glx_surface->target == GL_TEXTURE_2D || + obj_glx_surface->target == GL_TEXTURE_RECTANGLE_ARB); + break; + } + + const unsigned int w = obj_glx_surface->width; + const unsigned int h = obj_glx_surface->height; + glTexCoord2f(0.0f, 0.0f); glVertex2i(0, 0); + glTexCoord2f(tw , 0.0f); glVertex2i(w, 0); + glTexCoord2f(tw , th ); glVertex2i(w, h); + glTexCoord2f(0.0f, th ); glVertex2i(0, h); + } + glEnd(); + glBindTexture(obj_glx_surface->target, 0); + gl_unbind_shader_object(obj_glx_surface->procamp_shader); + } + + /* Render subpictures to FBO */ + VARectangle surface_rect; + surface_rect.x = 0; + surface_rect.y = 0; + surface_rect.width = obj_surface->width; + surface_rect.height = obj_surface->height; + status = render_subpictures(driver_data, obj_surface, &surface_rect); + + gl_unbind_framebuffer_object(obj_glx_surface->fbo); + return status; +} + +VAStatus +xvba_CopySurfaceGLX( + VADriverContextP ctx, + void *gl_surface, + VASurfaceID surface, + unsigned int flags +) +{ + XVBA_DRIVER_DATA_INIT; + + xvba_set_display_type(driver_data, VA_DISPLAY_GLX); + + object_glx_surface_p obj_glx_surface = gl_surface; + if (!obj_glx_surface) + return VA_STATUS_ERROR_INVALID_SURFACE; + + object_surface_p obj_surface = XVBA_SURFACE(surface); + if (!obj_surface) + return VA_STATUS_ERROR_INVALID_SURFACE; + + /* Make sure we have the necessary GLX extensions */ + if (!ensure_extensions()) + return VA_STATUS_ERROR_OPERATION_FAILED; + + GLContextState old_cs, *new_cs = obj_glx_surface->gl_context; + if (!gl_set_current_context(new_cs, &old_cs)) + return VA_STATUS_ERROR_OPERATION_FAILED; + + VAStatus status; + status = do_copy_surface_glx( + driver_data, + obj_glx_surface, + obj_surface, + flags + ); + + gl_set_current_context(&old_cs, NULL); + return status; +} + +// Locks output surface +static void +glx_output_surface_lock(object_glx_output_p obj_output) +{ + if (!obj_output || !obj_output->render_thread_ok) + return; + pthread_mutex_lock(&obj_output->lock); +} + +// Unlocks output surface +static void +glx_output_surface_unlock(object_glx_output_p obj_output) +{ + if (!obj_output || !obj_output->render_thread_ok) + return; + pthread_mutex_unlock(&obj_output->lock); +} + +// Destroys output surface +void +glx_output_surface_destroy( + xvba_driver_data_t *driver_data, + object_glx_output_p obj_output +) +{ + if (!obj_output) + return; + + if (1) { + const uint64_t end = get_ticks_usec(); + const uint64_t start = obj_output->render_start; + const uint64_t ticks = obj_output->render_ticks; + + D(bug("%llu refreshes in %llu usec (%.1f fps)\n", + ticks, end - start, + ticks * 1000000.0 / (end - start))); + } + + if (obj_output->render_thread_ok) { + async_queue_push(obj_output->render_comm, MSG2PTR(MSG_TYPE_QUIT)); + pthread_join(obj_output->render_thread, NULL); + obj_output->render_thread = 0; + obj_output->render_thread_ok = 0; + } + + if (obj_output->render_comm) { + async_queue_free(obj_output->render_comm); + obj_output->render_comm = NULL; + } + + if (obj_output->render_context) { + gl_destroy_context(obj_output->render_context); + obj_output->render_context = NULL; + } + + if (obj_output->parent) + --obj_output->parent->children_count; + + if (obj_output->gl_surface) { + if (!obj_output->parent) + destroy_glx_surface(driver_data, obj_output->gl_surface); + obj_output->gl_surface = NULL; + } + + if (obj_output->gl_context) { + glFinish(); + GLContextState dummy_cs; + dummy_cs.display = driver_data->x11_dpy; + dummy_cs.window = None; + dummy_cs.context = NULL; + gl_set_current_context(&dummy_cs, NULL); + if (!obj_output->parent) + gl_destroy_context(obj_output->gl_context); + obj_output->gl_context = NULL; + } + + if (obj_output->gl_window.xid != None) { +#if 0 + /* User's XDestroyWindow() on the parent window will destroy + our child windows too */ + if (!obj_output->parent) { + XUnmapWindow(driver_data->x11_dpy, obj_output->gl_window.xid); + x11_wait_event(driver_data->x11_dpy, obj_output->gl_window.xid, UnmapNotify); + XDestroyWindow(driver_data->x11_dpy, obj_output->gl_window.xid); + } +#endif + obj_output->gl_window.xid = None; + } + + if (obj_output->gl_window.cmap != None) { + if (!obj_output->parent) + XFreeColormap(driver_data->x11_dpy, obj_output->gl_window.cmap); + obj_output->gl_window.cmap = None; + } + + if (obj_output->gl_window.vi) { + if (!obj_output->parent) + XFree(obj_output->gl_window.vi); + obj_output->gl_window.vi = NULL; + } + free(obj_output); +} + +// Creates output surface +static object_glx_output_p +glx_output_surface_create( + xvba_driver_data_t *driver_data, + Window window, + unsigned int width, + unsigned int height +) +{ + object_glx_output_p obj_output = calloc(1, sizeof(*obj_output)); + if (!obj_output) + return NULL; + + obj_output->va_surface_status = VASurfaceReady; + obj_output->window.xid = window; + obj_output->window.width = width; + obj_output->window.height = height; + + pthread_mutex_init(&obj_output->lock, NULL); + + /* XXX: recurse through parents until we find an output surface */ + Window root_window, parent_window, *child_windows = NULL; + unsigned int n_child_windows = 0; + XQueryTree( + driver_data->x11_dpy, + window, + &root_window, + &parent_window, + &child_windows, &n_child_windows + ); + if (child_windows) + XFree(child_windows); + + GLContextState old_cs, *parent_cs = NULL; + gl_get_current_context(&old_cs); + old_cs.display = driver_data->x11_dpy; + + if (parent_window != None) { + object_output_p parent_obj_output; + parent_obj_output = output_surface_lookup(driver_data, parent_window); + if (parent_obj_output && parent_obj_output->glx) { + obj_output->parent = parent_obj_output->glx; + parent_cs = parent_obj_output->glx->gl_context; + } + } + + static GLint gl_visual_attr[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + GL_NONE + }; + + XWindowAttributes wattr; + XGetWindowAttributes(driver_data->x11_dpy, window, &wattr); + int depth = wattr.depth; + if (depth != 15 && depth != 16 && depth != 24 && depth != 32) + depth = 24; + + obj_output->gl_window.vi = glXChooseVisual( + driver_data->x11_dpy, + driver_data->x11_screen, + gl_visual_attr + ); + if (!obj_output->gl_window.vi) + goto error; + + obj_output->gl_window.cmap = XCreateColormap( + driver_data->x11_dpy, + RootWindow(driver_data->x11_dpy, driver_data->x11_screen), + obj_output->gl_window.vi->visual, + AllocNone + ); + if (obj_output->gl_window.cmap == None) + goto error; + + x11_get_window_colorkey(driver_data->x11_dpy, window, 0, 0, &obj_output->bgcolor); + if (driver_data->va_background_color) + obj_output->bgcolor = driver_data->va_background_color->value; + + XSetWindowAttributes xswa; + unsigned long xswa_mask = CWBorderPixel | CWBackPixel | CWColormap; + xswa.border_pixel = BlackPixel(driver_data->x11_dpy, driver_data->x11_screen); + xswa.background_pixel = obj_output->bgcolor; + xswa.colormap = obj_output->gl_window.cmap; + + obj_output->gl_window.xid = XCreateWindow( + driver_data->x11_dpy, + window, + 0, + 0, + width, + height, + 0, + depth, + InputOutput, + obj_output->gl_window.vi->visual, + xswa_mask, &xswa + ); + if (obj_output->gl_window.xid == None) + goto error; + + XSelectInput(driver_data->x11_dpy, obj_output->gl_window.xid, StructureNotifyMask); + XMapWindow(driver_data->x11_dpy, obj_output->gl_window.xid); + XLowerWindow(driver_data->x11_dpy, obj_output->gl_window.xid); + x11_wait_event(driver_data->x11_dpy, obj_output->gl_window.xid, MapNotify); + + /* XXX: assume the program will only be using vaPutSurface() and + doesn't have any other GLX context managed itself */ + ASSERT(driver_data->va_display_type == VA_DISPLAY_X11); + gl_set_current_context_cache(1); + + /* XXX: check that we don't already have a valid GLX context */ + obj_output->gl_context = gl_create_context( + driver_data->x11_dpy, + driver_data->x11_screen, + parent_cs + ); + if (!obj_output->gl_context) + goto error; + obj_output->gl_context->window = obj_output->gl_window.xid; + if (!gl_set_current_context(obj_output->gl_context, NULL)) + goto error; + if (!ensure_extensions()) { + gl_set_current_context(&old_cs, NULL); + goto error; + } + + gl_init_context(obj_output->gl_context); + gl_set_bgcolor(obj_output->bgcolor); + glClear(GL_COLOR_BUFFER_BIT); + gl_set_current_context(&old_cs, NULL); + + if (use_putsurface_fast()) { + RenderThreadArgs *args = NULL; + + obj_output->render_comm = async_queue_new(); + if (!obj_output->render_comm) + goto render_thread_init_end; + + args = malloc(sizeof(*args)); + if (!args) + goto render_thread_init_end; + args->driver_data = driver_data; + args->obj_output = obj_output; + obj_output->render_thread_ok = !pthread_create( + &obj_output->render_thread, + NULL, + render_thread, + args + ); + render_thread_init_end: + if (!obj_output->render_thread_ok) + free(args); + } + obj_output->render_ticks = 0; + obj_output->render_start = get_ticks_usec(); + return obj_output; + +error: + glx_output_surface_destroy(driver_data, obj_output); + return NULL; +} + +// Returns thread-specific GL context +static inline GLContextState * +glx_output_surface_get_context(object_glx_output_p obj_output) +{ + if (obj_output->render_thread_ok && + obj_output->render_thread == pthread_self()) + return obj_output->render_context; + return obj_output->gl_context; +} + +// Ensures output surface exists +static object_glx_output_p +glx_output_surface_ensure( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface, + Window window +) +{ + object_output_p obj_output; + obj_output = output_surface_ensure(driver_data, obj_surface, window); + if (!obj_output) + return NULL; + + object_glx_output_p glx_output = obj_output->glx; + if (!glx_output) { + unsigned int w, h; + x11_get_geometry(driver_data->x11_dpy, window, NULL, NULL, &w, &h); + glx_output = glx_output_surface_create(driver_data, window, w, h); + if (!glx_output) + return NULL; + obj_output->glx = glx_output; + } + return glx_output; +} + +// Ensures output surface size matches drawable size +static int +glx_output_surface_ensure_size( + xvba_driver_data_t *driver_data, + object_glx_output_p glx_output +) +{ + GLContextState * const gl_context = glx_output_surface_get_context(glx_output); + Display * const dpy = gl_context->display; + const Window win = glx_output->window.xid; + unsigned int width, height; + int size_changed = 0; + + x11_get_geometry(dpy, win, NULL, NULL, &width, &height); + if (glx_output->window.width != width || + glx_output->window.height != height) { + /* If there is still a ConfigureNotify event in the queue, + this means the user-application was not notified of the + change yet. So, just don't assume any change in this case */ + XEvent e; + if (XCheckTypedWindowEvent(dpy, win, ConfigureNotify, &e)) + XPutBackEvent(dpy, &e); + else { + glx_output->window.width = width; + glx_output->window.height = height; + size_changed = 1; + + /* Resize GL rendering window to fit new window size */ + XMoveResizeWindow( + dpy, + glx_output->gl_window.xid, + 0, 0, width, height + ); + XSync(dpy, False); + } + } + + /* Make sure the VA/GLX surface is created */ + if (size_changed) { + destroy_glx_surface(driver_data, glx_output->gl_surface); + glx_output->gl_surface = NULL; + } + if (!glx_output->gl_surface) { + glx_output->gl_surface = create_glx_surface( + driver_data, + width, + height + ); + if (!glx_output->gl_surface) + return -1; + glx_output->gl_surface->gl_context = glx_output->gl_context; + + /* Make sure the FBO is created */ + if (!fbo_ensure(glx_output->gl_surface)) + return -1; + gl_bind_framebuffer_object(glx_output->gl_surface->fbo); + glClear(GL_COLOR_BUFFER_BIT); + gl_unbind_framebuffer_object(glx_output->gl_surface->fbo); + } + return 0; +} + +// Ensure rectangle is within specified bounds +static inline void +ensure_bounds(VARectangle *r, unsigned int width, unsigned int height) +{ + if (r->x < 0) + r->x = 0; + if (r->y < 0) + r->y = 0; + if (r->width > width - r->x) + r->width = width - r->x; + if (r->height > height - r->y) + r->height = height - r->y; +} + +// Query GLX surface status +int +query_surface_status_glx( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface +) +{ + unsigned int i; + for (i = 0; i < obj_surface->output_surfaces_count; i++) { + object_glx_output_p obj_output = obj_surface->output_surfaces[i]->glx; + if (!obj_output) + continue; + if (obj_output->va_surface_status != VASurfaceDisplaying) + continue; + obj_output->va_surface_status = VASurfaceReady; + + /* Make sure all pending OpenGL commands have completed */ + GLContextState old_cs; + if (gl_set_current_context(obj_output->gl_context, &old_cs)) { + glFinish(); + gl_swap_buffers(obj_output->gl_context); + gl_set_current_context(&old_cs, NULL); + } + } + return XVBA_COMPLETED; +} + +// Queue surface for display +VAStatus +flip_surface( + xvba_driver_data_t *driver_data, + object_glx_output_p obj_output +) +{ + object_glx_surface_p const obj_glx_surface = obj_output->gl_surface; + + /* Draw GL surface to screen */ + glBindTexture(obj_glx_surface->target, obj_glx_surface->texture); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); + { + float tw, th; + switch (obj_glx_surface->target) { + case GL_TEXTURE_2D: + tw = 1.0f; + th = 1.0f; + break; + case GL_TEXTURE_RECTANGLE_ARB: + tw = (float)obj_glx_surface->width; + th = (float)obj_glx_surface->height; + break; + default: + tw = 0.0f; + th = 0.0f; + ASSERT(obj_glx_surface->target == GL_TEXTURE_2D || + obj_glx_surface->target == GL_TEXTURE_RECTANGLE_ARB); + break; + } + + const unsigned int w = obj_glx_surface->width; + const unsigned int h = obj_glx_surface->height; + glTexCoord2f(0.0f, 0.0f); glVertex2i(0, 0); + glTexCoord2f(0.0f, 1.0f); glVertex2i(0, h); + glTexCoord2f(1.0f, 1.0f); glVertex2i(w, h); + glTexCoord2f(1.0f, 0.0f); glVertex2i(w, 0); + } + glEnd(); + glBindTexture(obj_glx_surface->target, 0); + + gl_swap_buffers(glx_output_surface_get_context(obj_output)); + obj_output->render_ticks++; + return VA_STATUS_SUCCESS; +} + +static VAStatus +queue_surface( + xvba_driver_data_t *driver_data, + object_glx_output_p obj_output, + object_surface_p obj_surface +) +{ + /* Commit framebuffer to screen */ + obj_surface->va_surface_status = VASurfaceDisplaying; + obj_output->va_surface_status = VASurfaceDisplaying; + + if (obj_output->render_thread_ok) + return VA_STATUS_SUCCESS; + return flip_surface(driver_data, obj_output); +} + +// Render video surface (and subpictures) into the specified drawable +static VAStatus +do_put_surface_glx( + xvba_driver_data_t *driver_data, + object_glx_output_p obj_output, + object_surface_p obj_surface, + const VARectangle *src_rect, + const VARectangle *dst_rect, + const VARectangle *cliprects, + unsigned int num_cliprects, + unsigned int flags +) +{ + /* Ensure VA/GLX surface exists with the specified dimensions */ + object_glx_surface_p obj_glx_surface; + obj_glx_surface = glx_surface_ensure(driver_data, obj_surface, obj_output); + if (!obj_glx_surface) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + + if (glx_output_surface_ensure_size(driver_data, obj_output) < 0) + return VA_STATUS_ERROR_OPERATION_FAILED; + + /* Ensure visible rect is within parent window bounds */ + VARectangle vis_rect = *dst_rect; + ensure_bounds(&vis_rect, obj_output->window.width, obj_output->window.height); + + /* Reset GLX viewport for active context */ + gl_resize(obj_output->window.width, obj_output->window.height); + + /* Transfer surface to texture */ + VAStatus status; + if (!is_empty_surface(obj_surface)) { + status = transfer_surface(driver_data, obj_glx_surface, obj_surface, flags); + if (status != VA_STATUS_SUCCESS) + return status; + } + + /* Make sure color matrix for ProcAmp adjustments is setup */ + status = ensure_procamp_shader(driver_data, obj_glx_surface); + if (status != VA_STATUS_SUCCESS) + return status; + + /* Setup scaling algorithm */ + status = ensure_scaler(driver_data, obj_glx_surface, flags); + if (status != VA_STATUS_SUCCESS) + return status; + + /* Render picture */ + GLVTable * const gl_vtable = gl_get_vtable(); + float params[4]; + unsigned int i; + + gl_bind_framebuffer_object(obj_output->gl_surface->fbo); + gl_vtable->gl_active_texture(GL_TEXTURE0); + glBindTexture(obj_glx_surface->target, obj_glx_surface->texture); + if (obj_glx_surface->hqscaler) { + gl_bind_shader_object(obj_glx_surface->hqscaler); + params[0] = (float)obj_glx_surface->width; + params[1] = (float)obj_glx_surface->height; + params[2] = 1.0f / obj_glx_surface->width; + params[3] = 1.0f / obj_glx_surface->height; + gl_vtable->gl_program_local_parameter_4fv( + GL_FRAGMENT_PROGRAM, + 0, + params + ); + if (obj_glx_surface->hqscaler_texture) { + gl_vtable->gl_active_texture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_1D, obj_glx_surface->hqscaler_texture); + } + } + if (obj_glx_surface->use_procamp_shader) { + gl_bind_shader_object(obj_glx_surface->procamp_shader); + for (i = 0; i < 4; i++) + gl_vtable->gl_program_local_parameter_4fv( + GL_FRAGMENT_PROGRAM, i, + driver_data->cm_composite[i] + ); + } + if (flags & VA_CLEAR_DRAWABLE) { + if (driver_data->va_background_color && + driver_data->va_background_color->value != obj_output->bgcolor) { + obj_output->bgcolor = driver_data->va_background_color->value; + gl_set_bgcolor(obj_output->bgcolor); + } + glClear(GL_COLOR_BUFFER_BIT); + } + glPushMatrix(); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glTranslatef((float)vis_rect.x, (float)vis_rect.y, 0.0f); + if (!is_empty_surface(obj_surface)) { + const float surface_width = obj_surface->xvba_surface_width; + const float surface_height = obj_surface->xvba_surface_height; + float tx1 = src_rect->x / surface_width; + float ty1 = src_rect->y / surface_height; + float tx2 = tx1 + src_rect->width / surface_width; + float ty2 = ty1 + src_rect->height / surface_height; + const int w = vis_rect.width; + const int h = vis_rect.height; + + switch (obj_glx_surface->target) { + case GL_TEXTURE_RECTANGLE_ARB: + tx1 *= obj_glx_surface->width; + tx2 *= obj_glx_surface->width; + ty1 *= obj_glx_surface->height; + ty2 *= obj_glx_surface->height; + break; + } + + glBegin(GL_QUADS); + glTexCoord2f(tx1, ty1); glVertex2i(0, 0); + glTexCoord2f(tx1, ty2); glVertex2i(0, h); + glTexCoord2f(tx2, ty2); glVertex2i(w, h); + glTexCoord2f(tx2, ty1); glVertex2i(w, 0); + glEnd(); + } + gl_vtable->gl_active_texture(GL_TEXTURE0); + glBindTexture(obj_glx_surface->target, 0); + + /* Render subpictures */ + glScalef( + (float)dst_rect->width / (float)obj_surface->width, + (float)dst_rect->height / (float)obj_surface->height, + 1.0f + ); + status = render_subpictures(driver_data, obj_surface, src_rect); + glPopMatrix(); + if (obj_glx_surface->use_procamp_shader) + gl_unbind_shader_object(obj_glx_surface->procamp_shader); + if (obj_glx_surface->hqscaler) { + if (obj_glx_surface->hqscaler_texture) { + gl_vtable->gl_active_texture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_1D, 0); + } + gl_unbind_shader_object(obj_glx_surface->hqscaler); + } + gl_unbind_framebuffer_object(obj_output->gl_surface->fbo); + + /* Queue surface for display */ + return queue_surface(driver_data, obj_output, obj_surface); +} + +VAStatus +put_surface_glx( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface, + Drawable drawable, + const VARectangle *source_rect, + const VARectangle *target_rect, + const VARectangle *cliprects, + unsigned int num_cliprects, + unsigned int flags +) +{ + /* XXX: no clip rects supported */ + if (cliprects || num_cliprects > 0) + return VA_STATUS_ERROR_INVALID_PARAMETER; + + /* Ensure output surface (child window) is set up */ + object_glx_output_p obj_output; + obj_output = glx_output_surface_ensure(driver_data, obj_surface, drawable); + if (!obj_output) + return VA_STATUS_ERROR_OPERATION_FAILED; + + ASSERT(obj_output->window.xid == drawable); + ASSERT(obj_output->gl_window.xid != None); + ASSERT(obj_output->gl_context); + + /* Ensure source rect is within surface bounds */ + VARectangle src_rect = *source_rect; + ensure_bounds(&src_rect, obj_surface->width, obj_surface->height); + + /* Send args to render thread */ + if (obj_output->render_thread_ok) { + uint64_t now = get_ticks_usec(); + PutSurfaceMsg *msg; + + if (now >= obj_output->render_timestamp + VIDEO_REFRESH) { + if (!async_queue_push(obj_output->render_comm, MSG2PTR(MSG_TYPE_FLIP))) + return VA_STATUS_ERROR_OPERATION_FAILED; + obj_output->render_timestamp = now; + } + + msg = malloc(sizeof(*msg)); + if (!msg) + return VA_STATUS_ERROR_ALLOCATION_FAILED; + + msg->obj_surface = obj_surface; + msg->src_rect = src_rect; + msg->dst_rect = *target_rect; + msg->flags = flags; + if (!async_queue_push(obj_output->render_comm, msg)) + return VA_STATUS_ERROR_OPERATION_FAILED; + return VA_STATUS_SUCCESS; + } + + GLContextState old_cs; + if (!gl_set_current_context(obj_output->gl_context, &old_cs)) + return VA_STATUS_ERROR_OPERATION_FAILED; + + VAStatus status; + status = do_put_surface_glx( + driver_data, + obj_output, + obj_surface, + &src_rect, + target_rect, + cliprects, num_cliprects, + flags + ); + + gl_set_current_context(&old_cs, NULL); + return status; +} diff --git a/src/xvba_video_glx.h b/src/xvba_video_glx.h new file mode 100644 index 0000000..ceeb882 --- /dev/null +++ b/src/xvba_video_glx.h @@ -0,0 +1,166 @@ +/* + * xvba_video_glx.c - XvBA backend for VA-API (rendering to GLX) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef XVBA_VIDEO_GLX_H +#define XVBA_VIDEO_GLX_H + +#include <va/va.h> +#include <va/va_backend.h> +#include <GL/gl.h> +#include <GL/glx.h> +#include <pthread.h> +#include "object_heap.h" +#include "xvba_video_x11.h" +#include "utils_glx.h" +#include "uasyncqueue.h" + +#define XVBA_MAX_EVERGREEN_PARAMS 4 + +typedef struct object_glx_output object_glx_output_t; +typedef struct object_glx_surface object_glx_surface_t; +typedef struct object_glx_surface *object_glx_surface_p; + +struct object_image_glx { + GLenum target; + GLenum formats[3]; + GLuint textures[3]; + unsigned int num_textures; + unsigned int width; + unsigned int height; + GLShaderObject *shader; +}; + +struct object_glx_output { + VASurfaceStatus va_surface_status; + object_glx_output_p parent; + unsigned int children_count; + struct { + Window xid; + unsigned int width; + unsigned int height; + } window; + unsigned int bgcolor; + struct { + Window xid; + XVisualInfo *vi; + Colormap cmap; + } gl_window; + GLContextState *gl_context; + object_glx_surface_p gl_surface; + UAsyncQueue *render_comm; + pthread_t render_thread; + unsigned int render_thread_ok; + GLContextState *render_context; + uint64_t render_timestamp; + uint64_t render_ticks; + uint64_t render_start; + pthread_mutex_t lock; +}; + +struct object_glx_surface { + unsigned int refcount; + GLContextState *gl_context; + GLenum target; + GLenum format; + GLuint texture; + unsigned int width; + unsigned int height; + unsigned int va_scale; + XVBASurface *xvba_surface; + GLFramebufferObject *fbo; + unsigned int use_procamp_shader; + GLShaderObject *procamp_shader; + uint64_t procamp_mtime; + GLuint tx_texture; // temporary used for transfer_surface() + XVBASurface *tx_xvba_surface; + int evergreen_workaround; + GLuint evergreen_texture; + GLFramebufferObject *evergreen_fbo; + GLShaderObject *evergreen_shader; + float evergreen_params[1 + XVBA_MAX_EVERGREEN_PARAMS][4]; + unsigned int evergreen_params_count; + GLShaderObject *hqscaler; + GLuint hqscaler_texture; +}; + +// Destroys GLX output surface +void +glx_output_surface_destroy( + xvba_driver_data_t *driver_data, + object_glx_output_p obj_output +) attribute_hidden; + +// Unreferences GLX surface +void +glx_surface_unref( + xvba_driver_data_t *driver_data, + object_glx_surface_p obj_glx_surface +) attribute_hidden; + +// References GLX surface +object_glx_surface_p +glx_surface_ref( + xvba_driver_data_t *driver_data, + object_glx_surface_p obj_glx_surface +) attribute_hidden; + +// Query GLX surface status +int +query_surface_status_glx( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface +) attribute_hidden; + +// Render video surface (and subpictures) into the specified drawable +VAStatus +put_surface_glx( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface, + Drawable drawable, + const VARectangle *source_rect, + const VARectangle *target_rect, + const VARectangle *cliprects, + unsigned int num_cliprects, + unsigned int flags +) attribute_hidden; + +// vaCreateSurfaceGLX +VAStatus xvba_CreateSurfaceGLX( + VADriverContextP ctx, + unsigned int target, + unsigned int texture, + void **gl_surface +) attribute_hidden; + +// vaDestroySurfaceGLX +VAStatus xvba_DestroySurfaceGLX( + VADriverContextP ctx, + void *gl_surface +) attribute_hidden; + +// vaCopySurfaceGLX +VAStatus xvba_CopySurfaceGLX( + VADriverContextP ctx, + void *gl_surface, + VASurfaceID surface, + unsigned int flags +) attribute_hidden; + +#endif /* XVBA_VIDEO_GLX_H */ diff --git a/src/xvba_video_x11.c b/src/xvba_video_x11.c new file mode 100644 index 0000000..9439b91 --- /dev/null +++ b/src/xvba_video_x11.c @@ -0,0 +1,223 @@ +/* + * xvba_video_x11.c - XvBA backend for VA-API (rendering to X11 through PCOM) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "sysdeps.h" +#include "utils.h" +#include "xvba_video.h" +#include "xvba_video_x11.h" +#if USE_GLX +#include "xvba_video_glx.h" +#endif + +#define DEBUG 1 +#include "debug.h" + + +// Create output surface +static object_output_p +output_surface_create( + xvba_driver_data_t *driver_data, + Drawable drawable +) +{ + VASurfaceID surface = object_heap_allocate(&driver_data->output_heap); + if (surface == VA_INVALID_ID) + return NULL; + + object_output_p obj_output = XVBA_OUTPUT(surface); + if (!obj_output) + return NULL; + + obj_output->refcount = 1; + obj_output->drawable = drawable; + obj_output->glx = NULL; + return obj_output; +} + +// Destroy output surface +void +output_surface_destroy( + xvba_driver_data_t *driver_data, + object_output_p obj_output +) +{ +#if USE_GLX + if (obj_output->glx) { + glx_output_surface_destroy(driver_data, obj_output->glx); + obj_output->glx = NULL; + } +#endif + + object_heap_free(&driver_data->output_heap, (object_base_p)obj_output); +} + +// Reference output surface +object_output_p +output_surface_ref( + xvba_driver_data_t *driver_data, + object_output_p obj_output +) +{ + if (obj_output) + ++obj_output->refcount; + return obj_output; +} + +// Unreference output surface +// NOTE: this destroys the surface if refcount reaches zero +void +output_surface_unref( + xvba_driver_data_t *driver_data, + object_output_p obj_output +) +{ + if (obj_output && --obj_output->refcount == 0) + output_surface_destroy(driver_data, obj_output); +} + +// Lookup output surface in the whole output heap +object_output_p +output_surface_lookup( + xvba_driver_data_t *driver_data, + Drawable drawable +) +{ + object_heap_iterator iter; + object_base_p obj = object_heap_first(&driver_data->output_heap, &iter); + while (obj) { + object_output_p obj_output = (object_output_p)obj; + if (obj_output->drawable == drawable) + return obj_output; + obj = object_heap_next(&driver_data->output_heap, &iter); + } + return NULL; +} + +// Ensure an output surface is created for the specified surface and drawable +object_output_p +output_surface_ensure( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface, + Drawable drawable +) +{ + object_output_p obj_output = NULL; + int new_obj_output = 0; + unsigned int i; + + if (!obj_surface) + return NULL; + + /* Check for an output surface matching Drawable */ + for (i = 0; i < obj_surface->output_surfaces_count; i++) { + ASSERT(obj_surface->output_surfaces[i]); + if (obj_surface->output_surfaces[i]->drawable == drawable) { + obj_output = obj_surface->output_surfaces[i]; + break; + } + } + + /* ... that might have been created for another video surface */ + if (!obj_output) { + object_output_p m = output_surface_lookup(driver_data, drawable); + if (m) { + obj_output = output_surface_ref(driver_data, m); + new_obj_output = 1; + } + } + + /* Fallback: create a new output surface */ + if (!obj_output) { + obj_output = output_surface_create(driver_data, drawable); + if (!obj_output) + return NULL; + new_obj_output = 1; + } + + /* Append output surface */ + if (new_obj_output) { + if (realloc_buffer(&obj_surface->output_surfaces, + &obj_surface->output_surfaces_count_max, + 1 + obj_surface->output_surfaces_count, + sizeof(*obj_surface->output_surfaces)) == NULL) + return NULL; + obj_surface->output_surfaces[obj_surface->output_surfaces_count++] = obj_output; + } + return obj_output; +} + +// vaPutSurface +VAStatus +xvba_PutSurface( + VADriverContextP ctx, + VASurfaceID surface, + VADrawable draw, + short srcx, + short srcy, + unsigned short srcw, + unsigned short srch, + short destx, + short desty, + unsigned short destw, + unsigned short desth, + VARectangle *cliprects, + unsigned int number_cliprects, + unsigned int flags +) +{ + XVBA_DRIVER_DATA_INIT; + + xvba_set_display_type(driver_data, VA_DISPLAY_X11); + + D(bug("vaPutSurface(): surface 0x%08x, drawable 0x%08x, " + "src rect (%d,%d):%dx%d, dest rect (%d,%d):%dx%d\n", + surface, POINTER_TO_UINT(draw), + srcx, srcy, srcw, srch, + destx, desty, destw, desth)); + + /* Make sure drawable is valid */ + /* XXX: check the drawable is actually a window */ + if (draw == None) + return VA_STATUS_ERROR_INVALID_PARAMETER; + + object_surface_p obj_surface = XVBA_SURFACE(surface); + if (!obj_surface) + return VA_STATUS_ERROR_INVALID_SURFACE; + + VARectangle src_rect, dst_rect; + src_rect.x = srcx; + src_rect.y = srcy; + src_rect.width = srcw; + src_rect.height = srch; + dst_rect.x = destx; + dst_rect.y = desty; + dst_rect.width = destw; + dst_rect.height = desth; + + const XID xid = POINTER_TO_UINT(draw); +#if USE_GLX + return put_surface_glx(driver_data, obj_surface, + xid, + &src_rect, &dst_rect, + cliprects, number_cliprects, + flags); +#endif + return VA_STATUS_ERROR_UNIMPLEMENTED; +} diff --git a/src/xvba_video_x11.h b/src/xvba_video_x11.h new file mode 100644 index 0000000..99a3498 --- /dev/null +++ b/src/xvba_video_x11.h @@ -0,0 +1,92 @@ +/* + * xvba_video_x11.h - XvBA backend for VA-API (rendering to X11 through PCOM) + * + * xvba-video (C) 2009-2011 Splitted-Desktop Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef XVBA_VIDEO_X11_H +#define XVBA_VIDEO_X11_H + +#include "xvba_driver.h" + +typedef struct object_glx_output *object_glx_output_p; + +typedef struct object_output object_output_t; +struct object_output { + struct object_base base; + unsigned int refcount; + Drawable drawable; + object_glx_output_p glx; +}; + +// Destroy output surface +void +output_surface_destroy( + xvba_driver_data_t *driver_data, + object_output_p obj_output +) attribute_hidden; + +// Reference output surface +object_output_p +output_surface_ref( + xvba_driver_data_t *driver_data, + object_output_p obj_output +) attribute_hidden; + +// Unreference output surface +// NOTE: this destroys the surface if refcount reaches zero +void +output_surface_unref( + xvba_driver_data_t *driver_data, + object_output_p obj_output +) attribute_hidden; + +// Lookup output surface in the whole output heap +object_output_p +output_surface_lookup( + xvba_driver_data_t *driver_data, + Drawable drawable +) attribute_hidden; + +// Ensure an output surface is created for the specified surface and drawable +object_output_p +output_surface_ensure( + xvba_driver_data_t *driver_data, + object_surface_p obj_surface, + Drawable drawable +) attribute_hidden; + +// vaPutSurface +VAStatus +xvba_PutSurface( + VADriverContextP ctx, + VASurfaceID surface, + VADrawable draw, + short srcx, + short srcy, + unsigned short srcw, + unsigned short srch, + short destx, + short desty, + unsigned short destw, + unsigned short desth, + VARectangle *cliprects, + unsigned int number_cliprects, + unsigned int flags +) attribute_hidden; + +#endif /* XVBA_VIDEO_X11_H */ |