Forked panamaz for vulkan only code.
authorNot Zed <notzed@gmail.com>
Wed, 14 Sep 2022 02:46:02 +0000 (12:16 +0930)
committerNot Zed <notzed@gmail.com>
Wed, 14 Sep 2022 02:46:02 +0000 (12:16 +0930)
44 files changed:
.gitignore [new file with mode: 0644]
COPYING [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
config.make.in [new file with mode: 0644]
java.make [new file with mode: 0644]
maven.make [new file with mode: 0644]
nbproject/build-impl.xml [new file with mode: 0644]
nbproject/genfiles.properties [new file with mode: 0644]
nbproject/project.properties [new file with mode: 0644]
nbproject/project.xml [new file with mode: 0644]
src/notzed.display/classes/au/notzed/display/Display.java [new file with mode: 0644]
src/notzed.display/classes/au/notzed/display/Event.java [new file with mode: 0644]
src/notzed.display/classes/au/notzed/display/Window.java [new file with mode: 0644]
src/notzed.display/classes/module-info.java [new file with mode: 0644]
src/notzed.vulkan.test/classes/module-info.java [new file with mode: 0644]
src/notzed.vulkan.test/classes/vulkan/test/Cube.java [new file with mode: 0644]
src/notzed.vulkan.test/classes/vulkan/test/Demo.java [new file with mode: 0644]
src/notzed.vulkan.test/classes/vulkan/test/GLMaths.java [new file with mode: 0644]
src/notzed.vulkan.test/classes/vulkan/test/TestCube.java [new file with mode: 0644]
src/notzed.vulkan.test/classes/vulkan/test/TestMandelbrot.java [new file with mode: 0755]
src/notzed.vulkan.test/classes/vulkan/test/TestSDF.java [new file with mode: 0644]
src/notzed.vulkan.test/classes/vulkan/test/Tutorial.java [new file with mode: 0644]
src/notzed.vulkan.test/gen/gen.make [new file with mode: 0644]
src/notzed.vulkan.test/gen/generate-shaderio [new file with mode: 0755]
src/notzed.vulkan.test/shaders/vulkan/test/cube.frag [new file with mode: 0644]
src/notzed.vulkan.test/shaders/vulkan/test/cube.vert [new file with mode: 0644]
src/notzed.vulkan.test/shaders/vulkan/test/demo.frag [new file with mode: 0644]
src/notzed.vulkan.test/shaders/vulkan/test/demo.vert [new file with mode: 0644]
src/notzed.vulkan.test/shaders/vulkan/test/mandelbrot.comp [new file with mode: 0644]
src/notzed.vulkan.test/shaders/vulkan/test/sdf.frag [new file with mode: 0644]
src/notzed.vulkan.test/shaders/vulkan/test/sdf.vert [new file with mode: 0644]
src/notzed.vulkan/classes/module-info.java [new file with mode: 0644]
src/notzed.vulkan/gen/command-types.api [new file with mode: 0644]
src/notzed.vulkan/gen/gen.make [new file with mode: 0644]
src/notzed.vulkan/gen/generate-vulkan [new file with mode: 0755]
src/notzed.vulkan/gen/struct-types.api [new file with mode: 0644]
src/notzed.vulkan/gen/vulkan.pm [new file with mode: 0644]
src/notzed.xcb/classes/module-info.java [new file with mode: 0644]
src/notzed.xcb/classes/xcb/Connection.java [new file with mode: 0644]
src/notzed.xlib/classes/module-info.java [new file with mode: 0644]
src/notzed.xlib/gen/gen.make [new file with mode: 0644]
src/notzed.xlib/gen/xlib.api [new file with mode: 0644]
src/notzed.xlib/gen/xlib.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..45e0b9a
--- /dev/null
@@ -0,0 +1,8 @@
+bin/
+config.make
+mandelbrot.pam
+movie.avi
+/build/
+/dist/
+/.lib/
+/nbproject/private/
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  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.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  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.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     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
+state 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 3 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU 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.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..3da2adf
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,28 @@
+
+dist_VERSION=0.0.99
+dist_NAME=vulkanz
+dist_EXTRA=README COPYING
+
+include config.make
+
+java_MODULES = \
+       notzed.xlib notzed.xcb notzed.display \
+       notzed.vulkan notzed.vulkan.test
+
+notzed.vulkan_JDEPMOD = notzed.xlib notzed.xcb
+notzed.vulkan.test_JDEPMOD = notzed.vulkan
+notzed.xcb_JDEPMOD =
+notzed.xlib_JDEPMOD =
+notzed.display_JDEPMOD = notzed.xlib notzed.xcb notzed.vulkan
+
+notzed.vulkan.test_JMAIN = vulkan.test.TestMandelbrot vulkan.test.TestCube vulkan.test.TestSDF
+
+$(foreach module,$(java_MODULES),$(eval $(module)_JMAINFLAGS=--enable-native-access=notzed.nativez,$(module)))
+
+notzed.vulkan.test_JMAINFLAGS = --enable-native-access=notzed.nativez,notzed.xlib,notzed.xcb,notzed.vulkan
+
+include java.make
+
+maven_central_JARS =org.openjdk.jmh:jmh-core:1.33 org.openjdk.jmh:jmh-generator-annprocess:1.33
+
+include maven.make
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..3112661
--- /dev/null
+++ b/README
@@ -0,0 +1,267 @@
+
+Introduction
+------------
+
+This is various experiments with the panama-foreign abi JEP for
+invoking native C functions from Java directly and without JNI.
+
+The main goal is to experiment with creating a "java friendly" and
+mostly type-safe api directly in one step, without requiring
+additional wrapping.
+
+It uses a gcc plugin to compile c headers to obtain most of the api
+information, but requires cpp and perl to extract the values of
+#define constants as they are not available to the gcc plugin.
+
+This api information is then converted to Java code using a
+config-directed perl script `export-api'.  The config file can be used
+to create static (all-in-one) or object oriented output.  It includes
+templated strings as well as perl fragments to generate various source
+parts.
+
+The vulkan api is officially defined by a registry in xml, so there's
+a separate generator for that.
+
+Compile
+-------
+
+Requirements are gcc and GNU cpp, perl, GNU make, and of course a
+compatible jdk-foreign jdk.  The various modules require headers or
+sdks for their corresponding libraries, e.g. ffmpeg-5.x,
+vulkan-tools-1.2.x.
+
+Copy config.make.in to config.make and modify any variables required.
+
+Then make everything, parallel make should work.
+
+$ make -j
+
+Or build one module:
+
+$ make notzed.nativez
+
+Or run a demo (see next section):
+
+$ make run-notzed.vkregistry.test/vulkan.test.TestMandelbrot
+
+A non-recursive make setup is used although make file fragments are
+included from various locations across the modules.  All java is
+compiled as modules.
+
+JAVA_HOME must point to a compatible panama-enabled jdk.
+
+The latest at the time of writing was:
+
+ branch: foreign-jextract
+ commit: 2e1291680024f12fbf2f0f02b0f79d7b4348b369
+ date: Fri Feb 4 11:01:21 2022 +0000
+
+All output and intermediate files are in bin/
+
+bin/modules/<module>/classes - compiled java modules
+bin/gen/<module>/classes - generated java
+bin/gen/<module>/gen - generated intermediate non-java
+
+These are more or less an exploded view of all jmod files:
+
+bin/<target>/bin - commands for all modules
+bin/<target>/lib - libraries/config and modular jar files for all modules
+bin/<target>/include - header files for all modules
+
+Finally:
+
+bin/<target>/jmods - .jmod modules
+
+
+Demos
+-----
+
+Most examples have a demo, see the <module>_JMAIN variables in the
+Makefile for the targets.  They are executed using:
+
+$ make run-<module>/<main-class>
+
+Modules
+--------
+
+notzed.nativez contains some support classes and the code generator.
+The gcc plugin source is in src/notzed.nativez/native/ the code
+generator is in src/notzed.nativez/{bin,lib}.
+
+notzed.api is a pseudo-module containing a simple test c api for
+experiments, it just builds into a library.
+
+notzed.apistatic is a 'all in one class' static wrapper for
+notzed.api.
+
+notzed.apiobject is an object-oriented wrapper for notzed.api.
+
+notzed.clstatic is an 'all in one class' static wrapper for OpenCL
+(2.1).  The api closesly matches the C api except it converts error
+codes into exceptins and can infer certain arguments from others such
+as length parameters.  This is probably the most complete api.
+
+notzed.ffmpeg is a partial object-oriented mapping for ffmpeg-5.0 that
+closely follows the jjmpeg design.  It's enough to read video frames.
+The demo requires a file 'movie.api' in the root directory to run.
+
+notzed.vkheader uses the header-based generator on the vulkan
+installed headers.  This is still very incomplete work in progress.
+Much meta-data is lost such as which functions are extensions during
+the generation of the headers.  This is incomplete and dead-ended.
+
+notzed.vkregistry uses a completely different generator which directly
+parses the official xml registry specification for vulkan
+(/usr/share/vulkan/registry/vk.xml).  This is directly converted to
+about-as-java-friendly a vulkan api as one can hope for, particularly
+the constructors for all the config objects.  This is incomplete and
+dead-ended.
+
+notzed.vulkan also uses a different generator which directly parses
+the official xml registry specification for vulkan
+(/usr/share/vulkan/registry/vk.xml).  This version uses templates to
+generate the various structures in more concise and relatively
+consistenet way.  Work in progress.
+
+Export process
+--------------
+
+The main generator is written in perl and lives in
+src/notzed.nativez/{bin,lib}.
+
+The process:
+
+* run the gcc plugin to extract all of the available c structures from
+  gcc and save them to a perl hash.
+
+  gcc plugins aren't very well documented so it was a lot of trial and
+  error to get it to output all the type information even starting
+  with a partial example.  The names of parameters for function calls
+  were particularly problematic.
+
+* optionally run export-defines to extract #define constants.  They
+  can be grouped/excluded by name or by the filename they belong to.
+  The the first rule to match will complete the processing for a given
+  type.
+
+  This is also a surprisingly difficult process because the c
+  pre-processor can just generate arbitrary c expressions so the only
+  way to find their correct value is to execute the c.  So the export
+  script generates a c file which is compiled and executed to generate
+  the perl definitions.
+
+  Currently the types are mapped to a 'compatible' native type by
+  using the gcc operators __builtin_choose_expr,
+  __builtin_types_compatible_p, and typeof to implement a
+  pseudo-function overloading in c - possiblly using c++ is a better
+  choice here.  For now inclusions or exclusions are required to
+  avoid problematic definitions that confuse these operators.
+
+These files are then fed to generate-api.  It proceeds in multiple
+stages.
+
+First stages are handled by lib/api.pm.
+
+* Load and preprocess the api definition.
+
+ - Perform a bunch of 'fix up' processing on the data structures such
+   as inserting anonymous types which are not generated by the plugin.
+
+ - Create a full dependency tree for the objects
+   specificallyreferenced by the api definition so only objects and
+   functions of interest are included.
+
+ - Resolve all the various export options using some rules and store
+   the information about the selected options onto target objects.
+
+Then generate-api produces the java files from a combination of the
+processed data definitions and the api definition.
+
+* Export 'libraries'.  These are static classes of functions and/or
+  constants.
+
+ - Can include static methods.  Methods can have per-method template
+   overrides.
+
+ - Can include constants.
+
+* Export 'structures'.  These represent C pointers.
+
+ - struct/union have a MemorySegment, a layout, and potentially
+   accessors for fields.
+
+ - anonymous structures just have a MemoryAddress and no accessors.
+
+ - Can include static and member methods.  Methods can have per-method
+   template overrides.
+
+ - Can include constants.
+
+* Export 'calls'.  These are function interfaces for function pointers.
+
+ - The interface is the java form of the call.
+
+ - If available a typedef name is used, otherwise the names are mapped
+   to a mangled name of the form Call_<args>_<return>.
+
+ - An upcall factory creates an upcall to an instance of a hidden
+   trampline interface which performs the mapping of arguments to java
+   and returns to c.
+
+ - A downcall factory creates a downcall which maps the java call to
+   native and back.
+
+ - Both factories return a record of type FunctionPointer<type> which
+   contains both a NativeSymbol and the java interface instance so they
+   can be used from either environment.
+
+* Export 'constants'.  These are referenced enums or any defines which
+  haven't been included in any other library or struct.
+
+ - enums are mapped to an interface with fields of:
+     'static final int FOO = xx'
+   of
+     'static final long FOO = xx'
+
+ - defines are mapped to a matching native type which includes
+   floating point types and strings in addition to all the integral
+   types.
+
+lib/code.pm is used to generate some of the code and handle
+templating. Common code templates are defined in lib/code.api but can
+be extended or replaced by a given api file.
+
+lib/method.pm is used to generate per-field (struct) and per-argument
+(function) sub-templates for mapping c to and from java.  The
+sub-templates it uses are defined in lib/types.api but again they can
+be extended or replaced by a given api file.
+
+Status
+------
+
+It's all very much work in progress and due to the constant changes in
+panama will be in flux for some time.
+
+* bitfields are implemented.
+* varargs is not implemented.
+* the generator for notzed.vkregistry uses a lot of miserable
+  write-once perl.
+* the scope and object lifecycle stuff is not really sorted out yet.
+* the config format and features are still being fiddled with.
+* the config file isn't really documented.
+* the build system is still being fiddled with, some of the output
+  directories are mixed up.
+* linux-amd64 only at this point in time.
+
+License
+-------
+
+GNU General Public License, version 3 or later, but see individual
+file headers for specifics.
+
+Links
+-----
+
+ * https://www.zedzone/software/panamaz.html - project page.
+ * https://openjdk.java.net/projects/panama - openjdk panama page.
+ * https://github.com/openjdk/panama-foreign - openjdk panama source.
diff --git a/config.make.in b/config.make.in
new file mode 100644 (file)
index 0000000..d682dd1
--- /dev/null
@@ -0,0 +1,49 @@
+
+TARGET ?= linux-amd64
+
+JAVA_HOME ?= /usr/local/jdk
+JAVAFX_HOME ?= /usr/local/javafx-sdk
+FFMPEG_HOME ?= /opt/ffmpeg-5.0
+NATIVEZ_HOME=bin/$(TARGET)
+GCCPLUGINDIR:=$(shell gcc -print-file-name=plugin)
+
+JAVAMODPATH = bin/$(TARGET)/lib
+JAVACFLAGS =
+JMAINFLAGS = -Djava.library.path=bin/linux-amd64/lib:$(FFMPEG_HOME)/lib:/usr/lib64
+
+JAVA ?= $(JAVA_HOME)/bin/java
+JAVAC ?= $(JAVA_HOME)/bin/javac
+JAR ?= $(JAVA_HOME)/bin/jar
+JMOD ?= $(JAVA_HOME)/bin/jmod
+
+CFLAGS = -fPIC -Os -Wall
+CXXFLAGS =-fPIC -Os -Wall
+
+# Linux options
+# USE_SO_VERSION adds the major version to the library open name for ffmpeg libs on linux.
+linux-amd64_CPPFLAGS = \
+       -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux \
+       -DUSE_SO_VERSION=1
+linux-amd64_CFLAGS = -fPIC -Os -Wall
+linux-amd64_CC = cc
+linux-amd64_CXXFLAGS = -fPIC -Os -Wall
+linux-amd64_CXX = g++
+linux-amd64_LD = ld
+
+linux-amd64_SO = .so
+linux-amd64_LIB = lib
+
+# Windows options
+windows-amd64_CPPFLAGS = \
+       -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux \
+       -DHAVE_ALIGNED_MALLOC \
+       -DWIN32
+windows-amd64_CFLAGS = -Os -Wall
+windows-amd64_CC = x86_64-w64-mingw32-gcc
+windows-amd64_CXXFLAGS = -Os -Wall
+windows-amd64_CXX = x86_64-w64-mingw32-g++
+windows-amd64_LD = x86_64-w64-mingw32-ld
+windows-amd64_LDFLAGS = -Wl,--subsystem,windows
+
+windows-amd64_SO = .dll
+windows-amd64_LIB =
diff --git a/java.make b/java.make
new file mode 100644 (file)
index 0000000..c3b9030
--- /dev/null
+++ b/java.make
@@ -0,0 +1,422 @@
+#
+# Copyright (C) 2019,2022 Michael Zucchi
+#
+# This is the copyright for java.make
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+#
+
+# General purpose modular java makefile that supports native library
+# compilation directly.  Non-recrusve implementation.
+#
+# Uses metamake programming with some file conventions to implement
+# auto-make-like features.
+
+# Define modules
+# --------------
+# java_MODULES list of java modules to compile.  The sources must
+#              exist in src/<module>/classes.  Resource files are
+#              stored in src/<module>/classes.  Source-code
+#              generators must exist in src/<module>/gen.  Native
+#              libraries must exist in src/<module>/jni.
+
+# native_MODULES list of native-only "modules".
+
+
+# Global variables
+
+# JAVA_HOME            location of jdk.
+# JAVAC                        java compiler to use.  Default is 'javac' on the path.
+# JAVACFLAGS           javac flags applied to all invocations.
+# JAR                  jar command.
+# JARFLAGS             jar flags
+# JMOD                 jmod command.
+# JMODFLAGS            jmod flags.
+# JAVAFLAGS            java flags for run targets
+
+# Module specific variables
+
+# <module>_JDEPMOD     Lists modules which this one depends on.
+
+# <module>_JAVACFLAGS  Extra module-specific flags for each command.
+# <module>_JARFLAGS
+# <module>_JMODFLAGS
+
+# all paths are relative to the root package name
+
+# <module>_JAVA                        Java sources.  If not set it is found from src/<module>/classes/(*.java)
+# <module>_RESOURCES           .jar resources.  If not set it is found from src/<module>/classes/(not *.java)
+# <module>_JAVA_GENERATED      Java generated sources.
+# <module>_RESOURCES_GENERATED Java generated sources.
+
+# Variables for use in fragments
+
+# gen.make and jni.make can additionally make use of these variables
+
+# <module>_gendir      Location for files used in Java generation process (per project).
+# <module>_genjavadir  Location where _JAVA_GENERATED .java files will be created (per project).
+# <module>_objdir      Location for c objects (per target).
+# <module>_incdir      Location for output includes, .jmod staging.
+# <module>_libdir      Location for output libraries, .jmod staging.  May point to _bindir.
+# <module>_bindir      Location for output commands, .jmod staging.
+
+# Define libraries
+# ----------------
+
+# Each module can define one or more native libraries.
+
+# These are compiled after the java sources have been compiled as that
+# process also generates any native binding headers.
+
+# <module>_NATIVE_LIBRARIES    list of libraries to build.
+# library names match System.loadLibrary().
+
+# Global variables
+
+# <target>_LDFLAGS
+# <target>_LDLIBS
+# <target>_CPPFLAGS
+# <target>_CFLAGS
+# <target>_CC
+# <target>_CXXFLAGS
+# <target>_CXX
+# SO           shared library suffix
+# LIB          shared library prefix
+
+# Utility functions
+#
+# $(call library-path,<module>,<libname>) will resolve to the library file name.
+
+# Per library variables.
+
+# <library>_SOURCES    .c source files for library.  Paths are relative to src/<module>/native.
+# <library>_CXXSOURCES .c source files for library.  Paths are relative to src/<module>/native.
+# <library>_HEADERS    header files for install/jmod
+# <library>_COMMANDS   commands/bin/scripts for install/jmod
+
+# <library>_LDFLAGS    link flags
+# <library>_LIBADD     extra objects to add to link line
+# <library>_LDLIBS     link libraries
+# <library>_CPPFLAGS   c and c++ pre-processor flags.  "-Isrc/<module>/jni -Ibin/include/<module>" is implicit.
+# <library>_CCFLAGS    c compiler flags
+# <library>_CXXFLAGS   c++ compiler flags
+
+# <library>_DEPENDENCIES       A list of other objects on which this library depends before linking.
+
+# .c and .cc files have dependencies automatically generated
+
+# Targets
+# -------
+
+# make gen             only generate java sources
+# make clean           rm -rf bin
+# make dist            create dist tar in bin/
+# make | make jar      make all jars and jmods
+
+# Outputs
+# -------
+
+# All intermediate and output files are written to bin/
+
+# This layout is enforced by javac
+#  bin/include/<module>/        .h files from javac -h
+#  bin/modules/<module>/        .class files from javac
+
+# This layout is convenient for netbeans
+#  bin/gen/<module>/gen/       .c, exe files for generator     free use
+#  bin/gen/<module>/classes/   .java files from generator      <module>_JAVA_GENERATED
+
+# Working files
+#  bin/status/                 marker files for makefile
+
+#  bin/<module>/<target>/lib   .so librareies for jmod         <module>_LIBRARIES = libname
+#  bin/<module>/<target>/obj   .o, .d files for library        <libname>_SOURCES
+#  bin/<module>/<target>/include .h files for jmod             <libname>_HEADERS
+#  bin/<module>/<target>/<module>.jmod .jmod module
+
+# Output files
+#  bin/<target>/lib/           modular jar files and shared libraries for GNU/linux dev
+#  bin/<target>/include/       header files for exported shared libraries
+#  bin/<target>/bin/           shared libraries for microsoft dev
+#  bin/<target>/jmods/         jmod files for 'jlink' use.
+
+# ######################################################################
+
+all_MODULES = $(java_MODULES) $(native_MODULES)
+
+E:=
+S:=$(E) $(E)
+SO=$($(TARGET)_SO)
+LIB=$($(TARGET)_LIB)
+
+# Define some useful variables before including fragments
+define common_variables=
+$1_gendir:=bin/gen/$1/gen
+$1_genjavadir:=bin/gen/$1/classes
+$1_objdir:=bin/$1/$(TARGET)/obj
+$1_incdir:=bin/$1/$(TARGET)/include
+$1_libdir:=$$(if $$(filter windows-%,$(TARGET)),bin/$1/$(TARGET)/bin,bin/$1/$(TARGET)/lib)
+$1_bindir:=bin/$1/$(TARGET)/bin
+endef
+
+define java_variables=
+ifndef $1_JAVA
+$1_JAVA := $$(shell cd src/$1/classes && find * -type f -name '*.java')
+endif
+ifndef $1_RESOURCES
+$1_RESOURCES := $$(shell cd src/$1/classes && find * -type f \! -name '*.java')
+endif
+endef
+
+java_libdir:=$(if $(filter windows-%,$(TARGET)),bin/$(TARGET)/bin,bin/$(TARGET)/lib)
+java_bindir:=bin/$(TARGET)/bin
+java_jardir:=bin/$(TARGET)/lib
+java_incdir:=bin/$(TARGET)/include
+java_jmoddir:=bin/$(TARGET)/jmods
+
+$(foreach module,$(java_MODULES) $(native_MODULES),$(eval $(call common_variables,$(module))))
+$(foreach module,$(java_MODULES),$(eval $(call java_variables,$(module))))
+
+# ######################################################################
+
+all:
+jar:
+gen:
+
+.PHONY: all clean jar gen $(java_MODULES)
+clean:
+       rm -rf bin
+
+# Gen is things that go into the jar (sources and resources)
+include $(wildcard $(all_MODULES:%=src/%/gen/gen.make))
+# Native is things that go into the sdk/jmod
+include $(wildcard $(all_MODULES:%=src/%/native/native.make))
+
+# ######################################################################
+
+# create module depencies
+# variables:
+# <module>_sdk is the target location of an expanded 'sdk' for this module
+#  it resides in a common location bin/<target>/
+# <module>_jmod is the target location of a staging area for jmod files
+#  is resides in a per-module lcoation bin/<module>/<target>/
+# <module>_java is all the targets that will cause the invocation of javac
+#  it includes the module source, generated sources, and sentinals for generated sources
+
+# targets:
+# bin/status/<module>.depjava marks all source/generated sources are ready/updated
+# bin/status/<module>.depjar all compiled class files and resources are ready/updated
+# bin/status/<module>.sdk all files are available in bin/<target> as if it was an installed image
+
+define module_vars=
+$1_sdk  := $(addprefix $(java_bindir)/,$($1_COMMANDS)) $(addprefix $(java_libdir)/,$($1_LIBRARIES)) $($1_NATIVE_LIBRARIES:%=$(java_libdir)/lib%.so)
+$1_jmod := $(addprefix $($1_bindir)/,$($1_COMMANDS)) $(addprefix $($1_libdir)/,$($1_LIBRARIES)) $($1_NATIVE_LIBRARIES:%=$($1_libdir)/lib%.so)
+$1_java :=$($1_JAVA:%=src/$1/classes/%) $($1_JAVA_GENERATED:%=$($1_genjavadir)/%)
+$1_resources:= $($1_RESOURCES:%=src/$1/classes/%) $($1_RESOURCES_GENERATED:%=$($1_genjavadir)/%)
+$1_depjava := $($1_API:%=bin/status/$1-%.export) $(patsubst %,bin/status/%.classes, $(filter $($1_JDEPMOD),$(java_MODULES)))
+
+ifneq ("$$(strip $$($1_java) $$($1_depjava))", "")
+bin/status/$1.depjava: $$($1_java) $$($1_depjava)
+       @install -d $$(@D)
+       touch $$@
+bin/status/$1.depjar: bin/status/$1.classes $$($1_resources)
+       @install -d $$(@D)
+       touch $$@
+bin/status/$1.depmod: bin/status/$1.classes $$($1_resources) $$($1_jmod)
+       @install -d $$(@D)
+       touch $$@
+bin/status/$1.sdk: $(java_jardir)/$1.jar
+jar: $(java_jardir)/$1.jar
+gen: bin/status/$1.depjava
+$1 all: $(java_jardir)/$1.jar $(java_jmoddir)/$1.jmod
+else
+# acutally not sure here?
+$1 all: bin/status/$1.sdk
+endif
+
+$1-sdk sdk: bin/status/$1.sdk
+
+bin/status/$1.sdk: $$($1_sdk) $$($1_jmod)
+       @install -d $$(@D)
+       touch $$@
+
+endef
+
+#$(foreach m,$(all_MODULES),$(info $(call module_vars,$m)))
+$(foreach m,$(all_MODULES),$(eval $(call module_vars,$m)))
+
+# ######################################################################
+# notzed.nativez export-api
+# ######################################################################
+
+define api_targets=
+bin/status/$1-$2.export: src/$1/gen/$2.api src/$1/gen/$2.h
+bin/status/$1-$2.export:
+       mkdir -p bin/gen/$1/gen bin/status
+       $(NATIVEZ_HOME)/bin/export-api \
+               -w bin/gen/$1/gen -d bin/gen/$1/classes $($1_APIFLAGS) $($1_$2_APIFLAGS) src/$1/gen/$2.api
+       touch $$@
+
+bin/status/$1-$2.export.d:
+       @$(NATIVEZ_HOME)/bin/export-api -M -MT "$$(@:.d=) $$@" -MF $$@ \
+               -w bin/gen/$1/gen -d bin/gen/$1/classes $($1_APIFLAGS) $($1_$2_APIFLAGS) src/$1/gen/$2.api 2>/dev/null
+
+$(if $(filter clean dist gen,$(MAKECMDGOALS)),,-include bin/status/$1-$2.export.d)
+endef
+
+$(foreach m,$(all_MODULES),$(foreach a,$($m_API),$(eval $(call api_targets,$m,$a))))
+
+# ######################################################################
+# Java
+# ######################################################################
+
+# Build targets for java modules
+
+define java_targets=
+
+# Create (modular) jar
+$(java_jardir)/$1.jar: bin/status/$1.depjar
+       @install -d $$(@D)
+       $(JAR) cf $$@ \
+         $(JARFLAGS) $$($(1)_JARFLAGS) \
+         -C bin/modules/$(1) . \
+         $(if $($1_RESOURCES),$($1_RESOURCES:%=-C src/$1/classes %)) \
+         $(if $($1_RESOURCES_GENERATED),$($1_RESOURCES_GENERATED:%=-C bin/gen/$1/classes %))
+
+# Create a jmod
+$(java_jmoddir)/$1.jmod: bin/status/$1.depmod
+       rm -f $$@
+       @install -d $$(@D)
+       $$(JMOD) create \
+         $$(JMODFLAGS) $$($(1)_JMODFLAGS) \
+         --target-platform $(TARGET) \
+         --class-path bin/modules/$(1) \
+         $$(if $$(wildcard bin/$(1)/$(TARGET)/include),--header-files bin/$(1)/$(TARGET)/include) \
+         $$(if $$(wildcard src/$(1)/legal),--legal-notices src/$(1)/legal) \
+         $$(if $$(wildcard bin/$(1)/$(TARGET)/bin),--cmds bin/$(1)/$(TARGET)/bin) \
+         $$(if $$(wildcard bin/$(1)/$(TARGET)/lib),--libs bin/$(1)/$(TARGET)/lib) \
+         $$@
+
+# Create an IDE source zip, paths have to match --module-source-path
+$(java_jardir)/$1-sources.zip: bin/status/$1.depjar
+       @install -d $$(@D)
+       $(JAR) -c -f $$@ -M \
+               $$($1_JAVA:%=-C src/$1/classes %) \
+               $$($1_JAVA_GENERATED:%=-C bin/gen/$1/classes %)
+
+# resources
+bin/modules/$1/%: src/$1/classes/%
+       install -vD $$< $$@
+
+# Compile module.
+bin/status/$1.classes: bin/status/$1.depjava
+       @install -d $$(@D)
+       $(JAVAC) \
+               --module-source-path "src/*/classes:bin/gen/*/classes" \
+               $(if $(JAVAMODPATH),--module-path $(subst $(S),:,$(JAVAMODPATH))) \
+               $(JAVACFLAGS) $($1_JAVACFLAGS) \
+               -d bin/modules \
+               -m $1 \
+               $$($1_JAVA:%=src/$1/classes/%) \
+               $$($1_JAVA_GENERATED:%=bin/gen/$1/classes/%)
+       touch $$@
+endef
+
+#$(foreach module,$(java_MODULES),$(info $(call java_targets,$(module))))
+$(foreach module,$(java_MODULES),$(eval $(call java_targets,$(module))))
+
+# ######################################################################
+
+# setup run-* targets
+define run_targets=
+run-$1/$2: bin/status/$1.sdk $($1_JDEPMOD:%=bin/status/%.sdk)
+       LD_LIBRARY_PATH=$(FFMPEG_HOME)/lib \
+       $(JAVA) \
+               $(if $(strip $(JAVAMODPATH) $($1_JAVAMODPATH)),--module-path $(subst $(S),:,$(strip $(JAVAMODPATH) $($1_JAVAMODPATH)))) \
+               $(JMAINFLAGS) $($1_JMAINFLAGS) \
+               -m $1/$2 \
+               $(ARGV)
+.PHONY: run-$1/$2
+endef
+
+#$(foreach module,$(java_MODULES),$(foreach main,$($(module)_JMAIN),$(info $(call run_targets,$(module),$(main)))))
+$(foreach module,$(java_MODULES),$(foreach main,$($(module)_JMAIN),$(eval $(call run_targets,$(module),$(main)))))
+
+# ######################################################################
+# C and c++ native library support
+# ######################################################################
+
+define native_library=
+# Rule for library $$2 in module $$1
+$2_OBJS = $(patsubst %.c, $($1_objdir)/%.o, $($2_SOURCES)) \
+       $(patsubst %.cc, $($1_objdir)/%.o, $($2_CXXSOURCES))
+$2_SRCS = $(addprefix src/$1/native/,$($2_SOURCES))
+$2_SO = $($1_libdir)/$(LIB)$2$(SO)
+
+# Copy anything from staging area for jmods bin/module/<target>/* to sdk area bin/<target>/*
+$(java_libdir)/%: $($(1)_libdir)/%
+       @install -d $$(@D)
+       ln -fs $$(abspath $$<) $$@
+$(java_bindir)/%: $($(1)_bindir)/%
+       @install -d $$(@D)
+       ln -fs $$(abspath $$<) $$@
+$(java_incdir)/%: $($(1)_incdir)/%
+       @install -d $$(@D)
+       ln -fs $$(abspath $$<) $$@
+
+$($1_libdir)/$(LIB)$2$(SO): $$($2_OBJS) $($2_LIBADD) $($2_DEPENDENCIES)
+       @install -d $$(@D)
+       $($(TARGET)_CC) -o $$@ -shared \
+               $($(TARGET)_LDFLAGS) $($2_LDFLAGS) $$($2_OBJS) $($2_LIBADD) $($(TARGET)_LDLIBS) $($2_LDLIBS)
+
+$($1_objdir)/%.o: src/$1/native/%.c
+       @install -d $$(@D)
+       $($(TARGET)_CC) -Isrc/$1/native -Ibin/include/$1 \
+               $($(TARGET)_CPPFLAGS) $($2_CPPFLAGS) \
+               $($(TARGET)_CFLAGS) $($2_CFLAGS) -c -o $$@ $$<
+
+$($1_objdir)/%.o: src/$1/native/%.cc
+       @install -d $$(@D)
+       $($(TARGET)_CXX) -Isrc/$1/native -Ibin/include/$1 \
+               $($(TARGET)_CPPFLAGS) $($2_CPPFLAGS) \
+               $($(TARGET)_CXXFLAGS) $($2_CXXFLAGS) -c -o $$@ $$<
+
+# auto-dependencies for c files
+$($1_objdir)/%.d: src/$1/native/%.c
+       @install -d $$(@D)
+       @rm -f $$@
+       @$($(TARGET)_CC) -MM -MT "$$(@:.d=.o) $$@" -Isrc/$1/jni -Ibin/include/$1 \
+               $($(TARGET)_CPPFLAGS) $($2_CPPFLAGS) $$< -o $$@ 2>/dev/null
+
+# auto-dependencies for c++ files
+$($1_objdir)/%.d: src/$1/native/%.cc
+       @install -d $$(@D)
+       @rm -f $$@
+       @$($(TARGET)_CXX) -MM -MT "$$(@:.d=.o) $$@" -Isrc/$1/jni -Ibin/include/$1 \
+               $($(TARGET)_CPPFLAGS) $($2_CPPFLAGS) $$< -o $$@ 2>/dev/null
+
+$(if $(filter clean dist gen,$(MAKECMDGOALS)),,-include $$($2_OBJS:.o=.d))
+endef
+
+#$(foreach module,$(all_MODULES),$(foreach library,$($(module)_NATIVE_LIBRARIES),$(info $(call native_library,$(module),$(library)))))
+$(foreach module,$(all_MODULES),$(foreach library,$($(module)_NATIVE_LIBRARIES),$(eval $(call native_library,$(module),$(library)))))
+
+# ######################################################################
+
+dist:
+       @install -d bin
+       tar cfz bin/$(dist_NAME)-$(dist_VERSION).tar.gz \
+        --transform=s,^,$(dist_NAME)-$(dist_VERSION)/, \
+        config.make.in java.make Makefile src          \
+        $(dist_EXTRA)
diff --git a/maven.make b/maven.make
new file mode 100644 (file)
index 0000000..c9e82e2
--- /dev/null
@@ -0,0 +1,74 @@
+#
+# Copyright (C) 2021 Michael Zucchi
+#
+# This is the copyright for maven.make
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+#
+
+# This lets one download maven packages using simple automake syntax.
+
+# maven_<name>_URL = baseurl
+
+# Define the base url.  maven_central_URL is already defined as
+# maven_central_URL:=https://repo1.maven.org/maven2
+
+# maven_<name>_JARS = group:artifact:version group:artifact:version ...
+
+# Define the artifacts required from the given maven repository.
+
+# That's it!
+
+# It defines several make targets.
+
+# make maven-init
+#  Will download the jar files.
+
+# make maven-verify
+#  Will download and check the signatures using gpg.  The public key
+#  required for verification must be imported to gpg separately.
+
+# make distclean
+#  Will delete .lib
+
+# define maven central
+maven_central_URL:=https://repo1.maven.org/maven2
+maven_repository_URL:=https://mvnrepository.com/artifact
+
+# find out what repositories the makefile defined
+maven_REPOS=$(patsubst maven_%_URL,%,$(filter maven_%_URL,$(.VARIABLES)))
+
+# (group artifact version baseurl)
+define maven_func=
+.lib/$2-$3.jar:
+       mkdir -p .lib
+       wget -O $$@ $(4)/$(subst .,/,$1)/$2/$3/$2-$3.jar || ( rm $$@ ; exit 1 )
+.lib/$2-$3.jar.asc: .lib/$2-$3.jar
+       wget -O $$@ $(4)/$(subst .,/,$1)/$2/$3/$2-$3.jar.asc
+       gpg --batch --verify $$@ $$< || ( rm $$@ ; echo "GPG verification failed, you may need to import the public key." ; exit 1 )
+maven-init: .lib/$2-$3.jar
+maven-verify: .lib/$2-$3.jar.asc
+endef
+
+maven-init:
+maven-verify:
+
+.PHONY: maven-init maven-verify
+
+$(foreach repo,$(maven_REPOS),\
+       $(foreach jar,$(maven_$(repo)_JARS), \
+               $(eval $(call maven_func,$(word 1,$(subst :, ,$(jar))),$(word 2,$(subst :, ,$(jar))),$(word 3,$(subst :, ,$(jar))),$(maven_$(repo)_URL)))))
+
+distclean:
+       rm -rf .lib
diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml
new file mode 100644 (file)
index 0000000..d277f11
--- /dev/null
@@ -0,0 +1,1884 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+*** GENERATED FROM project.xml - DO NOT EDIT  ***
+***         EDIT ../build.xml INSTEAD         ***
+
+For the purpose of easier reading the script
+is divided into following sections:
+
+  - initialization
+  - compilation
+  - jar
+  - execution
+  - debugging
+  - javadoc
+  - test compilation
+  - test execution
+  - test debugging
+  - applet
+  - cleanup
+
+        -->
+<project xmlns:if="ant:if" xmlns:unless="ant:unless" basedir=".." default="default" name="vulkanz-impl">
+    <fail message="Please build using Ant 1.9.7 or higher.">
+        <condition>
+            <not>
+                <antversion atleast="1.9.7"/>
+            </not>
+        </condition>
+    </fail>
+    <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
+    <!-- 
+                ======================
+                INITIALIZATION SECTION 
+                ======================
+            -->
+    <target name="-pre-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="-pre-init" name="-init-private">
+        <property file="nbproject/private/config.properties"/>
+        <property file="nbproject/private/configs/${config}.properties"/>
+        <property file="nbproject/private/private.properties"/>
+    </target>
+    <target depends="-pre-init,-init-private" name="-init-user">
+        <property file="${user.properties.file}"/>
+        <!-- The two properties below are usually overridden -->
+        <!-- by the active platform. Just a fallback. -->
+        <property name="default.javac.source" value="9"/>
+        <property name="default.javac.target" value="9"/>
+    </target>
+    <target depends="-pre-init,-init-private,-init-user" name="-init-pre-project">
+        <property file="nbproject/configs/${config}.properties"/>
+        <property file="nbproject/project.properties"/>
+        <property name="netbeans.modular.tasks.version" value="1"/>
+        <property location="${build.dir}/tasks/${netbeans.modular.tasks.version}" name="netbeans.modular.tasks.dir"/>
+    </target>
+    <target depends="-init-pre-project" name="-check-netbeans-tasks">
+        <condition property="netbeans.tasks.compiled">
+            <available file="${netbeans.modular.tasks.dir}/out/netbeans/ModuleInfoSelector.class"/>
+        </condition>
+    </target>
+    <target depends="-init-pre-project,-check-netbeans-tasks" name="-init-compile-netbeans-tasks" unless="netbeans.tasks.compiled">
+        <echo file="${netbeans.modular.tasks.dir}/src/netbeans/CoalesceKeyvalue.java">
+
+package netbeans;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+public class CoalesceKeyvalue extends Task {
+    private String property;
+
+    public void setProperty(String property) {
+        this.property = property;
+    }
+
+    private String value;
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    private String valueSep;
+
+    public void setValueSep(String valueSep) {
+        this.valueSep = valueSep;
+    }
+
+    private String entrySep;
+
+    public void setEntrySep(String entrySep) {
+        this.entrySep = entrySep;
+    }
+
+    private String multiSep;
+
+    public void setMultiSep(String multiSep) {
+        this.multiSep = multiSep;
+    }
+
+    private String outSep;
+
+    public void setOutSep(String outSep) {
+        this.outSep = outSep;
+    }
+
+    @Override
+    public void execute() throws BuildException {
+        List&lt;String&gt; result = new ArrayList&lt;&gt;();
+        Map&lt;String, List&lt;String&gt;&gt; module2Paths = new HashMap&lt;&gt;();
+
+        for (String entry : value.split(Pattern.quote(entrySep))) {
+            String[] keyValue = entry.split(Pattern.quote(valueSep), 2);
+            if (keyValue.length == 1) {
+                result.add(keyValue[0]);
+            } else {
+                module2Paths.computeIfAbsent(keyValue[0], s -&gt; new ArrayList&lt;&gt;())
+                            .add(keyValue[1].trim());
+            }
+        }
+        module2Paths.entrySet()
+                    .stream()
+                    .forEach(e -&gt; result.add(e.getKey() + valueSep + e.getValue().stream().collect(Collectors.joining(multiSep))));
+        getProject().setProperty(property, result.stream().collect(Collectors.joining(" " + entrySep)));
+    }
+
+}
+
+                </echo>
+        <echo file="${netbeans.modular.tasks.dir}/src/netbeans/ModsourceRegexp.java">
+            
+package netbeans;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+public class ModsourceRegexp extends Task {
+    private String property;
+
+    public void setProperty(String property) {
+        this.property = property;
+    }
+
+    private String filePattern;
+
+    public void setFilePattern(String filePattern) {
+        this.filePattern = filePattern;
+    }
+
+    private String modsource;
+
+    public void setModsource(String modsource) {
+        this.modsource = modsource;
+    }
+
+    private List&lt;String&gt; expandGroup(String grp) {
+        List&lt;String&gt; exp = new ArrayList&lt;&gt;();
+        String item = "";
+        int depth = 0;
+
+        for (int i = 0; i &lt; grp.length(); i++) {
+            char c = grp.charAt(i);
+            switch (c) {
+                case '{':
+                    if (depth++ == 0) {
+                        continue;
+                    }
+                    break;
+                case '}':
+                    if (--depth == 0) {
+                        exp.add(item);
+                        continue;
+                    }
+                    break;
+                case ',':
+                    if (depth == 1) {
+                        exp.add(item);
+                        item = "";
+                        continue;
+                    }
+                default:
+                    break;
+            }
+            item = item + c;
+        }
+        return exp;
+    }
+
+    private List&lt;String&gt; pathVariants(String spec) {
+        return pathVariants(spec, new ArrayList&lt;&gt;());
+    }
+
+    private List&lt;String&gt; pathVariants(String spec, List&lt;String&gt; res) {
+        int start  = spec.indexOf('{');
+        if (start == -1) {
+            res.add(spec);
+            return res;
+        }
+        int depth = 1;
+        int end;
+        for (end = start + 1; end &lt; spec.length() &amp;&amp; depth &gt; 0; end++) {
+            char c = spec.charAt(end);
+            switch (c) {
+                case '{': depth++; break;
+                case '}': depth--; break;
+            }
+        }
+        String prefix = spec.substring(0, start);
+        String suffix = spec.substring(end);
+        expandGroup(spec.substring(start, end)).stream().forEach(item -&gt; {
+            pathVariants(prefix + item + suffix, res);
+        });
+        return res;
+    }
+
+    private String toRegexp2(String spec, String filepattern, String separator) {
+        List&lt;String&gt; prefixes = new ArrayList&lt;&gt;();
+        List&lt;String&gt; suffixes = new ArrayList&lt;&gt;();
+        pathVariants(spec).forEach(item -&gt; {
+            suffixes.add(item);
+        });
+        String tail = "";
+        String separatorString = separator;
+        if ("\\".equals(separatorString)) {
+            separatorString = "\\\\";
+        }
+        if (filepattern != null &amp;&amp; !Objects.equals(filepattern, tail)) {
+            tail = separatorString + filepattern;
+        }
+        return "([^" + separatorString +"]+)\\Q" + separator + "\\E(" + suffixes.stream().collect(Collectors.joining("|")) + ")" + tail;
+    }
+
+    @Override
+    public void execute() throws BuildException {
+        getProject().setProperty(property, toRegexp2(modsource, filePattern, getProject().getProperty("file.separator")));
+    }
+
+}
+
+                </echo>
+        <echo file="${netbeans.modular.tasks.dir}/src/netbeans/ModuleInfoSelector.java">
+            
+package netbeans;
+
+import java.io.File;
+import java.util.Arrays;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.selectors.BaseExtendSelector;
+
+public class ModuleInfoSelector extends BaseExtendSelector {
+
+    @Override
+    public boolean isSelected(File basedir, String filename, File file) throws BuildException {
+        String extension = Arrays.stream(getParameters())
+                                 .filter(p -&gt; "extension".equals(p.getName()))
+                                 .map(p -&gt; p.getValue())
+                                 .findAny()
+                                 .get();
+        return !new File(file, "module-info." + extension).exists();
+    }
+
+}
+
+                </echo>
+        <mkdir dir="${netbeans.modular.tasks.dir}/out"/>
+        <javac classpath="${ant.core.lib}" destdir="${netbeans.modular.tasks.dir}/out" srcdir="${netbeans.modular.tasks.dir}/src"/>
+    </target>
+    <target depends="-init-pre-project,-init-compile-netbeans-tasks" name="-init-project">
+        <taskdef classname="netbeans.CoalesceKeyvalue" classpath="${netbeans.modular.tasks.dir}/out" name="coalesce_keyvalue" uri="http://www.netbeans.org/ns/j2se-modular-project/1"/>
+        <taskdef classname="netbeans.ModsourceRegexp" classpath="${netbeans.modular.tasks.dir}/out" name="modsource_regexp" uri="http://www.netbeans.org/ns/j2se-modular-project/1"/>
+    </target>
+    <target name="-init-source-module-properties">
+        <property name="javac.modulepath" value=""/>
+        <property name="run.modulepath" value="${javac.modulepath}:${build.modules.dir}"/>
+        <property name="debug.modulepath" value="${run.modulepath}"/>
+        <property name="javac.upgrademodulepath" value=""/>
+        <property name="run.upgrademodulepath" value="${javac.upgrademodulepath}"/>
+        <condition else="" property="javac.systemmodulepath.cmd.line.arg" value="-system '${javac.systemmodulepath}'">
+            <and>
+                <isset property="javac.systemmodulepath"/>
+                <length length="0" string="${javac.systemmodulepath}" when="greater"/>
+            </and>
+        </condition>
+        <property name="dist.jlink.dir" value="${dist.dir}/jlink"/>
+        <property name="dist.jlink.output" value="${dist.jlink.dir}/${application.title}"/>
+    </target>
+    <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
+        <property name="platform.java" value="${java.home}/bin/java"/>
+        <j2semodularproject1:modsource_regexp xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" modsource="${test.src.dir.path}" property="have.tests.test.src.dir.regexp"/>
+        <dirset dir="${basedir}/${test.src.dir}" id="have.tests.test.src.dir.set" includes="*/*">
+            <filename regex="${have.tests.test.src.dir.regexp}"/>
+        </dirset>
+        <union id="have.tests.set">
+            <dirset refid="have.tests.test.src.dir.set"/>
+        </union>
+        <condition property="have.tests">
+            <or>
+                <resourcecount count="0" when="greater">
+                    <union refid="have.tests.set"/>
+                </resourcecount>
+            </or>
+        </condition>
+        <j2semodularproject1:modsource_regexp xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" modsource="${test.src.dir.path}" property="have.tests.test.src.dir.regexp"/>
+        <dirset dir="${basedir}/${test.src.dir}" id="have.tests.test.src.dir.patchset" includes="*/*">
+            <filename regex="${have.tests.test.src.dir.regexp}"/>
+        </dirset>
+        <union id="have.tests.patchset">
+            <dirset refid="have.tests.test.src.dir.patchset"/>
+        </union>
+        <j2semodularproject1:modsource_regexp xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" modsource="${src.dir.path}" property="have.sources.src.dir.regexp"/>
+        <dirset dir="${basedir}/${src.dir}" id="have.sources.src.dir.set" includes="*/*">
+            <filename regex="${have.sources.src.dir.regexp}"/>
+        </dirset>
+        <j2semodularproject1:modsource_regexp xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" modsource="${src.gen.dir.path}" property="have.sources.src.gen.dir.regexp"/>
+        <dirset dir="${basedir}/${src.gen.dir}" id="have.sources.src.gen.dir.set" includes="*/*">
+            <filename regex="${have.sources.src.gen.dir.regexp}"/>
+        </dirset>
+        <union id="have.sources.set">
+            <dirset refid="have.sources.src.dir.set"/>
+            <dirset refid="have.sources.src.gen.dir.set"/>
+        </union>
+        <condition property="have.sources">
+            <or>
+                <resourcecount count="0" when="greater">
+                    <union refid="have.sources.set"/>
+                </resourcecount>
+                <resourcecount count="0" when="greater">
+                    <union refid="have.sources.set"/>
+                </resourcecount>
+            </or>
+        </condition>
+        <condition property="main.class.available">
+            <and>
+                <isset property="main.class"/>
+                <not>
+                    <equals arg1="${main.class}" arg2="" trim="true"/>
+                </not>
+            </and>
+        </condition>
+        <condition property="netbeans.home+have.tests">
+            <and>
+                <isset property="netbeans.home"/>
+                <isset property="have.tests"/>
+            </and>
+        </condition>
+        <condition property="no.javadoc.preview">
+            <and>
+                <isset property="javadoc.preview"/>
+                <isfalse value="${javadoc.preview}"/>
+            </and>
+        </condition>
+        <condition property="do.archive">
+            <or>
+                <not>
+                    <istrue value="${jar.archive.disabled}"/>
+                </not>
+                <istrue value="${not.archive.disabled}"/>
+            </or>
+        </condition>
+        <property name="run.jvmargs" value=""/>
+        <property name="run.jvmargs.ide" value=""/>
+        <property name="javac.compilerargs" value=""/>
+        <property name="work.dir" value="${basedir}"/>
+        <condition property="no.deps">
+            <and>
+                <istrue value="${no.dependencies}"/>
+            </and>
+        </condition>
+        <property name="javac.debug" value="true"/>
+        <property name="javadoc.preview" value="true"/>
+        <property name="application.args" value=""/>
+        <property name="source.encoding" value="${file.encoding}"/>
+        <property name="runtime.encoding" value="${source.encoding}"/>
+        <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
+            <and>
+                <isset property="javadoc.encoding"/>
+                <not>
+                    <equals arg1="${javadoc.encoding}" arg2=""/>
+                </not>
+            </and>
+        </condition>
+        <property name="javadoc.encoding.used" value="${source.encoding}"/>
+        <property name="includes" value="**"/>
+        <property name="excludes" value=""/>
+        <property name="do.depend" value="false"/>
+        <condition property="do.depend.true">
+            <istrue value="${do.depend}"/>
+        </condition>
+        <path id="endorsed.classpath.path" path="${endorsed.classpath}"/>
+        <condition else="" property="endorsed.classpath.cmd.line.arg" value="-Xbootclasspath/p:'${toString:endorsed.classpath.path}'">
+            <and>
+                <isset property="endorsed.classpath"/>
+                <not>
+                    <equals arg1="${endorsed.classpath}" arg2="" trim="true"/>
+                </not>
+            </and>
+        </condition>
+        <condition else="" property="javac.profile.cmd.line.arg" value="-profile ${javac.profile}">
+            <isset property="profile.available"/>
+        </condition>
+        <condition else="false" property="jdkBug6558476">
+            <and>
+                <matches pattern="1\.[56]" string="${java.specification.version}"/>
+                <not>
+                    <os family="unix"/>
+                </not>
+            </and>
+        </condition>
+        <condition else="false" property="javac.fork">
+            <or>
+                <istrue value="${jdkBug6558476}"/>
+                <istrue value="${javac.external.vm}"/>
+            </or>
+        </condition>
+        <condition property="main.class.check.available">
+            <and>
+                <isset property="libs.CopyLibs.classpath"/>
+                <available classname="org.netbeans.modules.java.j2seproject.moduletask.ModuleMainClass" classpath="${libs.CopyLibs.classpath}"/>
+            </and>
+        </condition>
+        <property name="jar.index" value="false"/>
+        <property name="jar.index.metainf" value="${jar.index}"/>
+        <condition property="junit.available">
+            <or>
+                <available classname="org.junit.Test" classpath="${run.test.classpath}"/>
+                <available classname="junit.framework.Test" classpath="${run.test.classpath}"/>
+            </or>
+        </condition>
+        <condition property="testng.available">
+            <available classname="org.testng.annotations.Test" classpath="${run.test.classpath}"/>
+        </condition>
+        <condition property="junit+testng.available">
+            <and>
+                <istrue value="${junit.available}"/>
+                <istrue value="${testng.available}"/>
+            </and>
+        </condition>
+        <condition else="testng" property="testng.mode" value="mixed">
+            <istrue value="${junit+testng.available}"/>
+        </condition>
+        <condition else="" property="testng.debug.mode" value="-mixed">
+            <istrue value="${junit+testng.available}"/>
+        </condition>
+        <property name="java.failonerror" value="true"/>
+        <macrodef name="for-paths" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute name="paths"/>
+            <attribute default="${path.separator}" name="separator"/>
+            <element implicit="yes" name="call"/>
+            <sequential>
+                <local name="entry"/>
+                <local name="tail"/>
+                <local name="moreElements"/>
+                <loadresource property="entry" quiet="true" unless:blank="@{paths}">
+                    <concat>@{paths}</concat>
+                    <filterchain>
+                        <replaceregex pattern="([^@{separator}]*)\Q@{separator}\E.*" replace="\1"/>
+                    </filterchain>
+                </loadresource>
+                <sequential if:set="entry">
+                    <call/>
+                </sequential>
+                <condition else="false" property="moreElements" value="true">
+                    <contains string="@{paths}" substring="@{separator}"/>
+                </condition>
+                <loadresource if:true="${moreElements}" property="tail" quiet="true">
+                    <concat>@{paths}</concat>
+                    <filterchain>
+                        <replaceregex pattern="[^@{separator}]*\Q@{separator}\E(.*)" replace="\1"/>
+                    </filterchain>
+                </loadresource>
+                <j2semodularproject1:for-paths xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" if:true="${moreElements}" paths="${tail}">
+                    <call/>
+                </j2semodularproject1:for-paths>
+            </sequential>
+        </macrodef>
+        <property name="modules.supported.internal" value="true"/>
+        <condition else="${file.separator}" property="file.separator.string" value="\${file.separator}">
+            <equals arg1="${file.separator}" arg2="\"/>
+        </condition>
+    </target>
+    <target name="-post-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
+        <fail unless="src.dir">Must set src.dir</fail>
+        <fail unless="src.gen.dir">Must set src.gen.dir</fail>
+        <fail unless="test.src.dir">Must set test.src.dir</fail>
+        <fail unless="build.dir">Must set build.dir</fail>
+        <fail unless="dist.dir">Must set dist.dir</fail>
+        <fail unless="build.modules.dir">Must set build.modules.dir</fail>
+        <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
+        <fail unless="build.test.modules.dir">Must set build.test.modules.dir</fail>
+        <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
+        <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
+        <fail message="Java 9 support requires Ant 1.10.0 or higher.">
+            <condition>
+                <not>
+                    <antversion atleast="1.10.0"/>
+                </not>
+            </condition>
+        </fail>
+    </target>
+    <target name="-init-macrodef-property">
+        <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute name="name"/>
+            <attribute name="value"/>
+            <sequential>
+                <property name="@{name}" value="${@{value}}"/>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-ap-cmdline-properties,-init-source-module-properties" name="-init-macrodef-javac">
+        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${build.modules.dir}" name="destdir"/>
+            <attribute default="${javac.classpath}" name="classpath"/>
+            <attribute default="${javac.modulepath}" name="modulepath"/>
+            <attribute default="${src.dir}/*/${src.dir.path}:${src.gen.dir}/*/${src.gen.dir.path}" name="modulesourcepath"/>
+            <attribute default="${javac.upgrademodulepath}" name="upgrademodulepath"/>
+            <attribute default="${javac.processorpath}" name="processorpath"/>
+            <attribute default="${javac.processormodulepath}" name="processormodulepath"/>
+            <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/>
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="${javac.debug}" name="debug"/>
+            <attribute default="${empty.dir}" name="gensrcdir"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property location="${build.dir}/empty" name="empty.dir"/>
+                <mkdir dir="${empty.dir}"/>
+                <mkdir dir="@{apgeneratedsrcdir}"/>
+                <condition property="processormodulepath.set">
+                    <resourcecount count="0" when="greater">
+                        <path>
+                            <pathelement path="@{processormodulepath}"/>
+                        </path>
+                    </resourcecount>
+                </condition>
+                <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" target="${javac.target}" tempdir="${java.io.tmpdir}">
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <modulepath>
+                        <path path="@{modulepath}"/>
+                    </modulepath>
+                    <modulesourcepath>
+                        <path path="@{modulesourcepath}"/>
+                    </modulesourcepath>
+                    <upgrademodulepath>
+                        <path path="@{upgrademodulepath}"/>
+                    </upgrademodulepath>
+                    <compilerarg line="${javac.systemmodulepath.cmd.line.arg}"/>
+                    <compilerarg line="${javac.profile.cmd.line.arg}"/>
+                    <compilerarg line="${javac.compilerargs}"/>
+                    <compilerarg if:set="processormodulepath.set" value="--processor-module-path"/>
+                    <compilerarg if:set="processormodulepath.set" path="@{processormodulepath}"/>
+                    <compilerarg unless:set="processormodulepath.set" value="-processorpath"/>
+                    <compilerarg path="@{processorpath}:${empty.dir}" unless:set="processormodulepath.set"/>
+                    <compilerarg line="${ap.processors.internal}"/>
+                    <compilerarg line="${annotation.processing.processor.options}"/>
+                    <compilerarg value="-s"/>
+                    <compilerarg path="@{apgeneratedsrcdir}"/>
+                    <compilerarg line="${ap.proc.none.internal}"/>
+                    <customize/>
+                </javac>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-javac" name="-init-macrodef-javac-depend">
+        <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${src.dir}:${src.gen.dir}" name="srcdir"/>
+            <attribute default="${build.classes.dir}" name="destdir"/>
+            <attribute default="${javac.classpath}" name="classpath"/>
+            <sequential>
+                <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                </depend>
+            </sequential>
+        </macrodef>
+        <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${build.modules.dir}" name="destdir"/>
+            <sequential>
+                <fail unless="javac.includes">Must set javac.includes</fail>
+                <pathconvert pathsep="${line.separator}" property="javac.includes.binary">
+                    <path>
+                        <filelist dir="@{destdir}" files="${javac.includes}"/>
+                    </path>
+                    <globmapper from="*.java" to="*.class"/>
+                </pathconvert>
+                <tempfile deleteonexit="true" property="javac.includesfile.binary"/>
+                <echo file="${javac.includesfile.binary}" message="${javac.includes.binary}"/>
+                <delete>
+                    <files includesfile="${javac.includesfile.binary}"/>
+                </delete>
+                <delete>
+                    <fileset file="${javac.includesfile.binary}"/>
+                </delete>
+            </sequential>
+        </macrodef>
+    </target>
+    <target if="${junit.available}" name="-init-macrodef-junit-init">
+        <condition else="false" property="nb.junit.batch" value="true">
+            <and>
+                <istrue value="${junit.available}"/>
+                <not>
+                    <isset property="test.method"/>
+                </not>
+            </and>
+        </condition>
+        <condition else="false" property="nb.junit.single" value="true">
+            <and>
+                <istrue value="${junit.available}"/>
+                <isset property="test.method"/>
+            </and>
+        </condition>
+    </target>
+    <target name="-init-test-properties">
+        <property name="test.binaryincludes" value="&lt;nothing&gt;"/>
+        <property name="test.binarytestincludes" value=""/>
+        <property name="test.binaryexcludes" value=""/>
+    </target>
+    <target name="-init-macrodef-junit-prototype">
+        <macrodef name="junit-prototype" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <element name="customizePrototype" optional="true"/>
+            <sequential>
+                <property location="${build.dir}/empty" name="empty.dir"/>
+                <property name="junit.forkmode" value="perTest"/>
+                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
+                    <syspropertyset>
+                        <propertyref prefix="test-sys-prop."/>
+                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <classpath>
+                        <path path="${run.test.classpath}"/>
+                    </classpath>
+                    <formatter type="brief" usefile="false"/>
+                    <formatter type="xml"/>
+                    <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+                    <jvmarg value="-ea"/>
+                    <jvmarg value="--module-path"/>
+                    <jvmarg path="${run.modulepath}${path.separator}${run.test.modulepath}${path.separator}${empty.dir}"/>
+                    <jvmarg line="${run.test.jvmargs}"/>
+                    <customizePrototype/>
+                </junit>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-test-properties,-init-macrodef-junit-prototype" if="${nb.junit.single}" name="-init-macrodef-junit-single" unless="${nb.junit.batch}">
+        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <j2semodularproject1:junit-prototype xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1">
+                    <customizePrototype>
+                        <test methods="@{testmethods}" name="@{testincludes}" todir="${build.test.results.dir}"/>
+                        <customize/>
+                    </customizePrototype>
+                </j2semodularproject1:junit-prototype>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-test-properties,-init-macrodef-junit-prototype" if="${nb.junit.batch}" name="-init-macrodef-junit-batch" unless="${nb.junit.single}">
+        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <j2semodularproject1:junit-prototype xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1">
+                    <customizePrototype>
+                        <batchtest todir="${build.test.results.dir}">
+                            <mappedresources>
+                                <union>
+                                    <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="**/@{includes}">
+                                        <filename name="**/@{testincludes}"/>
+                                        <filename regex="${have.tests.test.src.dir.regexp}"/>
+                                    </fileset>
+                                </union>
+                                <regexpmapper from="${have.tests.test.src.dir.regexp}\Q${file.separator}\E(.*)$" to="\3"/>
+                            </mappedresources>
+                            <fileset dir="${build.test.modules.dir}" excludes="@{excludes},${excludes},${test.binaryexcludes}" includes="${test.binaryincludes}">
+                                <filename name="${test.binarytestincludes}"/>
+                            </fileset>
+                        </batchtest>
+                        <customize/>
+                    </customizePrototype>
+                </j2semodularproject1:junit-prototype>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-junit-init,-init-macrodef-junit-single, -init-macrodef-junit-batch" if="${junit.available}" name="-init-macrodef-junit"/>
+    <target if="${testng.available}" name="-init-macrodef-testng">
+        <macrodef name="testng" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <condition else="" property="testng.methods.arg" value="@{testincludes}.@{testmethods}">
+                    <isset property="test.method"/>
+                </condition>
+                <union id="test.set">
+                    <fileset dir="${test.src.dir}" excludes="@{excludes},**/*.xml,${excludes}" includes="@{includes}">
+                        <filename name="@{testincludes}"/>
+                    </fileset>
+                </union>
+                <taskdef classname="org.testng.TestNGAntTask" classpath="${run.test.classpath}" name="testng"/>
+                <testng classfilesetref="test.set" failureProperty="tests.failed" listeners="org.testng.reporters.VerboseReporter" methods="${testng.methods.arg}" mode="${testng.mode}" outputdir="${build.test.results.dir}" suitename="vulkanz" testname="TestNG tests" workingDir="${work.dir}">
+                    <xmlfileset dir="${build.test.classes.dir}" includes="@{testincludes}"/>
+                    <propertyset>
+                        <propertyref prefix="test-sys-prop."/>
+                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                    </propertyset>
+                    <classpath>
+                        <path path="${run.test.classpath}"/>
+                    </classpath>
+                    <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+                    <customize/>
+                </testng>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-test-impl">
+        <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element implicit="true" name="customize" optional="true"/>
+            <sequential>
+                <echo>No tests executed.</echo>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-junit" if="${junit.available}" name="-init-macrodef-junit-impl">
+        <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element implicit="true" name="customize" optional="true"/>
+            <sequential>
+                <j2semodularproject1:junit xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+                    <customize/>
+                </j2semodularproject1:junit>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-testng" if="${testng.available}" name="-init-macrodef-testng-impl">
+        <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element implicit="true" name="customize" optional="true"/>
+            <sequential>
+                <j2semodularproject1:testng xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+                    <customize/>
+                </j2semodularproject1:testng>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-test-impl,-init-macrodef-junit-impl,-init-macrodef-testng-impl" name="-init-macrodef-test">
+        <macrodef name="test" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <sequential>
+                <j2semodularproject1:test-impl xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+                    <customize>
+                        <jvmarg line="${run.jvmargs}"/>
+                        <jvmarg line="${run.jvmargs.ide}"/>
+                    </customize>
+                </j2semodularproject1:test-impl>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-junit" if="${junit.available}" name="-init-macrodef-junit-debug-impl">
+        <macrodef name="test-debug-impl" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element name="customizeDebuggee" optional="true"/>
+            <sequential>
+                <j2semodularproject1:junit xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+                    <customize>
+                        <jvmarg value="-agentlib:jdwp=transport=${debug-transport},address=${jpda.address}"/>
+                        <customizeDebuggee/>
+                    </customize>
+                </j2semodularproject1:junit>
+            </sequential>
+        </macrodef>
+    </target>
+    <target if="${testng.available}" name="-init-macrodef-testng-debug">
+        <macrodef name="testng-debug" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${main.class}" name="testClass"/>
+            <attribute default="" name="testMethod"/>
+            <element name="customize2" optional="true"/>
+            <sequential>
+                <condition else="-testclass @{testClass}" property="test.class.or.method" value="-methods @{testClass}.@{testMethod}">
+                    <isset property="test.method"/>
+                </condition>
+                <condition else="-suitename vulkanz -testname @{testClass} ${test.class.or.method}" property="testng.cmd.args" value="@{testClass}">
+                    <matches pattern=".*\.xml" string="@{testClass}"/>
+                </condition>
+                <delete dir="${build.test.results.dir}" quiet="true"/>
+                <mkdir dir="${build.test.results.dir}"/>
+                <j2semodularproject1:debug xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" classname="org.testng.TestNG" classpath="${debug.test.classpath}">
+                    <customizeDebuggee>
+                        <customize2/>
+                        <jvmarg value="-ea"/>
+                        <arg line="${testng.debug.mode}"/>
+                        <arg line="-d ${build.test.results.dir}"/>
+                        <arg line="-listener org.testng.reporters.VerboseReporter"/>
+                        <arg line="${testng.cmd.args}"/>
+                    </customizeDebuggee>
+                </j2semodularproject1:debug>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-testng-debug" if="${testng.available}" name="-init-macrodef-testng-debug-impl">
+        <macrodef name="testng-debug-impl" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${main.class}" name="testClass"/>
+            <attribute default="" name="testMethod"/>
+            <element implicit="true" name="customize2" optional="true"/>
+            <sequential>
+                <j2semodularproject1:testng-debug xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" testClass="@{testClass}" testMethod="@{testMethod}">
+                    <customize2/>
+                </j2semodularproject1:testng-debug>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-junit-debug-impl" if="${junit.available}" name="-init-macrodef-test-debug-junit">
+        <macrodef name="test-debug" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <attribute default="${main.class}" name="testClass"/>
+            <attribute default="" name="testMethod"/>
+            <sequential>
+                <j2semodularproject1:test-debug-impl xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+                    <customizeDebuggee>
+                        <jvmarg line="${run.jvmargs}"/>
+                        <jvmarg line="${run.jvmargs.ide}"/>
+                    </customizeDebuggee>
+                </j2semodularproject1:test-debug-impl>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-testng-debug-impl" if="${testng.available}" name="-init-macrodef-test-debug-testng">
+        <macrodef name="test-debug" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <attribute default="${main.class}" name="testClass"/>
+            <attribute default="" name="testMethod"/>
+            <sequential>
+                <j2semodularproject1:testng-debug-impl xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" testClass="@{testClass}" testMethod="@{testMethod}">
+                    <customize2>
+                        <syspropertyset>
+                            <propertyref prefix="test-sys-prop."/>
+                            <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                        </syspropertyset>
+                    </customize2>
+                </j2semodularproject1:testng-debug-impl>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-test-debug-junit,-init-macrodef-test-debug-testng" name="-init-macrodef-test-debug"/>
+    <!--
+                pre NB7.2 profiling section; consider it deprecated
+            -->
+    <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile, -profile-init-check" if="profiler.info.jvmargs.agent" name="profile-init"/>
+    <target if="profiler.info.jvmargs.agent" name="-profile-pre-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target if="profiler.info.jvmargs.agent" name="-profile-post-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target if="profiler.info.jvmargs.agent" name="-profile-init-macrodef-profile">
+        <macrodef name="resolve">
+            <attribute name="name"/>
+            <attribute name="value"/>
+            <sequential>
+                <property name="@{name}" value="${env.@{value}}"/>
+            </sequential>
+        </macrodef>
+        <macrodef name="profile">
+            <attribute default="${main.class}" name="classname"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property environment="env"/>
+                <resolve name="profiler.current.path" value="${profiler.info.pathvar}"/>
+                <java classname="@{classname}" dir="${profiler.info.dir}" failonerror="${java.failonerror}" fork="true" jvm="${profiler.info.jvm}">
+                    <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+                    <jvmarg value="${profiler.info.jvmargs.agent}"/>
+                    <jvmarg line="${profiler.info.jvmargs}"/>
+                    <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
+                    <arg line="${application.args}"/>
+                    <classpath>
+                        <path path="${run.classpath}"/>
+                    </classpath>
+                    <syspropertyset>
+                        <propertyref prefix="run-sys-prop."/>
+                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <customize/>
+                </java>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile" if="profiler.info.jvmargs.agent" name="-profile-init-check">
+        <fail unless="profiler.info.jvm">Must set JVM to use for profiling in profiler.info.jvm</fail>
+        <fail unless="profiler.info.jvmargs.agent">Must set profiler agent JVM arguments in profiler.info.jvmargs.agent</fail>
+    </target>
+    <!--
+                end of pre NB7.2 profiling section
+            -->
+    <target depends="-init-debug-args" name="-init-macrodef-nbjpda">
+        <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${main.class}" name="name"/>
+            <attribute default="${debug.modulepath}" name="modulepath"/>
+            <attribute default="${debug.classpath}" name="classpath"/>
+            <attribute default="" name="stopclassname"/>
+            <sequential>
+                <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
+                    <modulepath>
+                        <path path="@{modulepath}"/>
+                    </modulepath>
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                </nbjpdastart>
+            </sequential>
+        </macrodef>
+        <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${debug.modules.dir}" name="dir"/>
+            <sequential>
+                <nbjpdareload>
+                    <fileset dir="@{dir}" includes="${fix.classes}">
+                        <include name="*/${fix.includes}*.class"/>
+                    </fileset>
+                </nbjpdareload>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-debug-args">
+        <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
+            <os family="windows"/>
+        </condition>
+        <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
+            <isset property="debug.transport"/>
+        </condition>
+    </target>
+    <target depends="-init-debug-args" name="-init-macrodef-debug">
+        <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${module.name}" name="modulename"/>
+            <attribute default="${main.class}" name="classname"/>
+            <attribute default="${debug.modulepath}" name="modulepath"/>
+            <attribute default="${debug.classpath}" name="classpath"/>
+            <element name="customizeDebuggee" optional="true"/>
+            <sequential>
+                <j2semodularproject1:java xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" classname="@{classname}" classpath="@{classpath}" modulename="@{modulename}" modulepath="@{modulepath}">
+                    <customize>
+                        <jvmarg value="-agentlib:jdwp=transport=${debug-transport},address=${jpda.address}"/>
+                        <customizeDebuggee/>
+                    </customize>
+                </j2semodularproject1:java>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-source-module-properties" name="-init-macrodef-java">
+        <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${module.name}" name="modulename"/>
+            <attribute default="${main.class}" name="classname"/>
+            <attribute default="${run.modulepath}" name="modulepath"/>
+            <attribute default="${run.upgrademodulepath}" name="upgrademodulepath"/>
+            <attribute default="${run.classpath}" name="classpath"/>
+            <attribute default="jvm" name="jvm"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <java classname="@{classname}" dir="${work.dir}" failonerror="${java.failonerror}" fork="true" module="@{modulename}">
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <modulepath>
+                        <path path="@{modulepath}"/>
+                    </modulepath>
+                    <upgrademodulepath>
+                        <path path="@{upgrademodulepath}"/>
+                    </upgrademodulepath>
+                    <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
+                    <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
+                    <jvmarg line="${run.jvmargs}"/>
+                    <jvmarg line="${run.jvmargs.ide}"/>
+                    <syspropertyset>
+                        <propertyref prefix="run-sys-prop."/>
+                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <customize/>
+                </java>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-presetdef-jar">
+        <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <jar compress="${jar.compress}" index="${jar.index}" jarfile="${dist.jar}" manifestencoding="UTF-8">
+                <j2semodularproject1:fileset xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" dir="${build.classes.dir}" excludes="${dist.archive.excludes}"/>
+            </jar>
+        </presetdef>
+    </target>
+    <target name="-init-ap-cmdline-properties">
+        <property name="annotation.processing.enabled" value="true"/>
+        <property name="annotation.processing.processors.list" value=""/>
+        <property name="annotation.processing.processor.options" value=""/>
+        <property name="annotation.processing.run.all.processors" value="true"/>
+        <property name="javac.processorpath" value="${javac.classpath}"/>
+        <property name="javac.test.processorpath" value="${javac.test.classpath}"/>
+    </target>
+    <target depends="-init-ap-cmdline-properties" name="-init-ap-cmdline-supported">
+        <condition else="" property="ap.processors.internal" value="-processor ${annotation.processing.processors.list}">
+            <isfalse value="${annotation.processing.run.all.processors}"/>
+        </condition>
+        <condition else="" property="ap.proc.none.internal" value="-proc:none">
+            <isfalse value="${annotation.processing.enabled}"/>
+        </condition>
+    </target>
+    <target depends="-init-ap-cmdline-properties,-init-ap-cmdline-supported" name="-init-ap-cmdline">
+        <property name="ap.cmd.line.internal" value=""/>
+    </target>
+    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac-depend,-init-macrodef-test,-init-macrodef-test-debug,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar,-init-ap-cmdline" name="init"/>
+    <!--
+                ===================
+                COMPILATION SECTION
+                ===================
+            -->
+    <target name="-deps-jar-init" unless="built-jar.properties">
+        <property location="${build.dir}/built-jar.properties" name="built-jar.properties"/>
+        <delete file="${built-jar.properties}" quiet="true"/>
+    </target>
+    <target if="already.built.jar.${basedir}" name="-warn-already-built-jar">
+        <echo level="warn" message="Cycle detected: vulkanz was already built"/>
+    </target>
+    <target depends="init,-deps-jar-init" name="deps-jar" unless="no.deps">
+        <mkdir dir="${build.dir}"/>
+        <touch file="${built-jar.properties}" verbose="false"/>
+        <property file="${built-jar.properties}" prefix="already.built.jar."/>
+        <antcall target="-warn-already-built-jar"/>
+        <propertyfile file="${built-jar.properties}">
+            <entry key="${basedir}" value=""/>
+        </propertyfile>
+        <antcall target="-maybe-call-dep">
+            <param name="call.built.properties" value="${built-jar.properties}"/>
+            <param location="${project.notzed_nativez}" name="call.subproject"/>
+            <param location="${project.notzed_nativez}/build.xml" name="call.script"/>
+            <param name="call.target" value="jar"/>
+            <param name="transfer.built-jar.properties" value="${built-jar.properties}"/>
+            <param name="transfer.not.archive.disabled" value="true"/>
+            <param name="transfer.do.jlink" value="false"/>
+        </antcall>
+    </target>
+    <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
+    <target depends="init" name="-check-automatic-build">
+        <available file="${build.modules.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
+    </target>
+    <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
+        <antcall target="clean">
+            <param name="no.dependencies" value="true"/>
+        </antcall>
+    </target>
+    <target name="-pre-pre-compile">
+        <mkdir dir="${build.modules.dir}"/>
+    </target>
+    <target name="-pre-compile">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target if="do.depend.true" name="-compile-depend">
+        <pathconvert property="build.generated.subdirs">
+            <dirset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+                <include name="*"/>
+            </dirset>
+        </pathconvert>
+        <j2semodularproject1:depend xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" srcdir="${src.dir}:${src.gen.dir}:${build.generated.subdirs}"/>
+    </target>
+    <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
+        <j2semodularproject1:javac xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" gensrcdir="${build.generated.sources.dir}"/>
+        <j2semodularproject1:modsource_regexp xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" filePattern="(.*$)" modsource="${src.dir.path}" property="src.dir.path.regexp"/>
+        <echo message="Copying resources from ${src.dir}"/>
+        <copy todir="${build.modules.dir}">
+            <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+            <regexpmapper from="${src.dir.path.regexp}" to="\1/\3"/>
+        </copy>
+        <j2semodularproject1:modsource_regexp xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" filePattern="(.*$)" modsource="${src.gen.dir.path}" property="src.gen.dir.path.regexp"/>
+        <echo message="Copying resources from ${src.gen.dir}"/>
+        <copy todir="${build.modules.dir}">
+            <fileset dir="${src.gen.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+            <regexpmapper from="${src.gen.dir.path.regexp}" to="\1/\3"/>
+        </copy>
+    </target>
+    <target if="has.persistence.xml" name="-copy-persistence-xml">
+        <fail message="XXX: Not supported on MM projects"/>
+        <mkdir dir="${build.classes.dir}/META-INF"/>
+        <copy todir="${build.classes.dir}/META-INF">
+            <fileset dir="${meta.inf.dir}" includes="persistence.xml orm.xml"/>
+        </copy>
+    </target>
+    <target name="-post-compile">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
+    <target name="-pre-compile-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-jar" name="-do-compile-single">
+        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+        <j2semodularproject1:force-recompile xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1"/>
+        <j2semodularproject1:javac xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}, module-info.java"/>
+    </target>
+    <target name="-post-compile-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
+    <!--
+                ====================
+                JAR BUILDING SECTION
+                ====================
+            -->
+    <target depends="init,compile" name="-check-module-main-class">
+        <condition property="do.module.main.class">
+            <and>
+                <available file="${module.dir}/module-info.class"/>
+                <isset property="main.class.check.available"/>
+            </and>
+        </condition>
+    </target>
+    <target depends="init" name="-pre-pre-jar">
+        <dirname file="${dist.jar}" property="dist.jar.dir"/>
+        <mkdir dir="${dist.jar.dir}"/>
+    </target>
+    <target name="-pre-jar">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="-pre-single-jar"/>
+    <target depends="-pre-single-jar" if="module.jar.filename" name="-make-single-jar">
+        <jar basedir="${module.dir}" compress="${jar.compress}" destfile="${dist.dir}/${module.jar.filename}" excludes="${dist.archive.excludes}" manifestencoding="UTF-8"/>
+    </target>
+    <target depends="init,compile,-pre-pre-jar,-pre-jar,-main-module-check-condition" if="do.archive" name="-do-jar-jar" unless="do.mkdist">
+        <property location="${build.modules.dir}" name="build.modules.dir.resolved"/>
+        <dirset dir="${build.modules.dir.resolved}" id="do.jar.dirs" includes="*"/>
+        <pathconvert property="do.jar.dir.list" refid="do.jar.dirs">
+            <identitymapper/>
+        </pathconvert>
+        <j2semodularproject1:for-paths xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" paths="${do.jar.dir.list}">
+            <local name="module.jar.filename"/>
+            <local name="module.jar.name.tmp"/>
+            <basename file="${entry}" property="module.jar.name.tmp"/>
+            <property name="module.jar.filename" value="${module.jar.name.tmp}.jar"/>
+            <antcall inheritRefs="true" target="-make-single-jar">
+                <param name="module.jar.filename" value="${module.jar.filename}"/>
+                <param location="${entry}" name="module.dir"/>
+            </antcall>
+        </j2semodularproject1:for-paths>
+        <condition property="named.module.internal">
+            <and>
+                <isset property="module.name"/>
+                <length length="0" string="${module.name}" when="greater"/>
+            </and>
+        </condition>
+        <condition property="unnamed.module.internal">
+            <not>
+                <isset property="named.module.internal"/>
+            </not>
+        </condition>
+        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
+        <property location="${dist.jar}" name="dist.jar.resolved"/>
+        <pathconvert property="run.classpath.with.dist.jar">
+            <path path="${run.classpath}"/>
+            <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
+        </pathconvert>
+        <pathconvert property="run.modulepath.with.dist.jar">
+            <path path="${run.modulepath}"/>
+            <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
+        </pathconvert>
+        <condition else="" property="jar.usage.message.module.path" value=" --module-path ${run.modulepath.with.dist.jar}">
+            <and>
+                <isset property="modules.supported.internal"/>
+                <length length="0" string="${run.modulepath.with.dist.jar}" when="greater"/>
+            </and>
+        </condition>
+        <condition else="" property="jar.usage.message.class.path" value=" -cp ${run.classpath.with.dist.jar}">
+            <length length="0" string="${run.classpath.with.dist.jar}" when="greater"/>
+        </condition>
+        <condition else=" ${main.class}" property="jar.usage.message.main.class" value=" -m ${module.name}/${main.class}">
+            <isset property="named.module.internal"/>
+        </condition>
+        <condition else="" property="jar.usage.message" value="To run this application from the command line without Ant, try:${line.separator}${platform.java}${jar.usage.message.module.path}${jar.usage.message.class.path}${jar.usage.message.main.class}">
+            <isset property="main.class.available"/>
+        </condition>
+        <condition else="debug" property="jar.usage.level" value="info">
+            <isset property="main.class.available"/>
+        </condition>
+        <echo level="${jar.usage.level}" message="${jar.usage.message}"/>
+    </target>
+    <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-jar" name="-do-jar-without-libraries"/>
+    <target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-with-libraries"/>
+    <target name="-post-jar">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,compile,-pre-jar,-do-jar-without-libraries,-do-jar-with-libraries,-post-jar" name="-do-jar"/>
+    <target depends="init,compile,-pre-jar,-do-jar,-post-jar,deploy" description="Build JAR." name="jar"/>
+    <!--
+                =================
+                DEPLOY SECTION
+                =================
+            -->
+    <target name="-pre-deploy">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init" name="-check-jlink">
+        <condition property="do.jlink.internal">
+            <and>
+                <istrue value="${do.jlink}"/>
+                <isset property="do.archive"/>
+            </and>
+        </condition>
+    </target>
+    <target depends="init,-do-jar,-post-jar,-pre-deploy,-check-jlink,-main-module-set" if="do.jlink.internal" name="-do-deploy">
+        <delete dir="${dist.jlink.dir}" failonerror="false" quiet="true"/>
+        <property name="jlink.launcher.name" value="${application.title}"/>
+        <pathconvert pathsep="," property="jlink.modulelist.internal">
+            <fileset dir="${dist.dir}" includes="*.jar"/>
+            <mapper>
+                <chainedmapper>
+                    <flattenmapper/>
+                    <globmapper from="*.jar" to="*"/>
+                </chainedmapper>
+            </mapper>
+        </pathconvert>
+        <condition else="${jlink.modulelist.internal}" property="jlink.add.modules" value="${jlink.modulelist.internal},${jlink.additionalmodules}">
+            <and>
+                <isset property="jlink.additionalmodules"/>
+                <length length="0" string="${jlink.additionalmodules}" when="greater"/>
+            </and>
+        </condition>
+        <condition property="jlink.do.strip.internal">
+            <and>
+                <isset property="jlink.strip"/>
+                <istrue value="${jlink.strip}"/>
+            </and>
+        </condition>
+        <condition property="jlink.do.additionalparam.internal">
+            <and>
+                <isset property="jlink.additionalparam"/>
+                <length length="0" string="${jlink.additionalparam}" when="greater"/>
+            </and>
+        </condition>
+        <condition property="jlink.do.launcher.internal">
+            <and>
+                <istrue value="${jlink.launcher}"/>
+                <isset property="module.name"/>
+                <length length="0" string="${module.name}" when="greater"/>
+                <isset property="main.class.available"/>
+            </and>
+        </condition>
+        <property name="platform.jlink" value="${jdk.home}/bin/jlink"/>
+        <property name="jlink.systemmodules.internal" value="${jdk.home}/jmods"/>
+        <exec executable="${platform.jlink}">
+            <arg value="--module-path"/>
+            <arg path="${jlink.systemmodules.internal}:${run.modulepath}:${dist.dir}"/>
+            <arg value="--add-modules"/>
+            <arg value="${jlink.add.modules}"/>
+            <arg if:set="jlink.do.strip.internal" value="--strip-debug"/>
+            <arg if:set="jlink.do.launcher.internal" value="--launcher"/>
+            <arg if:set="jlink.do.launcher.internal" value="${jlink.launcher.name}=${module.name}/${main.class}"/>
+            <arg if:set="jlink.do.additionalparam.internal" line="${jlink.additionalparam}"/>
+            <arg value="--output"/>
+            <arg value="${dist.jlink.output}"/>
+        </exec>
+    </target>
+    <target name="-post-deploy">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="-do-jar,-post-jar,-pre-deploy,-do-deploy,-post-deploy" name="deploy"/>
+    <!--
+                =================
+                EXECUTION SECTION
+                =================
+            -->
+    <target name="-check-main-class">
+        <fail unless="main.class">No main class specified</fail>
+    </target>
+    <target depends="init,compile,-check-main-class,-main-module-check" description="Run a main class." name="run">
+        <property name="main.class.relativepath" refid="main.class.relativepath"/>
+        <pathconvert pathsep="," property="src.dir.list" refid="have.sources.set"/>
+        <j2semodularproject1:modsource_regexp xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" filePattern="(.*$)" modsource="${src.dir.path}" property="run.src.dir.path.regexp"/>
+        <j2semodularproject1:java xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <customize>
+                <arg line="${application.args}"/>
+            </customize>
+        </j2semodularproject1:java>
+    </target>
+    <target name="-main-module-set" unless="module.name">
+        <condition else="${main.class}" property="check.class.name" value="${run.class}">
+            <isset property="run.class"/>
+        </condition>
+        <condition property="run.modules.dir" value="${build.modules.dir}">
+            <not>
+                <isset property="run.modules.dir"/>
+            </not>
+        </condition>
+        <resources id="main.class.relativepath">
+            <mappedresources>
+                <string value="${check.class.name}"/>
+                <unpackagemapper from="*" to="*.class"/>
+            </mappedresources>
+        </resources>
+        <property location="${run.modules.dir}" name="run.modules.dir.location"/>
+        <pathconvert property="module.name">
+            <fileset dir="${run.modules.dir}" includes="**/${toString:main.class.relativepath}"/>
+            <regexpmapper from="\Q${run.modules.dir.location}${file.separator}\E([^${file.separator.string}]+)\Q${file.separator}\E.*\.class" to="\1"/>
+        </pathconvert>
+    </target>
+    <target depends="-main-module-set" name="-main-module-check">
+        <fail message="Could not determine module of the main class and module.name is not set">
+            <condition>
+                <or>
+                    <not>
+                        <isset property="module.name"/>
+                    </not>
+                    <length length="0" string="${module.name}" when="equal"/>
+                </or>
+            </condition>
+        </fail>
+    </target>
+    <target depends="-main-module-set" if="main.class.available" name="-main-module-check-condition">
+        <fail message="Could not determine module of the main class and module.name is not set">
+            <condition>
+                <or>
+                    <not>
+                        <isset property="module.name"/>
+                    </not>
+                    <length length="0" string="${module.name}" when="equal"/>
+                </or>
+            </condition>
+        </fail>
+    </target>
+    <target name="-do-not-recompile">
+        <property name="javac.includes.binary" value=""/>
+    </target>
+    <target depends="init,compile-single,-main-module-check" name="run-single">
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+        <j2semodularproject1:java xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" classname="${run.class}"/>
+    </target>
+    <target depends="init,compile-test-single,-init-test-run-module-properties,-main-module-check" name="run-test-with-main">
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+        <j2semodularproject1:java xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" classname="${run.class}" classpath="${run.test.classpath}" modulepath="${run.test.modulepath}">
+            <customize>
+                <jvmarg line="${run.test.jvmargs}"/>
+            </customize>
+        </j2semodularproject1:java>
+    </target>
+    <!--
+                =================
+                DEBUGGING SECTION
+                =================
+            -->
+    <target name="-debug-init">
+        <condition else="${main.class}" property="run.class" value="${debug.class}">
+            <isset property="debug.class"/>
+        </condition>
+        <fail message="debug.class or main.class property is not set" unless="run.class"/>
+    </target>
+    <target depends="init,-debug-init,-main-module-check" if="netbeans.home" name="-debug-start-debugger">
+        <j2semodularproject1:nbjpdastart xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" name="${debug.class}"/>
+    </target>
+    <target depends="init,-debug-init,-main-module-check" if="netbeans.home" name="-debug-start-debugger-main-test">
+        <j2semodularproject1:nbjpdastart xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" classpath="${debug.test.classpath}" name="${debug.class}"/>
+    </target>
+    <target depends="init,compile,-debug-init,-main-module-check" name="-debug-start-debuggee">
+        <j2semodularproject1:debug xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" classname="${run.class}">
+            <customizeDebuggee>
+                <arg line="${application.args}"/>
+            </customizeDebuggee>
+        </j2semodularproject1:debug>
+    </target>
+    <target depends="init,compile,-debug-init,-main-module-check,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
+    <target depends="init,-debug-init,-main-module-check" if="netbeans.home" name="-debug-start-debugger-stepinto">
+        <j2semodularproject1:nbjpdastart xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" stopclassname="${debug.class}"/>
+    </target>
+    <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
+    <target depends="init,compile-single,-debug-init,-main-module-check" if="netbeans.home" name="-debug-start-debuggee-single">
+        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
+        <j2semodularproject1:debug xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" classname="${debug.class}"/>
+    </target>
+    <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
+    <target depends="init,compile-test-single,-debug-init,-main-module-check" if="netbeans.home" name="-debug-start-debuggee-main-test">
+        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
+        <j2semodularproject1:debug xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" classname="${debug.class}" classpath="${debug.test.classpath}"/>
+    </target>
+    <target depends="init,compile-test-single,-debug-start-debugger-main-test,-debug-start-debuggee-main-test" if="netbeans.home" name="debug-test-with-main"/>
+    <target depends="init" name="-pre-debug-fix">
+        <fail unless="fix.includes">Must set fix.includes</fail>
+        <property name="javac.includes" value="${fix.includes}.java"/>
+    </target>
+    <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
+        <property location="${build.modules.dir}" name="debug.modules.dir"/>
+        <j2semodularproject1:nbjpdareload xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1"/>
+    </target>
+    <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
+    <!--
+                =================
+                PROFILING SECTION
+                =================
+            -->
+    <!--
+                pre NB7.2 profiler integration
+            -->
+    <target depends="profile-init,compile" description="Profile a project in the IDE." if="profiler.info.jvmargs.agent" name="-profile-pre72">
+        <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
+        <nbprofiledirect>
+            <classpath>
+                <path path="${run.classpath}"/>
+            </classpath>
+        </nbprofiledirect>
+        <profile/>
+    </target>
+    <target depends="profile-init,compile-single" description="Profile a selected class in the IDE." if="profiler.info.jvmargs.agent" name="-profile-single-pre72">
+        <fail unless="profile.class">Must select one file in the IDE or set profile.class</fail>
+        <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
+        <nbprofiledirect>
+            <classpath>
+                <path path="${run.classpath}"/>
+            </classpath>
+        </nbprofiledirect>
+        <profile classname="${profile.class}"/>
+    </target>
+    <target depends="profile-init,compile-single" if="profiler.info.jvmargs.agent" name="-profile-applet-pre72">
+        <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
+        <nbprofiledirect>
+            <classpath>
+                <path path="${run.classpath}"/>
+            </classpath>
+        </nbprofiledirect>
+        <profile classname="sun.applet.AppletViewer">
+            <customize>
+                <arg value="${applet.url}"/>
+            </customize>
+        </profile>
+    </target>
+    <target depends="-init-macrodef-junit,profile-init,compile-test-single" if="profiler.info.jvmargs.agent" name="-profile-test-single-pre72">
+        <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
+        <nbprofiledirect>
+            <classpath>
+                <path path="${run.test.classpath}"/>
+            </classpath>
+        </nbprofiledirect>
+        <j2semodularproject1:junit xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" excludes="${excludes}" includes="${includes}" testincludes="${profile.class}" testmethods="">
+            <customize>
+                <jvmarg value="-agentlib:jdwp=transport=${debug-transport},address=${jpda.address}"/>
+                <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
+                <jvmarg value="${profiler.info.jvmargs.agent}"/>
+                <jvmarg line="${profiler.info.jvmargs}"/>
+                <classpath>
+                    <path path="${run.test.classpath}"/>
+                </classpath>
+            </customize>
+        </j2semodularproject1:junit>
+    </target>
+    <!--
+                end of pre NB72 profiling section
+            -->
+    <target if="netbeans.home" name="-profile-check">
+        <condition property="profiler.configured">
+            <or>
+                <contains casesensitive="true" string="${run.jvmargs.ide}" substring="-agentpath:"/>
+                <contains casesensitive="true" string="${run.jvmargs.ide}" substring="-javaagent:"/>
+            </or>
+        </condition>
+    </target>
+    <target depends="-profile-check,-profile-pre72" description="Profile a project in the IDE." if="profiler.configured" name="profile" unless="profiler.info.jvmargs.agent">
+        <startprofiler/>
+        <antcall target="run"/>
+    </target>
+    <target depends="-profile-check,-profile-single-pre72" description="Profile a selected class in the IDE." if="profiler.configured" name="profile-single" unless="profiler.info.jvmargs.agent">
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+        <startprofiler/>
+        <antcall target="run-single"/>
+    </target>
+    <target depends="-profile-test-single-pre72" description="Profile a selected test in the IDE." name="profile-test-single"/>
+    <target depends="-profile-check" description="Profile a selected test in the IDE." if="profiler.configured" name="profile-test" unless="profiler.info.jvmargs">
+        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
+        <startprofiler/>
+        <antcall target="test-single"/>
+    </target>
+    <target depends="-profile-check" description="Profile a selected class in the IDE." if="profiler.configured" name="profile-test-with-main">
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+        <startprofiler/>
+        <antcall target="run-test-with-main"/>
+    </target>
+    <target depends="-profile-check,-profile-applet-pre72" if="profiler.configured" name="profile-applet" unless="profiler.info.jvmargs.agent">
+        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+        <startprofiler/>
+        <antcall target="run-applet"/>
+    </target>
+    <!--
+                ===============
+                JAVADOC SECTION
+                ===============
+            -->
+    <target depends="init" if="have.sources" name="-javadoc-build">
+        <mkdir dir="${dist.javadoc.dir}"/>
+        <condition else="" property="javadoc.endorsed.classpath.cmd.line.arg" value="-J${endorsed.classpath.cmd.line.arg}">
+            <and>
+                <isset property="endorsed.classpath.cmd.line.arg"/>
+                <not>
+                    <equals arg1="${endorsed.classpath.cmd.line.arg}" arg2=""/>
+                </not>
+            </and>
+        </condition>
+        <condition else="" property="bug5101868workaround" value="*.java">
+            <matches pattern="1\.[56](\..*)?" string="${java.version}"/>
+        </condition>
+        <condition else="" property="javadoc.html5.cmd.line.arg" value="-html5">
+            <and>
+                <isset property="javadoc.html5"/>
+                <available file="${jdk.home}${file.separator}lib${file.separator}jrt-fs.jar"/>
+            </and>
+        </condition>
+        <javadoc additionalparam="-J-Dfile.encoding=${file.encoding} ${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
+            <classpath>
+                <path path="${javac.classpath}"/>
+            </classpath>
+            <fileset dir="${src.dir}" excludes="${bug5101868workaround},${excludes}" includes="${includes}">
+                <filename name="**/*.java"/>
+            </fileset>
+            <fileset dir="${src.gen.dir}" excludes="${bug5101868workaround},${excludes}" includes="${includes}">
+                <filename name="**/*.java"/>
+            </fileset>
+            <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+                <include name="**/*.java"/>
+                <exclude name="*.java"/>
+            </fileset>
+            <arg line="${javadoc.endorsed.classpath.cmd.line.arg}"/>
+            <arg line="${javadoc.html5.cmd.line.arg}"/>
+        </javadoc>
+        <copy todir="${dist.javadoc.dir}">
+            <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
+                <filename name="**/doc-files/**"/>
+            </fileset>
+            <fileset dir="${src.gen.dir}" excludes="${excludes}" includes="${includes}">
+                <filename name="**/doc-files/**"/>
+            </fileset>
+            <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+                <include name="**/doc-files/**"/>
+            </fileset>
+        </copy>
+    </target>
+    <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
+        <nbbrowse file="${dist.javadoc.dir}/index.html"/>
+    </target>
+    <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
+    <!--
+                =========================
+                TEST COMPILATION SECTION
+                =========================
+            -->
+    <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
+        <mkdir dir="${build.test.modules.dir}"/>
+    </target>
+    <target name="-pre-compile-test">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="-init-source-module-properties" name="-init-test-run-module-properties">
+        <fileset dir="${build.test.modules.dir}" id="run.test.packages.internal" includes="**/*.class"/>
+        <property location="${build.test.modules.dir}" name="build.test.modules.dir.abs.internal"/>
+        <pathconvert pathsep=" " property="run.test.addexports.internal" refid="run.test.packages.internal">
+            <chainedmapper>
+                <filtermapper>
+                    <replacestring from="${build.test.modules.dir.abs.internal}${file.separator}" to=""/>
+                </filtermapper>
+                <regexpmapper from="^([^${file.separator.string}]*)\Q${file.separator}\E(.*)\Q${file.separator}\E.*\.class$$" to="\1${path.separator}\2"/>
+                <filtermapper>
+                    <uniqfilter/>
+                    <replacestring from="${file.separator}" to="."/>
+                </filtermapper>
+                <regexpmapper from="([^${file.separator.string}]+)${path.separator}(.*)" to="--add-exports \1/\2=ALL-UNNAMED"/>
+            </chainedmapper>
+        </pathconvert>
+        <property location="${build.test.modules.dir}" name="build.test.modules.location"/>
+        <pathconvert pathsep="," property="run.test.addmodules.list">
+            <map from="${build.test.modules.location}${file.separator}" to=""/>
+            <dirset dir="${build.test.modules.dir}" includes="*"/>
+            <chainedmapper>
+                <filtermapper>
+                    <uniqfilter/>
+                </filtermapper>
+            </chainedmapper>
+        </pathconvert>
+        <pathconvert pathsep=" " property="run.test.patchmodules.list">
+            <dirset dir="${build.test.modules.dir}" includes="*">
+                <custom classname="netbeans.ModuleInfoSelector" classpath="${netbeans.modular.tasks.dir}/out">
+                    <param name="extension" value="class"/>
+                </custom>
+            </dirset>
+            <chainedmapper>
+                <filtermapper>
+                    <uniqfilter/>
+                </filtermapper>
+                <regexpmapper from=".*\Q${file.separator}\E([^${file.separator.string}]+)$" to="--patch-module \1=\0"/>
+            </chainedmapper>
+        </pathconvert>
+        <j2semodularproject1:coalesce_keyvalue xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" entrySep="--patch-module " multiSep="${path.separator}" property="run.test.patchmodules" value="${run.test.patchmodules.list}" valueSep="="/>
+        <condition else="" property="run.test.addmodules.internal" value="--add-modules ${run.test.addmodules.list}">
+            <isset property="run.test.addmodules.list"/>
+        </condition>
+        <pathconvert pathsep=" " property="run.test.addreads.internal">
+            <map from="${build.test.modules.location}" to=""/>
+            <dirset dir="${build.test.modules.dir}" includes="*"/>
+            <chainedmapper>
+                <regexpmapper from="^\Q${build.test.modules.location}${file.separator}\E(.*)" to="\1"/>
+                <regexpmapper from="(.*)" to="--add-reads \1=ALL-UNNAMED"/>
+                <filtermapper>
+                    <uniqfilter/>
+                </filtermapper>
+            </chainedmapper>
+        </pathconvert>
+        <property name="run.test.jvmargs" value="${run.test.addmodules.internal} ${run.test.addreads.internal} ${run.test.addexports.internal} ${run.test.patchmodules}"/>
+    </target>
+    <target depends="-init-source-module-properties" name="-init-test-javac-module-properties">
+        <pathconvert pathsep=" " property="compile.test.patchmodule.internal" refid="have.tests.patchset">
+            <regexpmapper from="(.*\Q${file.separator}\E)([^${file.separator.string}]+)\Q${file.separator}\E(.*)$$" to="--patch-module \2=\1\2${file.separator.string}\3"/>
+        </pathconvert>
+        <pathconvert pathsep=" " property="compile.test.addreads">
+            <union refid="have.tests.set"/>
+            <chainedmapper>
+                <firstmatchmapper>
+                    <regexpmapper from="${have.tests.test.src.dir.regexp}" to="\1"/>
+                </firstmatchmapper>
+                <regexpmapper from="(.*)" to="--add-reads \1=ALL-UNNAMED"/>
+                <filtermapper>
+                    <uniqfilter/>
+                </filtermapper>
+            </chainedmapper>
+        </pathconvert>
+        <j2semodularproject1:coalesce_keyvalue xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" entrySep="--patch-module " multiSep="${path.separator}" property="compile.test.patchmodules" value="${compile.test.patchmodule.internal}" valueSep="="/>
+        <property name="javac.test.moduleargs" value="${compile.test.patchmodules} ${compile.test.addreads}"/>
+    </target>
+    <target depends="-init-test-javac-module-properties" name="-init-test-module-properties">
+        <property location="${build.modules.dir}" name="test.module.build.location"/>
+        <property name="test.source.modulepath" value="${test.src.dir}/*/${test.src.dir.path}"/>
+        <property name="test.compile.modulepath" value="${javac.test.modulepath}:${build.modules.dir}"/>
+        <macrodef name="test-javac" uri="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <element implicit="true" name="additionalargs" optional="true"/>
+            <sequential>
+                <j2semodularproject1:javac xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" apgeneratedsrcdir="${build.test.modules.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.modules.dir}" excludes="@{excludes}" includes="@{includes}" modulepath="${test.compile.modulepath}" modulesourcepath="${test.source.modulepath}" processorpath="${javac.test.processorpath}">
+                    <customize>
+                        <compilerarg line="${javac.test.moduleargs}"/>
+                        <additionalargs/>
+                    </customize>
+                </j2semodularproject1:javac>
+            </sequential>
+        </macrodef>
+    </target>
+    <target if="do.depend.true" name="-compile-test-depend">
+        <j2semodularproject1:depend xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
+    </target>
+    <target depends="init,deps-jar,compile,-init-test-module-properties,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
+        <j2semodularproject1:test-javac xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1"/>
+        <j2semodularproject1:modsource_regexp xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" filePattern="(.*$)" modsource="${test.src.dir.path}" property="test.src.dir.path.regexp"/>
+        <echo message="Copying resources from ${test.src.dir}"/>
+        <copy todir="${build.test.modules.dir}">
+            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+            <regexpmapper from="${test.src.dir.path.regexp}" to="\1/\3"/>
+        </copy>
+    </target>
+    <target name="-post-compile-test">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
+    <target name="-pre-compile-test-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-jar,compile,-init-test-module-properties,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
+        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+        <j2semodularproject1:force-recompile xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" destdir="${build.test.modules.dir}"/>
+        <j2semodularproject1:test-javac xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" includes="${javac.includes}"/>
+        <j2semodularproject1:modsource_regexp xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" filePattern="(.*$)" modsource="${test.src.dir.path}" property="test.src.dir.path.regexp"/>
+        <echo message="Copying resources from ${test.src.dir}"/>
+        <copy todir="${build.test.modules.dir}">
+            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+            <regexpmapper from="${test.src.dir.path.regexp}" to="\1/\3"/>
+        </copy>
+    </target>
+    <target name="-post-compile-test-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
+    <!--
+                =======================
+                TEST EXECUTION SECTION
+                =======================
+            -->
+    <target depends="init" if="have.tests" name="-pre-test-run">
+        <mkdir dir="${build.test.results.dir}"/>
+    </target>
+    <target name="-init-test-run">
+        <property name="run.modules.dir" value="${build.test.modules.dir}"/>
+    </target>
+    <target depends="init,compile-test,-init-test-run-module-properties,-pre-test-run" if="have.tests" name="-do-test-run">
+        <j2semodularproject1:test xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" includes="${includes}" testincludes="**/*Test.java"/>
+    </target>
+    <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
+        <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
+    </target>
+    <target depends="init" if="have.tests" name="test-report"/>
+    <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
+    <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
+    <target depends="init" if="have.tests" name="-pre-test-run-single">
+        <mkdir dir="${build.test.results.dir}"/>
+    </target>
+    <target depends="init,compile-test-single,-init-test-run-module-properties,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
+        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
+        <j2semodularproject1:test xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" excludes="" includes="${test.includes}" testincludes="${test.includes}"/>
+    </target>
+    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
+        <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
+    </target>
+    <target depends="init,compile-test-single,-init-test-run-module-properties,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
+    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single-method">
+        <fail unless="test.class">Must select some files in the IDE or set test.class</fail>
+        <fail unless="test.method">Must select some method in the IDE or set test.method</fail>
+        <j2semodularproject1:test xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" excludes="" includes="${javac.includes}" testincludes="${test.class}" testmethods="${test.method}"/>
+    </target>
+    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single-method" if="have.tests" name="-post-test-run-single-method">
+        <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
+    </target>
+    <target depends="init,compile-test-single,-init-test-run-module-properties,-pre-test-run-single,-do-test-run-single-method,-post-test-run-single-method" description="Run single unit test." name="test-single-method"/>
+    <!--
+                =======================
+                TEST DEBUGGING SECTION
+                =======================
+            -->
+    <target depends="init,compile-test-single,-init-test-run-module-properties,-pre-test-run-single" if="have.tests" name="-debug-start-debuggee-test">
+        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
+        <j2semodularproject1:test-debug xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" excludes="" includes="${javac.includes}" testClass="${test.class}" testincludes="${javac.includes}"/>
+    </target>
+    <target depends="init,compile-test-single,-init-test-run-module-properties,-pre-test-run-single" if="have.tests" name="-debug-start-debuggee-test-method">
+        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
+        <fail unless="test.method">Must select some method in the IDE or set test.method</fail>
+        <j2semodularproject1:test-debug xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" excludes="" includes="${javac.includes}" testClass="${test.class}" testMethod="${test.method}" testincludes="${test.class}" testmethods="${test.method}"/>
+    </target>
+    <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
+        <j2semodularproject1:nbjpdastart xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1" classpath="${debug.test.classpath}" name="${test.class}"/>
+    </target>
+    <target depends="init,compile-test-single,-init-test-run-module-properties,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
+    <target depends="init,compile-test-single,-init-test-run-module-properties,-debug-start-debugger-test,-debug-start-debuggee-test-method" name="debug-test-method"/>
+    <target depends="debug-test-method" name="debug-single-method"/>
+    <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
+        <property name="debug.modules.dir" value="${build.test.modules.dir}"/>
+        <j2semodularproject1:nbjpdareload xmlns:j2semodularproject1="http://www.netbeans.org/ns/j2se-modular-project/1"/>
+    </target>
+    <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
+    <!--
+                =========================
+                APPLET EXECUTION SECTION
+                =========================
+            -->
+    <target name="run-applet">
+        <fail message="Applets are no longer supported by JDK 9"/>
+    </target>
+    <!--
+                =========================
+                APPLET DEBUGGING  SECTION
+                =========================
+            -->
+    <target name="-debug-start-debuggee-applet">
+        <fail message="Applets are no longer supported by JDK 9"/>
+    </target>
+    <target name="debug-applet">
+        <fail message="Applets are no longer supported by JDK 9"/>
+    </target>
+    <!--
+                ===============
+                CLEANUP SECTION
+                ===============
+            -->
+    <target name="-deps-clean-init" unless="built-clean.properties">
+        <property location="${build.dir}/built-clean.properties" name="built-clean.properties"/>
+        <delete file="${built-clean.properties}" quiet="true"/>
+    </target>
+    <target if="already.built.clean.${basedir}" name="-warn-already-built-clean">
+        <echo level="warn" message="Cycle detected: vulkanz was already built"/>
+    </target>
+    <target depends="init,-deps-clean-init" name="deps-clean" unless="no.deps">
+        <mkdir dir="${build.dir}"/>
+        <touch file="${built-clean.properties}" verbose="false"/>
+        <property file="${built-clean.properties}" prefix="already.built.clean."/>
+        <antcall target="-warn-already-built-clean"/>
+        <propertyfile file="${built-clean.properties}">
+            <entry key="${basedir}" value=""/>
+        </propertyfile>
+        <antcall target="-maybe-call-dep">
+            <param name="call.built.properties" value="${built-clean.properties}"/>
+            <param location="${project.notzed_nativez}" name="call.subproject"/>
+            <param location="${project.notzed_nativez}/build.xml" name="call.script"/>
+            <param name="call.target" value="clean"/>
+            <param name="transfer.built-clean.properties" value="${built-clean.properties}"/>
+            <param name="transfer.not.archive.disabled" value="true"/>
+            <param name="transfer.do.jlink" value="false"/>
+        </antcall>
+    </target>
+    <target depends="init" name="-do-clean">
+        <delete dir="${build.dir}"/>
+        <delete dir="${dist.jlink.output}"/>
+        <delete dir="${dist.dir}" followsymlinks="false" includeemptydirs="true"/>
+    </target>
+    <target name="-post-clean">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target name="-recompile-netbeans-tasks-after-clean">
+        <antcall inheritall="false" target="-init-compile-netbeans-tasks"/>
+    </target>
+    <target depends="init,deps-clean,-do-clean,-recompile-netbeans-tasks-after-clean,-post-clean" description="Clean build products." name="clean"/>
+    <target name="-check-call-dep">
+        <property file="${call.built.properties}" prefix="already.built."/>
+        <condition property="should.call.dep">
+            <and>
+                <not>
+                    <isset property="already.built.${call.subproject}"/>
+                </not>
+                <available file="${call.script}"/>
+            </and>
+        </condition>
+    </target>
+    <target depends="-check-call-dep" if="should.call.dep" name="-maybe-call-dep">
+        <ant antfile="${call.script}" inheritall="false" target="${call.target}">
+            <propertyset>
+                <propertyref prefix="transfer."/>
+                <mapper from="transfer.*" to="*" type="glob"/>
+            </propertyset>
+        </ant>
+    </target>
+</project>
diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties
new file mode 100644 (file)
index 0000000..fe2aca6
--- /dev/null
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=9e6776ee
+build.xml.script.CRC32=84612a8f
+build.xml.stylesheet.CRC32=32069288@1.17
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=9e6776ee
+nbproject/build-impl.xml.script.CRC32=4a65f9d9
+nbproject/build-impl.xml.stylesheet.CRC32=d1ebcf0f@1.17
diff --git a/nbproject/project.properties b/nbproject/project.properties
new file mode 100644 (file)
index 0000000..484a1fa
--- /dev/null
@@ -0,0 +1,101 @@
+annotation.processing.enabled=true
+annotation.processing.enabled.in.editor=false
+annotation.processing.processors.list=
+annotation.processing.run.all.processors=true
+annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
+application.title=vulkanz
+application.vendor=notzed
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+build.generated.sources.dir=${build.dir}/generated-sources
+build.modules.dir=${build.dir}/modules
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.modules.dir=${build.dir}/test/modules
+build.test.results.dir=${build.dir}/test/results
+# Uncomment to specify the preferred debugger connection transport:
+#debug.transport=dt_socket
+debug.classpath=\
+    ${run.classpath}
+debug.modulepath=\
+    ${run.modulepath}
+debug.test.classpath=\
+    ${run.test.classpath}
+debug.test.modulepath=\
+    ${run.test.modulepath}
+# Files in build.classes.dir which should be excluded from distribution jar
+dist.archive.excludes=
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.javadoc.dir=${dist.dir}/javadoc
+dist.jlink.dir=${dist.dir}/jlink
+dist.jlink.output=${dist.jlink.dir}/vulkanz
+endorsed.classpath=
+excludes=
+includes=**
+jar.compress=false
+javac.classpath=
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.external.vm=false
+javac.modulepath=\
+    ${reference.notzed_nativez.notzed_nativez_jar}
+javac.processormodulepath=
+javac.processorpath=\
+    ${javac.classpath}
+javac.source=18
+javac.target=18
+javac.test.classpath=\
+    ${javac.classpath}
+javac.test.modulepath=\
+    ${javac.modulepath}:\
+    ${build.modules.dir}
+javac.test.processorpath=\
+    ${javac.test.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=${source.encoding}
+javadoc.html5=false
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+# The jlink additional root modules to resolve
+jlink.additionalmodules=
+# The jlink additional command line parameters
+jlink.additionalparam=
+jlink.launcher=true
+jlink.launcher.name=vulkanz
+platform.active=default_platform
+project.license=gpl3-notzed
+project.notzed_nativez=../nativez
+reference.notzed_nativez.notzed_nativez_jar=${project.notzed_nativez}/dist/notzed.nativez.jar
+run.classpath=
+# Space-separated list of JVM arguments used when running the project.
+# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
+# To set system properties for unit tests define test-sys-prop.name=value:
+run.jvmargs=--enable-native-access=notzed.vulkan,notzed.nativez,notzed.xlib
+run.modulepath=\
+    ${javac.modulepath}:\
+    ${build.modules.dir}
+run.test.classpath=\
+    ${javac.test.classpath}
+run.test.modulepath=\
+    ${javac.test.modulepath}:\
+    ${build.test.modules.dir}
+source.encoding=UTF-8
+src.dir=src
+src.dir.path=classes
+src.gen.dir=bin/gen
+src.gen.dir.path=classes
+test.src.dir=src
+test.src.dir.path=tests
diff --git a/nbproject/project.xml b/nbproject/project.xml
new file mode 100644 (file)
index 0000000..5fcd924
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.java.j2semodule</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/j2se-modular-project/1">
+            <name>vulkanz</name>
+            <source-roots>
+                <root id="src.dir" pathref="src.dir.path"/>
+                <root id="src.gen.dir" pathref="src.gen.dir.path"/>
+            </source-roots>
+            <test-roots>
+                <root id="test.src.dir" pathref="test.src.dir.path"/>
+            </test-roots>
+        </data>
+        <references xmlns="http://www.netbeans.org/ns/ant-project-references/1">
+            <reference>
+                <foreign-project>notzed_nativez</foreign-project>
+                <artifact-type>jar</artifact-type>
+                <script>build.xml</script>
+                <target>jar</target>
+                <clean-target>clean</clean-target>
+                <id>notzed.nativez.jar</id>
+            </reference>
+        </references>
+    </configuration>
+</project>
diff --git a/src/notzed.display/classes/au/notzed/display/Display.java b/src/notzed.display/classes/au/notzed/display/Display.java
new file mode 100644 (file)
index 0000000..55806b8
--- /dev/null
@@ -0,0 +1,43 @@
+
+package au.notzed.display;
+
+import jdk.incubator.foreign.*;
+import au.notzed.nativez.*;
+
+import vulkan.*;
+import static vulkan.Vulkan.*;
+
+import xlib.*;
+import static xlib.XLib.*;
+
+public abstract class Display {
+
+       public abstract Window createWindow(int width, int height);
+       public abstract void close();
+
+       public static Display createX11Display() {
+               return new X11Display(XOpenDisplay(null));
+       }
+
+       static class X11Display extends Display {
+               XDisplay display;
+
+               static {
+                       XInitThreads();
+               }
+
+               X11Display(XDisplay display) {
+                       this.display = display;
+               }
+
+               public Window createWindow(int width, int height) {
+                       return Window.createX11Window(display, width, height);
+               }
+
+               public void close() {
+                       XCloseDisplay(display);
+               }
+       }
+
+       // xcb, sdl etc
+}
diff --git a/src/notzed.display/classes/au/notzed/display/Event.java b/src/notzed.display/classes/au/notzed/display/Event.java
new file mode 100644 (file)
index 0000000..4599bf9
--- /dev/null
@@ -0,0 +1,59 @@
+package au.notzed.display;
+
+// basic event handling
+public class Event {
+
+       public final int type;
+       public final int x, y, width, height;
+       // shift etc
+       public final int key;
+       public final int button;
+
+       public static final int CLOSE = 0;
+       public static final int KEY = 1;
+       public static final int POINTER = 2;
+       public static final int BUTTON = 3;
+       public static final int EXPOSE = 4;
+       public static final int RESIZE = 5;
+
+       public Event(int type, int x, int y, int width, int height, int key, int button) {
+               this.type = type;
+               this.x = x;
+               this.y = y;
+               this.width = width;
+               this.height = height;
+               this.key = key;
+               this.button = button;
+       }
+
+       public static Event key(int key) {
+               return new Event(KEY, 0, 0, 0, 0, key, 0);
+       }
+
+       public static Event expose(int x, int y, int width, int height) {
+               return new Event(EXPOSE, x, y, width, height, 0, 0);
+       }
+
+       public static Event resize(int x, int y, int width, int height) {
+               return new Event(RESIZE, x, y, width, height, 0, 0);
+       }
+
+       public static Event close() {
+               return new Event(CLOSE, 0, 0, 0, 0, 0, 0);
+       }
+
+       public String toString() {
+               switch (type) {
+               case CLOSE:
+                       return "[Event close]";
+               case KEY:
+                       return "[Event key=" + key + "]";
+               case EXPOSE:
+                       return "[Event expose=" + x + "," + y + "," + width + "," + height + "]";
+               case RESIZE:
+                       return "[Event resize=" + x + "," + y + "," + width + "," + height + "]";
+               default:
+                       return "[Event]";
+               }
+       }
+}
diff --git a/src/notzed.display/classes/au/notzed/display/Window.java b/src/notzed.display/classes/au/notzed/display/Window.java
new file mode 100644 (file)
index 0000000..ef1106b
--- /dev/null
@@ -0,0 +1,141 @@
+package au.notzed.display;
+
+import jdk.incubator.foreign.*;
+import au.notzed.nativez.*;
+
+import vulkan.*;
+import static vulkan.Vulkan.*;
+
+import xlib.*;
+import static xlib.XLib.*;
+
+public abstract class Window {
+
+       public abstract VkSurfaceKHR createVulkanSurface(VkInstance instance, ResourceScope scope);
+
+       public abstract Event nextEvent(boolean blocking);
+
+       public abstract void close();
+
+       static Window createX11Window(XDisplay display, int width, int height) {
+               try ( Frame frame = Frame.frame()) {
+                       long visualMask = VisualScreenMask;
+                       IntArray numberOfVisuals = IntArray.createArray(1, frame);
+                       XVisualInfo vInfoTemplate = XVisualInfo.create(frame);
+
+                       vInfoTemplate.setScreen(DefaultScreen(display));
+
+                       XVisualInfo visualInfo = XGetVisualInfo(display, visualMask, vInfoTemplate, numberOfVisuals);
+                       long colormap = XCreateColormap(display, RootWindow(display, vInfoTemplate.getScreen()), visualInfo.getVisual(), AllocNone);
+
+                       XSetWindowAttributes windowAttributes = XSetWindowAttributes.create(frame);
+
+                       windowAttributes.setBackgroundPixmap(0);
+                       windowAttributes.setColormap(colormap);
+                       windowAttributes.setEventMask(KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask);
+
+                       long window = XCreateWindow(display, RootWindow(display, vInfoTemplate.getScreen()),
+                               0, 0, width, height,
+                               0, visualInfo.getDepth(), InputOutput, visualInfo.getVisual(),
+                               CWBackPixmap  | CWEventMask | CWColormap, windowAttributes);
+
+                       XSelectInput(display, window, ExposureMask | KeyPressMask | StructureNotifyMask | PointerMotionMask);
+
+                       XMapWindow(display, window);
+                       XFlush(display);
+
+                       return new X11Window(display, window);
+               }
+       }
+
+       static class X11Window extends Window {
+
+               XDisplay display;
+               long window;
+               // TODO: move to display
+               long wm_delete_window;
+               long wm_protocols;
+
+               X11Window(XDisplay display, long window) {
+                       this.display = display;
+                       this.window = window;
+                       wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", 0);
+                       wm_protocols = XInternAtom(display, "WM_PROTOCOLS", 0);
+                       try ( Frame frame = Frame.frame()) {
+                               LongArray atoms = LongArray.create(frame, wm_delete_window);
+                               XSetWMProtocols(display, window, atoms, 1);
+                       }
+               }
+
+               @Override
+               public void close() {
+                       XDestroyWindow(display, window);
+               }
+
+               @Override
+               public Event nextEvent(boolean blocking) {
+
+                       try ( Frame frame = Frame.frame()) {
+                               XEvent event = XEvent.create(frame);
+
+                               while (true) {
+                                       if (!blocking && XPending(display) == 0)
+                                               return null;
+
+                                       int res = XNextEvent(display, event);
+
+                                       //System.out.printf("next event: %d type %d\n", res, event.getType());
+                                       switch (event.getType()) {
+                                       case KeyPress:
+                                               return Event.key(event.getXkey().getKeycode());
+                                       case Expose: {
+                                               XExposeEvent x = event.getXexpose();
+                                               return Event.expose(x.getX(), x.getY(), x.getWidth(), x.getHeight());
+                                       }
+                                       case ConfigureNotify: {
+                                               // nb: xy isn't useful
+                                               XConfigureEvent x = event.getXconfigure();
+                                               return Event.resize(x.getX(), x.getY(), x.getWidth(), x.getHeight());
+                                       }
+                                       case UnmapNotify: {
+                                               XUnmapEvent x = event.getXunmap();
+                                               return Event.resize(0, 0, 0, 0);
+                                       }
+                                       case MapNotify: {
+                                               try ( Frame sub = Frame.frame()) {
+                                                       XWindowAttributes at = XWindowAttributes.create(sub);
+                                                       XGetWindowAttributes(display, window, at);
+
+                                                       return Event.resize(at.getX(), at.getY(), at.getWidth(), at.getHeight());
+                                               }
+                                       }
+                                       case ClientMessage: {
+                                               XClientMessageEvent x = event.getXclient();
+
+                                               if (x.getMessageType() == wm_protocols
+                                                       && x.getData().getLElement(0) == wm_delete_window) {
+                                                       return Event.close();
+                                               } else {
+                                                       System.out.printf("unknown client message\n");
+                                               }
+                                               break;
+                                       }
+                                       }
+                               }
+                       }
+               }
+
+               @Override
+               public VkSurfaceKHR createVulkanSurface(VkInstance instance, ResourceScope scope) {
+                       try ( Frame frame = Frame.frame()) {
+                               VkXlibSurfaceCreateInfoKHR surfaceinfo = VkXlibSurfaceCreateInfoKHR.create(
+                                       0,
+                                       display,
+                                       window,
+                                       frame);
+
+                               return instance.vkCreateXlibSurfaceKHR(surfaceinfo, scope);
+                       }
+               }
+       }
+}
diff --git a/src/notzed.display/classes/module-info.java b/src/notzed.display/classes/module-info.java
new file mode 100644 (file)
index 0000000..ac0ced3
--- /dev/null
@@ -0,0 +1,8 @@
+
+module notzed.display {
+       requires notzed.xlib;
+       requires notzed.xcb;
+       requires notzed.vulkan;
+
+       exports au.notzed.display;
+}
diff --git a/src/notzed.vulkan.test/classes/module-info.java b/src/notzed.vulkan.test/classes/module-info.java
new file mode 100644 (file)
index 0000000..b21b754
--- /dev/null
@@ -0,0 +1,7 @@
+
+module notzed.vulkan.test {
+       requires notzed.vulkan;
+       requires notzed.display;
+       
+       requires java.desktop;
+}
diff --git a/src/notzed.vulkan.test/classes/vulkan/test/Cube.java b/src/notzed.vulkan.test/classes/vulkan/test/Cube.java
new file mode 100644 (file)
index 0000000..e8f824e
--- /dev/null
@@ -0,0 +1,55 @@
+
+package vulkan.test;
+
+//struct Vertex {
+//    float posX, posY, posZ, posW;  // Position data
+//    float r, g, b, a;              // Color
+//};
+
+class Cube {
+       static final int dataStride = 8 * 4;
+       static final float[] data = new float[] {
+               // red face
+               -1, -1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+               -1, 1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+               1, -1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+               1, -1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+               -1, 1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+               1, 1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+               // green face
+               -1, -1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+               1, -1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+               -1, 1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+               -1, 1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+               1, -1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+               1, 1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+               // blue face
+               -1, 1, 1, 1.f, 0.f, 0.f, 1.f, 1.f,
+               -1, -1, 1, 1.f, 0.f, 0.f, 1.f, 1.f,
+               -1, 1, -1, 1.f, 0.f, 0.f, 1.f, 1.f,
+               -1, 1, -1, 1.f, 0.f, 0.f, 1.f, 1.f,
+               -1, -1, 1, 1.f, 0.f, 0.f, 1.f, 1.f,
+               -1, -1, -1, 1.f, 0.f, 0.f, 1.f, 1.f,
+               // yellow face
+               1, 1, 1, 1.f, 1.f, 1.f, 0.f, 1.f,
+               1, 1, -1, 1.f, 1.f, 1.f, 0.f, 1.f,
+               1, -1, 1, 1.f, 1.f, 1.f, 0.f, 1.f,
+               1, -1, 1, 1.f, 1.f, 1.f, 0.f, 1.f,
+               1, 1, -1, 1.f, 1.f, 1.f, 0.f, 1.f,
+               1, -1, -1, 1.f, 1.f, 1.f, 0.f, 1.f,
+               // magenta face
+               1, 1, 1, 1.f, 1.f, 0.f, 1.f, 1.f,
+               -1, 1, 1, 1.f, 1.f, 0.f, 1.f, 1.f,
+               1, 1, -1, 1.f, 1.f, 0.f, 1.f, 1.f,
+               1, 1, -1, 1.f, 1.f, 0.f, 1.f, 1.f,
+               -1, 1, 1, 1.f, 1.f, 0.f, 1.f, 1.f,
+               -1, 1, -1, 1.f, 1.f, 0.f, 1.f, 1.f,
+               // cyan face
+               1, -1, 1, 1.f, 0.f, 1.f, 1.f, 1.f,
+               1, -1, -1, 1.f, 0.f, 1.f, 1.f, 1.f,
+               -1, -1, 1, 1.f, 0.f, 1.f, 1.f, 1.f,
+               -1, -1, 1, 1.f, 0.f, 1.f, 1.f, 1.f,
+               1, -1, -1, 1.f, 0.f, 1.f, 1.f, 1.f,
+               -1, -1, -1, 1.f, 0.f, 1.f, 1.f, 1.f,
+       };
+}
diff --git a/src/notzed.vulkan.test/classes/vulkan/test/Demo.java b/src/notzed.vulkan.test/classes/vulkan/test/Demo.java
new file mode 100644 (file)
index 0000000..45ef9bb
--- /dev/null
@@ -0,0 +1,807 @@
+/*
+ * Copyright (C) 2022 Michael Zucchi
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+package vulkan.test;
+
+import au.notzed.nativez.FloatArray;
+import au.notzed.nativez.Frame;
+import au.notzed.nativez.HandleArray;
+import au.notzed.nativez.IntArray;
+import au.notzed.nativez.Memory;
+import java.util.HashSet;
+import java.util.List;
+import java.util.stream.Stream;
+import jdk.incubator.foreign.GroupLayout;
+import jdk.incubator.foreign.MemoryLayout;
+import jdk.incubator.foreign.MemorySegment;
+import jdk.incubator.foreign.ResourceScope;
+import jdk.incubator.foreign.SegmentAllocator;
+import jdk.incubator.foreign.ValueLayout;
+import vulkan.PFN_vkDebugUtilsMessengerCallbackEXT;
+import vulkan.VkApplicationInfo;
+import vulkan.VkAttachmentDescription;
+import vulkan.VkAttachmentReference;
+import vulkan.VkBuffer;
+import vulkan.VkBufferCopy;
+import vulkan.VkBufferCreateInfo;
+import vulkan.VkCommandBuffer;
+import vulkan.VkCommandBufferAllocateInfo;
+import vulkan.VkCommandBufferBeginInfo;
+import vulkan.VkCommandPool;
+import vulkan.VkCommandPoolCreateInfo;
+import vulkan.VkDebugUtilsMessengerCreateInfoEXT;
+import vulkan.VkDebugUtilsMessengerEXT;
+import vulkan.VkDevice;
+import vulkan.VkDeviceCreateInfo;
+import vulkan.VkDeviceMemory;
+import vulkan.VkDeviceQueueCreateInfo;
+import vulkan.VkExtensionProperties;
+import vulkan.VkFence;
+import vulkan.VkFenceCreateInfo;
+import vulkan.VkFramebuffer;
+import vulkan.VkFramebufferCreateInfo;
+import vulkan.VkGraphicsPipelineCreateInfo;
+import vulkan.VkImage;
+import vulkan.VkImageView;
+import vulkan.VkImageViewCreateInfo;
+import vulkan.VkInstance;
+import vulkan.VkInstanceCreateInfo;
+import vulkan.VkMemoryAllocateInfo;
+import vulkan.VkMemoryRequirements;
+import vulkan.VkPhysicalDevice;
+import vulkan.VkPhysicalDeviceFeatures;
+import vulkan.VkPhysicalDeviceMemoryProperties;
+import vulkan.VkPhysicalDeviceProperties;
+import vulkan.VkPipeline;
+import vulkan.VkPipelineColorBlendAttachmentState;
+import vulkan.VkPipelineColorBlendStateCreateInfo;
+import vulkan.VkPipelineDynamicStateCreateInfo;
+import vulkan.VkPipelineInputAssemblyStateCreateInfo;
+import vulkan.VkPipelineLayout;
+import vulkan.VkPipelineLayoutCreateInfo;
+import vulkan.VkPipelineMultisampleStateCreateInfo;
+import vulkan.VkPipelineRasterizationStateCreateInfo;
+import vulkan.VkPipelineShaderStageCreateInfo;
+import vulkan.VkPipelineVertexInputStateCreateInfo;
+import vulkan.VkPipelineViewportStateCreateInfo;
+import vulkan.VkQueue;
+import vulkan.VkQueueFamilyProperties;
+import vulkan.VkRenderPass;
+import vulkan.VkRenderPassCreateInfo;
+import vulkan.VkSemaphore;
+import vulkan.VkSemaphoreCreateInfo;
+import vulkan.VkShaderModule;
+import vulkan.VkShaderModuleCreateInfo;
+import vulkan.VkSubmitInfo;
+import vulkan.VkSubpassDependency;
+import vulkan.VkSubpassDescription;
+import vulkan.VkSurfaceCapabilitiesKHR;
+import vulkan.VkSurfaceFormatKHR;
+import vulkan.VkSurfaceKHR;
+import vulkan.VkSwapchainCreateInfoKHR;
+import vulkan.VkSwapchainKHR;
+import vulkan.VkVertexInputAttributeDescription;
+import vulkan.VkVertexInputBindingDescription;
+import vulkan.Vulkan;
+import static vulkan.Vulkan.*;
+
+/**
+ * Basic demo setup.
+ */
+public class Demo {
+
+       // requested queue types, includes the pseudo-type PRESENT to simplify api
+       public final static int QUEUE_GRAPHICS = VK_QUEUE_GRAPHICS_BIT;
+       public final static int QUEUE_COMPUTE = VK_QUEUE_COMPUTE_BIT;
+       public final static int QUEUE_TRANSFER = VK_QUEUE_TRANSFER_BIT;
+       public final static int QUEUE_PRESENT = 0x80;
+       public final static int QUEUE_MASK = QUEUE_GRAPHICS | QUEUE_COMPUTE | QUEUE_TRANSFER;
+
+       public static VkInstance createInstance(int apiVersion, String[] layers, String[] extensions, ResourceScope scope) {
+               try ( Frame frame = Frame.frame()) {
+                       VkInstanceCreateInfo info = VkInstanceCreateInfo.create(
+                               0,
+                               VkApplicationInfo.create(null, 0, null, 0, apiVersion, frame),
+                               layers.length, layers,
+                               extensions.length, extensions,
+                               frame
+                       );
+
+                       return VkInstance.vkCreateInstance(info, scope);
+               }
+       }
+
+       public static VkDebugUtilsMessengerEXT createLogger(VkInstance instance, ResourceScope scope) throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       var cb = PFN_vkDebugUtilsMessengerCallbackEXT.upcall((severity, flags, data) -> {
+                               System.out.printf("Debug: %d: %s\n", severity, data.getMessage());
+                               return 0;
+                       }, scope);
+                       VkDebugUtilsMessengerCreateInfoEXT info = VkDebugUtilsMessengerCreateInfoEXT.create(
+                               0,
+                               //VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
+                               VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
+                               | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
+                               | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
+                               VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
+                               | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
+                               | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
+                               cb,
+                               null,
+                               frame);
+
+                       return instance.vkCreateDebugUtilsMessengerEXT(info, scope);
+               }
+       }
+
+       static float priority(VkPhysicalDeviceProperties props) {
+               switch (props.getDeviceType()) {
+               default:
+               case VK_PHYSICAL_DEVICE_TYPE_OTHER:
+                       return 0;
+               case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
+                       return 3;
+               case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
+                       return 4;
+               case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
+                       return 2;
+               case VK_PHYSICAL_DEVICE_TYPE_CPU:
+                       return 1;
+               }
+       }
+
+       static VkDeviceQueueCreateInfo selectQueues(VkPhysicalDevice dev, VkSurfaceKHR surface, int[] dflags, int[] families, SegmentAllocator frame$) {
+               try ( Frame frame = Frame.frame()) {
+                       int allFamilies = 0;
+
+                       // This tries to find a matching queue for each application queue requested.
+                       // It priorities queues which are 'more exclusive' than others.
+                       VkQueueFamilyProperties p = dev.vkGetPhysicalDeviceQueueFamilyProperties(frame);
+                       for (int i = 0; i < dflags.length; i++) {
+                               int dflag = dflags[i];
+                               int family = -1;
+                               float bestPri = 0;
+
+                               for (int j = 0; j < p.length(); j++) {
+                                       int flags = p.getQueueFlagsAtIndex(j);
+                                       float pri = 0;
+
+                                       if (surface != null && dev.vkGetPhysicalDeviceSurfaceSupportKHR(j, surface))
+                                               flags |= QUEUE_PRESENT;
+                                       // according to vulkan tutorial either implies transfer
+                                       if ((flags & (Vulkan.VK_QUEUE_GRAPHICS_BIT | Vulkan.VK_QUEUE_COMPUTE_BIT)) != 0)
+                                               flags |= QUEUE_TRANSFER;
+
+                                       if ((flags & dflag) == dflag)
+                                               pri = 5 - Integer.bitCount(flags & ~dflag);
+
+                                       if (pri > bestPri) {
+                                               bestPri = pri;
+                                               family = j;
+                                       }
+                               }
+
+                               if (family == -1)
+                                       throw new RuntimeException("Cannot find required queues");
+
+                               families[i] = family;
+                               allFamilies |= 1 << family;
+                       }
+
+                       int nfamilies = Integer.bitCount(allFamilies);
+
+                       VkDeviceQueueCreateInfo qinfo = VkDeviceQueueCreateInfo.createArray(nfamilies, frame$);
+                       for (int i = 0; i < nfamilies; i++) {
+                               int family = Integer.numberOfTrailingZeros(allFamilies);
+                               FloatArray qpri = FloatArray.create(frame$, 0.0f);
+
+                               qinfo.setQueueFamilyIndexAtIndex(i, family);
+                               qinfo.setQueueCountAtIndex(i, 1);
+                               qinfo.setQueuePrioritiesAtIndex(i, qpri);
+
+                               allFamilies = allFamilies & ~(1 << family);
+                       }
+
+                       System.out.println(qinfo);
+
+                       return qinfo;
+               }
+       }
+
+       // instance?  scope?
+       // allocation?
+       public static record DemoDevice(
+               VkPhysicalDevice dev,
+               VkPhysicalDeviceMemoryProperties memory,
+               VkDevice device,
+               VkSurfaceKHR surface,
+               int[] queueFamilies,
+               VkQueue[] queues, VkCommandPool[] pools) {
+
+               public void close() {
+                       Stream.of(pools).forEach(device::vkDestroyCommandPool);
+                       device.vkDestroyDevice();
+               }
+
+               public VkSemaphore createSemaphore(ResourceScope scope) {
+                       try ( Frame frame = Frame.frame()) {
+                               VkSemaphoreCreateInfo info = VkSemaphoreCreateInfo.create(frame);
+                               return device.vkCreateSemaphore(info, scope);
+                       }
+               }
+
+               public VkFence createFence(ResourceScope scope) {
+                       try ( Frame frame = Frame.frame()) {
+                               VkFenceCreateInfo info = VkFenceCreateInfo.create(frame);
+                               return device.vkCreateFence(info, scope);
+                       }
+               }
+
+               public HandleArray<VkSemaphore> createSemaphores(int count, SegmentAllocator alloc, ResourceScope scope) {
+                       HandleArray<VkSemaphore> list = VkSemaphore.createArray(count, alloc, scope);
+                       try ( Frame frame = Frame.frame()) {
+                               VkSemaphoreCreateInfo info = VkSemaphoreCreateInfo.create(frame);
+
+                               for (int i = 0; i < count; i++)
+                                       list.set(i, device.vkCreateSemaphore(info, scope));
+                       }
+                       return list;
+               }
+
+               public HandleArray<VkFence> createFences(int count, int flags, SegmentAllocator alloc, ResourceScope scope) {
+                       HandleArray<VkFence> list = VkFence.createArray(count, alloc, scope);
+                       try ( Frame frame = Frame.frame()) {
+                               VkFenceCreateInfo info = VkFenceCreateInfo.create(flags, frame);
+
+                               for (int i = 0; i < count; i++)
+                                       list.set(i, device.vkCreateFence(info, scope));
+                       }
+                       return list;
+               }
+
+               public int findMemory(int type, int reqProp) {
+                       for (int i = 0, len = memory.getMemoryTypeCount(); i < len; i++) {
+                               int prop = memory.getMemoryTypes$propertyFlagsAtIndex(i);
+
+                               if (((1 << i) & type) != 0 && (prop & reqProp) == reqProp)
+                                       return i;
+
+                       }
+                       throw new RuntimeException("memory type not found");
+               }
+       }
+
+       // TODO: alloc?
+       public static DemoDevice createDevice(VkInstance instance, VkPhysicalDeviceFeatures features, VkSurfaceKHR surface, int[] queueFlags, String[] extensions, ResourceScope scope) {
+               try ( Frame frame = Frame.frame()) {
+                       HandleArray<VkPhysicalDevice> devs = instance.vkEnumeratePhysicalDevices(frame, scope);
+                       VkPhysicalDeviceProperties dp = VkPhysicalDeviceProperties.create(frame);
+                       VkPhysicalDeviceFeatures df = VkPhysicalDeviceFeatures.create(frame);
+
+                       // First select best device
+                       //  - must support features
+                       //  - if surface non-null then must support surface output
+                       VkPhysicalDevice dev = null;
+                       VkSurfaceFormatKHR surfaceFormats = null;
+                       IntArray presentModes = null;
+
+                       float bestPriority = -1;
+                       for (int i = 0; i < devs.size(); i++) {
+                               VkPhysicalDevice d = devs.getAtIndex(i);
+                               VkSurfaceFormatKHR formats = null;
+                               IntArray modes = null;
+                               float pri;
+
+                               d.vkGetPhysicalDeviceProperties(dp);
+                               pri = priority(dp);
+                               if (pri <= bestPriority)
+                                       continue;
+
+                               if (!checkDeviceExtensionSupport(d, extensions))
+                                       continue;
+
+                               d.vkGetPhysicalDeviceFeatures(df);
+                               if (!df.hasFeatures(features))
+                                       continue;
+
+                               if (surface != null) {
+                                       formats = d.vkGetPhysicalDeviceSurfaceFormatsKHR(surface, frame);
+                                       modes = d.vkGetPhysicalDeviceSurfacePresentModesKHR(surface, frame);
+
+                                       if (formats.isEmpty() || modes.isEmpty())
+                                               continue;
+                               }
+
+                               dev = d;
+                               surfaceFormats = formats;
+                               presentModes = modes;
+                               bestPriority = pri;
+                       }
+
+                       if (dev == null)
+                               throw new RuntimeException("No suitable device found");
+
+                       int[] families = new int[queueFlags.length];
+                       VkDeviceQueueCreateInfo qinfo = selectQueues(dev, surface, queueFlags, families, frame);
+
+                       VkDeviceCreateInfo devinfo = VkDeviceCreateInfo.create(
+                               0,
+                               (int)qinfo.length(), qinfo,
+                               0, null, // enabled layers
+                               (int)Memory.length(extensions), extensions,
+                               features,
+                               frame);
+
+                       var device = dev.vkCreateDevice(devinfo, scope);
+
+                       VkCommandPoolCreateInfo poolInfo = VkCommandPoolCreateInfo.create(0, 0, frame);
+
+                       // TODO: only create one pool/queue per unique family?
+                       VkQueue[] queues = new VkQueue[families.length];
+                       VkCommandPool[] pools = new VkCommandPool[families.length];
+
+                       for (int i = 0; i < families.length; i++) {
+                               System.out.printf(" queue[%d] = family[%d]\n", i, families[i]);
+                               queues[i] = device.vkGetDeviceQueue(families[i], 0, scope);
+
+                               poolInfo.setQueueFamilyIndex(families[i]);
+                               pools[i] = device.vkCreateCommandPool(poolInfo, scope);
+                       }
+
+                       VkPhysicalDeviceMemoryProperties memory = VkPhysicalDeviceMemoryProperties.create((SegmentAllocator)scope);
+
+                       dev.vkGetPhysicalDeviceMemoryProperties(memory);
+
+                       return new DemoDevice(dev, memory, device, surface, families, queues, pools);
+               }
+       }
+
+       public record ColourFormat(int format, int colourSpace) {
+
+               boolean matchAt(VkSurfaceFormatKHR fmt, int i) {
+                       return fmt.getFormatAtIndex(i) == format
+                               & fmt.getColorSpaceAtIndex(i) == colourSpace;
+               }
+
+               public static ColourFormat SRGB = new ColourFormat(VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR);
+
+       }
+
+       static ColourFormat selectColourFormat(VkSurfaceFormatKHR formats, ColourFormat def, ColourFormat... query) {
+               int len = (int)formats.length();
+
+               if (len == 1 && formats.getColorSpaceAtIndex(0) == VK_FORMAT_UNDEFINED)
+                       return def;
+
+               for (ColourFormat q: query) {
+                       for (int i = 0; i < len; i++) {
+                               if (q.matchAt(formats, i))
+                                       return q;
+                       }
+               }
+
+               return new ColourFormat(formats.getFormatAtIndex(0), formats.getColorSpaceAtIndex(0));
+       }
+
+       static int selectPresentMode(IntArray modes, int def, int... query) {
+               for (int q: query) {
+                       if (modes.contains(q))
+                               return q;
+               }
+               return def;
+       }
+
+       static int selectFlags(int flag, int def, int... query) {
+               for (int q: query) {
+                       if ((flag & q) == q)
+                               return q;
+               }
+               return def;
+       }
+
+       static int clampi(int v, int min, int max) {
+               return Math.max(min, Math.min(max, v));
+       }
+
+       public static record DemoChain(VkSwapchainKHR swapchain,
+               ColourFormat format,
+               int extWidth, int extHeight,
+               HandleArray<VkImage> images,
+               HandleArray<VkImageView> views,
+               VkRenderPass renderPass) {
+
+       }
+
+       static DemoChain createSwapchain(DemoDevice dd, ColourFormat prefFormat, int prefPresentMode, int pixWidth, int pixHeight, SegmentAllocator alloc, ResourceScope scope) {
+               try ( Frame frame = Frame.frame()) {
+                       ColourFormat format = selectColourFormat(
+                               dd.dev.vkGetPhysicalDeviceSurfaceFormatsKHR(dd.surface, frame),
+                               ColourFormat.SRGB,
+                               prefFormat);
+
+                       int presentMode = selectPresentMode(dd.dev.vkGetPhysicalDeviceSurfacePresentModesKHR(dd.surface, frame),
+                               VK_PRESENT_MODE_FIFO_KHR,
+                               prefPresentMode,
+                               VK_PRESENT_MODE_FIFO_KHR,
+                               VK_PRESENT_MODE_FIFO_RELAXED_KHR,
+                               VK_PRESENT_MODE_MAILBOX_KHR,
+                               VK_PRESENT_MODE_IMMEDIATE_KHR);
+
+                       VkSurfaceCapabilitiesKHR caps = VkSurfaceCapabilitiesKHR.create(frame);
+
+                       dd.dev.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dd.surface, caps);
+
+                       int extWidth = caps.getCurrentExtent$width();
+                       int extHeight = caps.getCurrentExtent$height();
+
+                       if (extWidth == ~0) {
+                               extWidth = clampi(pixWidth, caps.getMinImageExtent$width(), caps.getMaxImageExtent$width());
+                               extHeight = clampi(pixHeight, caps.getMinImageExtent$height(), caps.getMaxImageExtent$height());
+                       }
+
+                       int minCount = caps.getMinImageCount();
+                       int maxCount = caps.getMaxImageCount();
+                       int imageCount = clampi(minCount + 1, minCount, maxCount == 0 ? Integer.MAX_VALUE : maxCount);
+
+                       int composite = selectFlags(caps.getSupportedCompositeAlpha(),
+                               VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
+                               VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
+                               VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
+                               VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
+                               VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR);
+
+                       VkSwapchainCreateInfoKHR chainInfo = VkSwapchainCreateInfoKHR.create(
+                               0,
+                               dd.surface,
+                               imageCount,
+                               format.format, format.colourSpace,
+                               extWidth, extHeight,
+                               1, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+                               VK_SHARING_MODE_EXCLUSIVE, 0, null,// createDevice requires present+graphics queue the same
+                               caps.getCurrentTransform(), // prefer VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR ?
+                               composite,
+                               presentMode,
+                               VK_TRUE, // clipped
+                               null, // old chain
+                               frame);
+
+                       VkSwapchainKHR swapchain = dd.device.vkCreateSwapchainKHR(chainInfo, scope);
+
+                       // Create images and image views, here or elsewhere?
+                       HandleArray<VkImage> images = dd.device.vkGetSwapchainImagesKHR(swapchain, alloc, scope);
+                       HandleArray<VkImageView> views = VkImageView.createArray(images.length(), alloc, scope);
+
+                       VkImageViewCreateInfo viewInfo = VkImageViewCreateInfo.create(
+                               0,
+                               null,
+                               VK_IMAGE_VIEW_TYPE_2D,
+                               format.format,
+                               VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A,
+                               VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1,
+                               frame);
+
+                       for (int i = 0; i < images.size(); i++) {
+                               viewInfo.setImage(images.get(i));
+                               views.set(i, dd.device.vkCreateImageView(viewInfo, scope));
+                       }
+
+                       VkRenderPass renderPass = dumbRenderPass(dd.device, format.format, scope);
+
+                       return new DemoChain(swapchain, format, extWidth, extHeight, images, views, renderPass);
+               }
+       }
+
+       /* graphics pipeline model from vulkan tutorial [programmable shader]
+
+       input assembler
+       [vertex shader]
+       [tesselation]
+       [geometry shader]
+       rasterisation
+       [fragment shader]
+       colour blending
+
+        */
+
+ /* each shader  (code, stage, main)
+           + specialisation info to initialise constants per-pipeline shader stage init
+
+        */
+       static VkRenderPass dumbRenderPass(VkDevice device, int format, ResourceScope scope) {
+               try ( Frame frame = Frame.frame()) {
+                       VkAttachmentDescription colourAttachment = VkAttachmentDescription.create(
+                               0,
+                               format,
+                               VK_SAMPLE_COUNT_1_BIT,
+                               VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE,
+                               VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE,
+                               VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+                               frame);
+
+                       VkAttachmentReference colourRef = VkAttachmentReference.create(0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, frame);
+                       VkSubpassDescription subpass = VkSubpassDescription.create(
+                               0,
+                               VK_PIPELINE_BIND_POINT_GRAPHICS,
+                               0, null,
+                               1, colourRef, null, null,
+                               0, null,
+                               frame);
+
+                       VkSubpassDependency dependency = VkSubpassDependency.create(
+                               VK_SUBPASS_EXTERNAL, 0,
+                               VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+                               0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+                               0, frame);
+
+                       VkRenderPassCreateInfo renderPassInfo = VkRenderPassCreateInfo.create(
+                               0,
+                               1, colourAttachment,
+                               1, subpass,
+                               0, null,
+                               frame);
+
+                       return device.vkCreateRenderPass(renderPassInfo, scope);
+               }
+       }
+
+       static VkShaderModule createShaderModule(VkDevice device, IntArray spirv, ResourceScope scope) {
+               try ( Frame frame = Frame.frame()) {
+                       VkShaderModuleCreateInfo vsInfo = VkShaderModuleCreateInfo.create(
+                               0,
+                               spirv.length() * 4,
+                               spirv,
+                               frame);
+                       return device.vkCreateShaderModule(vsInfo, scope);
+               }
+       }
+
+       static VkPipelineLayout dumbPipelineLayout(VkDevice device, ResourceScope scope) {
+               try ( Frame frame = Frame.frame()) {
+                       VkPipelineLayoutCreateInfo layoutInfo = VkPipelineLayoutCreateInfo.create(0, 0, null, 0, null, frame);
+                       return device.vkCreatePipelineLayout(layoutInfo, scope);
+               }
+       }
+
+       static VkPipeline dumbPipeline(VkDevice device, VkShaderModule vertex, VkShaderModule fragment, VkPipelineLayout layout, VkRenderPass renderPass, ResourceScope scope) {
+               try ( Frame frame = Frame.frame()) {
+                       VkPipelineShaderStageCreateInfo shader = VkPipelineShaderStageCreateInfo.createArray(2, frame);
+
+                       shader.setAtIndex(0, 0, VK_SHADER_STAGE_VERTEX_BIT, vertex, "main", null, frame);
+                       shader.setAtIndex(1, 0, VK_SHADER_STAGE_FRAGMENT_BIT, fragment, "main", null, frame);
+
+                       VkPipelineDynamicStateCreateInfo dynamic = VkPipelineDynamicStateCreateInfo.create(
+                               0,
+                               2, IntArray.create(frame, VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR),
+                               frame);
+
+                       // TODO: parameter?
+                       VkVertexInputBindingDescription binding = VkVertexInputBindingDescription.createArray(1, frame);
+                       VkVertexInputAttributeDescription attribute = VkVertexInputAttributeDescription.createArray(2, frame);
+
+                       binding.setAtIndex(0, 0, 5 * 4, Vulkan.VK_VERTEX_INPUT_RATE_VERTEX, frame);
+                       attribute.setAtIndex(0, 0, 0, Vulkan.VK_FORMAT_R32G32_SFLOAT, 0, frame);
+                       attribute.setAtIndex(1, 1, 0, Vulkan.VK_FORMAT_R32G32B32_SFLOAT, 2 * 4, frame);
+
+                       VkPipelineVertexInputStateCreateInfo vertextInput = VkPipelineVertexInputStateCreateInfo.create(
+                               0,
+                               1, binding,
+                               2, attribute,
+                               frame);
+
+                       VkPipelineInputAssemblyStateCreateInfo inputAssembly = VkPipelineInputAssemblyStateCreateInfo.create(
+                               0,
+                               VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
+                               VK_FALSE,
+                               frame);
+
+                       VkPipelineViewportStateCreateInfo viewport = VkPipelineViewportStateCreateInfo.create(
+                               0,
+                               1, null,
+                               1, null,
+                               frame);
+
+                       VkPipelineRasterizationStateCreateInfo rasterisation = VkPipelineRasterizationStateCreateInfo.create(
+                               0,
+                               VK_FALSE,
+                               VK_FALSE,
+                               VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_CLOCKWISE,
+                               VK_FALSE, 0.0f, 0.0f, 0.0f,
+                               1.0f, frame);
+
+                       VkPipelineMultisampleStateCreateInfo multisample = VkPipelineMultisampleStateCreateInfo.create(
+                               0,
+                               VK_SAMPLE_COUNT_1_BIT,
+                               VK_FALSE, 1.0f, null,
+                               VK_FALSE, VK_FALSE,
+                               frame);
+
+                       VkPipelineColorBlendAttachmentState colourBlendAttachment = VkPipelineColorBlendAttachmentState.create(
+                               VK_FALSE,
+                               VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD,
+                               VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD,
+                               VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
+                               frame);
+
+                       VkPipelineColorBlendStateCreateInfo colourBlend = VkPipelineColorBlendStateCreateInfo.create(
+                               0,
+                               VK_FALSE, VK_LOGIC_OP_NO_OP,
+                               1, colourBlendAttachment,
+                               1.0f, 1.0f, 1.0f, 1.0f,
+                               frame);
+
+                       VkGraphicsPipelineCreateInfo pipeline = VkGraphicsPipelineCreateInfo.create(
+                               0,
+                               2, shader,
+                               vertextInput, inputAssembly,
+                               null,
+                               viewport,
+                               rasterisation,
+                               multisample,
+                               null,
+                               colourBlend,
+                               dynamic,
+                               layout,
+                               renderPass, 0,
+                               null, 0, frame);
+
+                       return device.vkCreateGraphicsPipeline(null, pipeline, scope);
+
+               }
+       }
+
+       public static HandleArray<VkFramebuffer> createFramebuffers(DemoDevice device, DemoChain chain, SegmentAllocator alloc, ResourceScope scope) {
+               try ( Frame frame = Frame.frame()) {
+                       var frameBuffers = VkFramebuffer.createArray(chain.views.length(), alloc);
+                       HandleArray<VkImageView> attachments = VkImageView.createArray(1, frame);
+                       VkFramebufferCreateInfo framebufferInfo = VkFramebufferCreateInfo.create(
+                               0,
+                               chain.renderPass,
+                               1, attachments,
+                               chain.extWidth, chain.extHeight,
+                               1,
+                               frame);
+
+                       for (int i = 0; i < chain.views.size(); i++) {
+                               attachments.setAtIndex(0, chain.views.get(i));
+                               frameBuffers.setAtIndex(i, device.device.vkCreateFramebuffer(framebufferInfo, scope));
+                       }
+                       return frameBuffers;
+               }
+       }
+
+       public static HandleArray<VkCommandBuffer> createCommandBuffers(DemoDevice device, int appIndex, int count, SegmentAllocator alloc, ResourceScope scope) {
+               try ( Frame frame = Frame.frame()) {
+                       VkCommandBufferAllocateInfo bufferInfo = VkCommandBufferAllocateInfo.create(
+                               device.pools[appIndex],
+                               VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+                               count,
+                               frame);
+
+                       return device.device.vkAllocateCommandBuffers(bufferInfo, (SegmentAllocator)scope, scope);
+               }
+       }
+
+       public static void resetCommandBuffers(DemoDevice device, int appIndex, HandleArray<VkCommandBuffer> commandBuffers) {
+               try ( Frame frame = Frame.frame()) {
+                       VkCommandBufferAllocateInfo bufferInfo = VkCommandBufferAllocateInfo.create(
+                               device.pools[appIndex],
+                               VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+                               commandBuffers.size(),
+                               frame);
+
+                       device.device.vkAllocateCommandBuffers(bufferInfo, commandBuffers);
+               }
+       }
+
+       // device?
+       public record Buffer(
+               VkBuffer buffer,
+               VkDeviceMemory memory,
+               long size,
+               long offset) {
+
+               public void close(VkDevice device) {
+                       device.vkFreeMemory(memory);
+                       device.vkDestroyBuffer(buffer);
+               }
+       }
+
+       // TODO: memory allocator?  - limited memory blocks?
+       public static Buffer createBuffer(DemoDevice device, long size, int usage, int props, int[] sharedFamilies, MemorySegment src, ResourceScope scope) {
+               try ( Frame frame = Frame.frame()) {
+                       IntArray families = sharedFamilies != null ? IntArray.create(frame, sharedFamilies) : null;
+                       VkBufferCreateInfo create = VkBufferCreateInfo.create(0, size, usage, VK_SHARING_MODE_CONCURRENT, sharedFamilies != null ? sharedFamilies.length : 0, families, frame);
+                       VkBuffer buffer = device.device.vkCreateBuffer(create, scope);
+                       VkMemoryRequirements req = VkMemoryRequirements.create(frame);
+
+                       device.device.vkGetBufferMemoryRequirements(buffer, req);
+
+                       VkMemoryAllocateInfo allocate = VkMemoryAllocateInfo.create(
+                               req.getSize(),
+                               device.findMemory(req.getMemoryTypeBits(), props),
+                               frame);
+
+                       System.out.printf("size = %x\n", size);
+
+                       VkDeviceMemory memory = device.device.vkAllocateMemory(allocate, scope);
+
+                       device.device.vkBindBufferMemory(buffer, memory, 0);
+
+                       if (src != null) {
+                               MemorySegment mem = device.device.vkMapMemory(memory, 0, size, 0, frame.scope());
+
+                               mem.copyFrom(src);
+                               device.device.vkUnmapMemory(memory);
+                       }
+
+                       return new Buffer(buffer, memory, size, 0);
+               }
+       }
+
+       public static void copyBuffer(DemoDevice device, Buffer src, Buffer dst) {
+               try ( Frame frame = Frame.frame()) {
+                       VkCommandPool xfer = device.pools()[1];
+                       VkCommandBufferAllocateInfo alloc = VkCommandBufferAllocateInfo.create(xfer, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, frame);
+                       HandleArray<VkCommandBuffer> cmds = device.device.vkAllocateCommandBuffers(alloc, frame, frame.scope());
+                       VkCommandBuffer cmd = cmds.get(0);
+
+                       VkCommandBufferBeginInfo begin = VkCommandBufferBeginInfo.create(Vulkan.VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, null, frame);
+                       VkBufferCopy region = VkBufferCopy.create(0, 0, dst.size, frame);
+
+                       cmd.vkBeginCommandBuffer(begin);
+                       cmd.vkCmdCopyBuffer(src.buffer, dst.buffer, 1, region);
+                       cmd.vkEndCommandBuffer();
+
+                       VkSubmitInfo submit = VkSubmitInfo.create(0, null, null, 1, cmds, 0, null, frame);
+                       device.queues[1].vkQueueSubmit(1, submit, null);
+
+                       device.queues[1].vkQueueWaitIdle();
+
+                       // NB: must be finished before freeing
+                       device.device.vkFreeCommandBuffers(xfer, 1, cmds);
+               }
+
+       }
+
+       // not worth it, its simple enough
+       static VkVertexInputAttributeDescription createInputAttributes(SegmentAllocator alloc, int binding, int... loc) {
+               int len = loc.length / 3;
+
+               VkVertexInputAttributeDescription attribute = VkVertexInputAttributeDescription.createArray(len, alloc);
+               for (int i = 0; i < len; i++) {
+                       attribute.setAtIndex(i, loc[i * 3 + 0], binding, loc[i * 3 + 1], loc[i * 3 + 2], alloc);
+               }
+               return attribute;
+       }
+
+       static boolean checkDeviceExtensionSupport(VkPhysicalDevice dev, String... query) {
+               // spec versions?
+               if (query.length > 0) {
+                       try ( Frame frame = Frame.frame()) {
+                               VkExtensionProperties props = dev.vkEnumerateDeviceExtensionProperties(null, frame);
+                               HashSet<String> set = new HashSet(List.of(query));
+                               for (int i = 0; i < props.length(); i++)
+                                       set.remove(props.getExtensionNameAtIndex(i));
+
+                               if (!set.isEmpty())
+                                       return false;
+                       }
+               }
+
+               return true;
+       }
+
+}
diff --git a/src/notzed.vulkan.test/classes/vulkan/test/GLMaths.java b/src/notzed.vulkan.test/classes/vulkan/test/GLMaths.java
new file mode 100644 (file)
index 0000000..3414740
--- /dev/null
@@ -0,0 +1,131 @@
+
+package vulkan.test;
+
+import static java.lang.Math.*;
+import java.util.Arrays;
+
+public class GLMaths {
+       public static void identity4f(float []matrix) {
+               Arrays.fill(matrix, 0.0f);
+               for (int i = 0; i < 4; i++)
+                       matrix[i * 4 + i] = 1.0f;
+       }
+
+       public static float length3f(float [] a) {
+               float sum = 0;
+               for (int i = 0; i < 3; i++)
+                       sum += a[i] * a[i];
+               return (float)sqrt(sum);
+       }
+
+       public static void sub3f(float [] c, float [] a, float [] b) {
+               for (int i = 0; i < 3; i++)
+                       c[i] = a[i] - b[i];
+       }
+
+       public static void norm3f(float [] vec) {
+               float fix = 1.0f / length3f(vec);
+               for (int i = 0; i < 3; i++)
+                       vec[i] *= fix;
+       }
+
+       public static void cross3f(float [] c, float [] a, float [] b) {
+               c[0] = a[1] * b[2] - a[2] * b[1];
+               c[1] = a[2] * b[0] - a[0] * b[2];
+               c[2] = a[0] * b[1] - a[1] * b[0];
+       }
+
+       public static float dot3f(float [] a, float [] b) {
+               return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
+       }
+
+       public static float [] mult4x4f(float [] c, float [] b, float [] a) {
+               c[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12];
+               c[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13];
+               c[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14];
+               c[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15];
+
+               c[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12];
+               c[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13];
+               c[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14];
+               c[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15];
+
+               c[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12];
+               c[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13];
+               c[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14];
+               c[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15];
+
+               c[12] = a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12];
+               c[13] = a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13];
+               c[14] = a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14];
+               c[15] = a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15];
+
+               return c;
+       }
+
+       public static void lookAt(float []mat, float []eye, float []centre, float []up) {
+               float forward[] = new float[3], side[] = new float[3], u[] = new float[3];
+
+               sub3f(forward, centre, eye);
+               norm3f(forward);
+               cross3f(side, forward, up);
+               norm3f(side);
+               cross3f(u, side, forward);
+
+               mat[0] = side[0];
+               mat[4] = side[1];
+               mat[8] = side[2];
+
+               mat[1] = u[0];
+               mat[5] = u[1];
+               mat[9] = u[2];
+
+               mat[2] = -forward[0];
+               mat[6] = -forward[1];
+               mat[10] = -forward[2];
+
+               mat[12] = -dot3f(side, eye);
+               mat[13] = -dot3f(u, eye);
+               mat[14] = dot3f(forward, eye);
+
+               mat[3] = 0.0f;
+               mat[7] = 0.0f;
+               mat[11] = 0.0f;
+
+               mat[15] = 1.0f;
+       }
+
+       public static void frustum(float []mat, float left, float right, float bottom, float top, float znear, float zfar) {
+               float temp, temp2, temp3, temp4;
+
+               temp = 2.0f * znear;
+               temp2 = right - left;
+               temp3 = top - bottom;
+               temp4 = zfar - znear;
+               mat[0] = temp / temp2;
+               mat[1] = 0.0f;
+               mat[2] = 0.0f;
+               mat[3] = 0.0f;
+               mat[4] = 0.0f;
+               mat[5] = temp / temp3;
+               mat[6] = 0.0f;
+               mat[7] = 0.0f;
+               mat[8] = (right + left) / temp2;
+               mat[9] = (top + bottom) / temp3;
+               mat[10] = (-zfar - znear) / temp4;
+               mat[11] = -1.0f;
+               mat[12] = 0.0f;
+               mat[13] = 0.0f;
+               mat[14] = (-temp * zfar) / temp4;
+               mat[15] = 0.0f;
+       }
+
+       public static void perspective(float []mat, float fovy, float aspect, float znear, float zfar) {
+               float ymax, xmax;
+
+               ymax = znear * (float)tan(fovy * 0.5f);
+               xmax = ymax * aspect;
+
+               frustum(mat, -xmax, xmax, -ymax, ymax, znear, zfar);
+       }
+}
diff --git a/src/notzed.vulkan.test/classes/vulkan/test/TestCube.java b/src/notzed.vulkan.test/classes/vulkan/test/TestCube.java
new file mode 100644 (file)
index 0000000..f68a177
--- /dev/null
@@ -0,0 +1,1041 @@
+/*
+The MIT License (MIT)
+
+Copyright (C) 2017 Eric Arnebäck
+Copyright (C) 2019 Michael Zucchi
+
+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, sublicense, 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 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 NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS 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.
+
+ */
+
+ /*
+ * This is a Java conversion of a C conversion of this:
+ * https://github.com/Erkaman/vulkan_minimal_compute
+ *
+ * It's been simplified a bit and converted to the 'zvk' api.
+ */
+package vulkan.test;
+
+import au.notzed.display.Display;
+import jdk.incubator.foreign.*;
+import au.notzed.nativez.*;
+
+import vulkan.*;
+import static vulkan.Vulkan.*;
+
+import au.notzed.display.*;
+import static vulkan.test.GLMaths.*;
+
+public class TestCube {
+
+       static final boolean debug = true;
+
+       final static int NUM_SAMPLES = VK_SAMPLE_COUNT_1_BIT;
+       final static int NUM_DESCRIPTOR_SETS = 1;
+
+       ResourceScope scope = ResourceScope.newSharedScope();
+
+       int width = 800;
+       int height = 800;
+       float projection[] = new float[16];
+       float view[] = new float[16];
+       float model[] = new float[16];
+       float clip[] = new float[]{
+               1.0f, 0.0f, 0.0f, 0.0f,
+               0.0f, -1.0f, 0.0f, 0.0f,
+               0.0f, 0.0f, 0.5f, 0.0f,
+               0.0f, 0.0f, 0.5f, 1.0f
+       };
+       float mvp[] = new float[16];
+
+       VkInstance instance;
+       VkPhysicalDevice physicalDevice;
+       VkPhysicalDeviceMemoryProperties memory_properties;
+       VkPhysicalDeviceFeatures device_features;
+
+       int present_queue_index;
+       int graphics_queue_index;
+
+       VkDevice device;
+       VkSwapchainKHR chain;
+
+       VkQueue graphics_queue;
+       VkQueue present_queue;
+
+       int chainImageFormat;
+       HandleArray<VkImage> chainImage;
+       HandleArray<VkImageView> chainImageView;
+
+       int depthFormat;
+       VkImage depthImage;
+       VkImageView depthView;
+       VkDeviceMemory depthMemory;
+
+       VkCommandPool cmd_pool;
+       HandleArray<VkCommandBuffer> cmd;
+
+       BufferMemory uniform;
+       VkPipelineLayout pipeline_layout;
+
+       VkDescriptorSetLayout desc_layout;
+       VkDescriptorPool desc_pool;
+       HandleArray<VkDescriptorSet> desc_set;
+
+       VkRenderPass render_pass;
+       HandleArray<VkFramebuffer> framebuffers;
+
+       BufferMemory vertex;
+       HandleArray<VkBuffer> vertexBuffer = VkBuffer.createArray(1, (SegmentAllocator)scope);
+       VkVertexInputBindingDescription vi_binding = VkVertexInputBindingDescription.createArray(1, (SegmentAllocator)scope);
+       VkVertexInputAttributeDescription vi_attribs = VkVertexInputAttributeDescription.createArray(2, (SegmentAllocator)scope);
+
+       IntArray cube_vs;
+       IntArray cube_fs;
+       HandleArray<VkShaderModule> shader = VkShaderModule.createArray(2, (SegmentAllocator)scope);
+
+       HandleArray<VkPipeline> pipeline = VkPipeline.createArray(1, (SegmentAllocator)scope);
+
+       VkSemaphore chainSemaphore;
+       VkFence drawFence;
+
+       record BufferMemory(VkBuffer buffer, VkDeviceMemory memory, long size) {
+
+               public void free(VkDevice device) {
+                       device.vkFreeMemory(memory);
+                       device.vkDestroyBuffer(buffer);
+               }
+       }
+
+       VkDebugUtilsMessengerEXT logger;
+
+       void init_debug() throws Exception {
+               if (!debug)
+                       return;
+               try ( Frame frame = Frame.frame()) {
+                       var cb = PFN_vkDebugUtilsMessengerCallbackEXT.upcall((severity, flags, data) -> {
+                               System.out.printf("Debug: %d: %s\n", severity, data.getMessage());
+                               return 0;
+                       }, scope);
+                       VkDebugUtilsMessengerCreateInfoEXT info = VkDebugUtilsMessengerCreateInfoEXT.create(
+                               0,
+                               VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
+                               | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
+                               | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
+                               VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
+                               cb,
+                               null,
+                               frame);
+
+                       logger = instance.vkCreateDebugUtilsMessengerEXT(info, scope);
+               }
+
+               //typedef VkBool32 (*PFN_vkDebugUtilsMessengerCallbackEXT)(VkDebugUtilsMessageSeverityFlagBitsEXT, VkDebugUtilsMessageTypeFlagsEXT, const VkDebugUtilsMessengerCallbackDataEXT *, void *);
+       }
+
+       void init_instance() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       VkInstanceCreateInfo info = VkInstanceCreateInfo.create(
+                               0,
+                               VkApplicationInfo.create("cube", 1, "cube-engine", 2, VK_API_VERSION_1_0, frame),
+                               1, new String[]{"VK_LAYER_KHRONOS_validation"},
+                               3, new String[]{"VK_KHR_surface", "VK_KHR_xlib_surface", "VK_EXT_debug_utils"},
+                               frame
+                       );
+
+                       instance = VkInstance.vkCreateInstance(info, scope);
+                       System.out.printf("instance = %s\n", instance);
+               }
+       }
+
+       Display display;
+       Window window;
+       VkSurfaceKHR surface;
+
+       void init_surface() throws Exception {
+               display = Display.createX11Display();
+               window = display.createWindow(width, height);
+               surface = window.createVulkanSurface(instance, scope);
+               System.out.printf("surface: %s\n", surface);
+       }
+
+       void init_device() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       IntArray count$h = IntArray.create(frame, 1);
+                       IntArray present$h = IntArray.create(frame, 1);
+                       HandleArray<VkPhysicalDevice> devs;
+                       int count;
+                       int res;
+
+                       devs = instance.vkEnumeratePhysicalDevices(frame, scope);
+
+                       // Search for device and queue indices
+                       int devid = -1;
+                       int present_queue = -1;
+                       int graphics_queue = -1;
+                       for (int i = 0; i < devs.length(); i++) {
+                               VkPhysicalDevice dev = devs.getAtIndex(i);
+                               VkQueueFamilyProperties famprops;
+
+                               famprops = dev.vkGetPhysicalDeviceQueueFamilyProperties(frame);
+
+                               for (int j = 0; j < famprops.length(); j++) {
+                                       boolean present = dev.vkGetPhysicalDeviceSurfaceSupportKHR(j, surface);
+
+                                       if (present && present_queue == -1)
+                                               present_queue = j;
+                                       if ((famprops.getQueueFlagsAtIndex(j) & VK_QUEUE_GRAPHICS_BIT) != 0) {
+                                               graphics_queue = j;
+                                               if (present) {
+                                                       present_queue = j;
+                                                       break;
+                                               }
+                                       }
+                               }
+                               if (present_queue != -1 && graphics_queue != -1) {
+                                       devid = i;
+                                       break;
+                               }
+                       }
+
+                       if (devid == -1)
+                               throw new Exception("Cannot find a suitable device");
+
+                       physicalDevice = devs.getAtIndex(devid);
+                       present_queue_index = present_queue;
+                       graphics_queue_index = graphics_queue;
+
+                       // NOTE: app scope
+                       memory_properties = VkPhysicalDeviceMemoryProperties.create((SegmentAllocator)scope);
+                       physicalDevice.vkGetPhysicalDeviceMemoryProperties(memory_properties);
+                       device_features = VkPhysicalDeviceFeatures.create((SegmentAllocator)scope);
+                       physicalDevice.vkGetPhysicalDeviceFeatures(device_features);
+
+                       FloatArray qpri = FloatArray.create(frame, 0.0f);
+                       VkDeviceQueueCreateInfo qinfo = VkDeviceQueueCreateInfo.create(
+                               0,
+                               graphics_queue,
+                               1, qpri,
+                               frame);
+                       String[] extensions = {
+                               "VK_KHR_swapchain"
+                       };
+                       VkPhysicalDeviceFeatures features = VkPhysicalDeviceFeatures.create(frame);
+                       features.setDepthClamp(1);
+                       VkDeviceCreateInfo devinfo = VkDeviceCreateInfo.create(
+                               0,
+                               1, qinfo,
+                               0, null,
+                               extensions.length, extensions,
+                               features,
+                               frame);
+
+                       device = physicalDevice.vkCreateDevice(devinfo, scope);
+
+                       System.out.printf("device = %s\n", device);
+
+                       /* ************************************************************** */
+                       int format;
+                       VkSurfaceFormatKHR surfFormats = physicalDevice.vkGetPhysicalDeviceSurfaceFormatsKHR(surface, frame);
+                       // If the format list includes just one entry of VK_FORMAT_UNDEFINED,
+                       // the surface has no preferred format.  Otherwise, at least one
+                       // supported format will be returned.
+                       if (surfFormats.length() == 1 && surfFormats.getFormatAtIndex(0) == VK_FORMAT_UNDEFINED) {
+                               format = VK_FORMAT_B8G8R8A8_UNORM;
+                       } else {
+                               format = surfFormats.getFormatAtIndex(0);
+                       }
+
+                       VkSurfaceCapabilitiesKHR surfCapabilities = VkSurfaceCapabilitiesKHR.create(frame);
+
+                       physicalDevice.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(surface, surfCapabilities);
+                       IntArray presentModes = physicalDevice.vkGetPhysicalDeviceSurfacePresentModesKHR(surface, frame);
+
+                       VkExtent2D swapchainExtent;
+                       // width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF.
+                       if (surfCapabilities.getCurrentExtent().getWidth() == 0xFFFFFFFF) {
+                               // If the surface size is undefined, the size is set to
+                               // the size of the images requested.
+                               swapchainExtent = VkExtent2D.create(
+                                       clampi(width, surfCapabilities.getMinImageExtent().getWidth(), surfCapabilities.getMaxImageExtent().getWidth()),
+                                       clampi(height, surfCapabilities.getMinImageExtent().getHeight(), surfCapabilities.getMaxImageExtent().getHeight()),
+                                       frame);
+                       } else {
+                               // If the surface size is defined, the swap chain size must match
+                               swapchainExtent = surfCapabilities.getCurrentExtent();
+                       }
+                       int compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+                       int compositeAlphaFlags[] = {
+                               VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
+                               VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
+                               VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
+                               VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,};
+                       for (int flag: compositeAlphaFlags) {
+                               if ((surfCapabilities.getSupportedCompositeAlpha() & flag) != 0) {
+                                       compositeAlpha = flag;
+                                       break;
+                               }
+                       }
+
+                       VkSwapchainCreateInfoKHR chaininfo = VkSwapchainCreateInfoKHR.create(
+                               0,
+                               surface,
+                               surfCapabilities.getMinImageCount(),
+                               format,
+                               VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
+                               swapchainExtent.getWidth(), swapchainExtent.getHeight(),
+                               1, //.imageArrayLayers = 1,
+                               VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+                               VK_SHARING_MODE_EXCLUSIVE,
+                               // assumes queues are same.
+                               0, null,
+                               (surfCapabilities.getSupportedTransforms() & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) != 0
+                               ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : surfCapabilities.getCurrentTransform(),
+                               compositeAlpha,
+                               VK_PRESENT_MODE_FIFO_KHR,
+                               VK_TRUE,
+                               null,
+                               frame);
+
+                       chain = device.vkCreateSwapchainKHR(chaininfo, scope);
+
+                       int chainImageCount;
+
+                       chainImage = device.vkGetSwapchainImagesKHR(chain, (SegmentAllocator)scope, scope);
+                       chainImageCount = (int)chainImage.length();
+                       chainImageView = VkImageView.createArray(chainImageCount, (SegmentAllocator)scope);
+
+                       VkImageViewCreateInfo viewinfo = VkImageViewCreateInfo.create(
+                               0,
+                               null,
+                               VK_IMAGE_VIEW_TYPE_2D,
+                               format,
+                               VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A,
+                               VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1,
+                               frame);
+
+                       for (int i = 0; i < chainImageCount; i++) {
+                               viewinfo.setImage(chainImage.get(i));
+
+                               chainImageView.setAtIndex(i, device.vkCreateImageView(viewinfo, scope));
+                       }
+
+                       chainImageFormat = format;
+               }
+       }
+
+       void init_device_queue() {
+               graphics_queue = device.vkGetDeviceQueue(graphics_queue_index, 0, scope);
+               if (graphics_queue_index == present_queue_index) {
+                       present_queue = graphics_queue;
+               } else {
+                       present_queue = device.vkGetDeviceQueue(present_queue_index, 0, scope);
+               }
+       }
+
+       void init_command() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       VkCommandPoolCreateInfo poolinfo = VkCommandPoolCreateInfo.create(
+                               0,
+                               graphics_queue_index,
+                               frame);
+
+                       cmd_pool = device.vkCreateCommandPool(poolinfo, scope);
+
+                       VkCommandBufferAllocateInfo cmdinfo = VkCommandBufferAllocateInfo.create(
+                               cmd_pool,
+                               VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+                               1,
+                               frame);
+
+                       cmd = device.vkAllocateCommandBuffers(cmdinfo, (SegmentAllocator)scope, scope);
+               }
+       }
+
+       // parameterise as init_image?
+       void init_depth() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       int format = VK_FORMAT_D16_UNORM;
+                       VkMemoryRequirements req = VkMemoryRequirements.create(frame);
+                       VkImageCreateInfo imageinfo = VkImageCreateInfo.create(
+                               0,
+                               VK_IMAGE_TYPE_2D,
+                               format,
+                               width, height, 1,
+                               1,
+                               1,
+                               NUM_SAMPLES,
+                               0,
+                               VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
+                               VK_SHARING_MODE_EXCLUSIVE,
+                               0, null,
+                               VK_IMAGE_LAYOUT_UNDEFINED,
+                               frame);
+
+                       depthImage = device.vkCreateImage(imageinfo, scope);
+
+                       device.vkGetImageMemoryRequirements(depthImage, req);
+                       VkMemoryAllocateInfo alloc = VkMemoryAllocateInfo.create(
+                               req.getSize(),
+                               find_memory_type(memory_properties, req.getMemoryTypeBits(), VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT),
+                               frame);
+
+                       depthMemory = device.vkAllocateMemory(alloc, scope);
+
+                       device.vkBindImageMemory(depthImage, depthMemory, 0);
+
+                       VkImageViewCreateInfo viewinfo = VkImageViewCreateInfo.create(
+                               0,
+                               depthImage,
+                               VK_IMAGE_VIEW_TYPE_2D,
+                               VK_FORMAT_D16_UNORM,
+                               VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A,
+                               VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1,
+                               frame);
+
+                       depthView = device.vkCreateImageView(viewinfo, scope);
+                       depthFormat = format;
+               }
+       }
+
+       void init_uniform() throws Exception {
+               uniform = init_buffer(mvp.length * 4, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, MemorySegment.ofArray(mvp));
+       }
+
+       void init_descriptor() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       HandleArray<VkDescriptorSetLayout> layout_table = VkDescriptorSetLayout.createArray(1, frame);
+                       VkDescriptorSetLayoutBinding layout_binding = VkDescriptorSetLayoutBinding.create(
+                               0,
+                               VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+                               1,
+                               VK_SHADER_STAGE_VERTEX_BIT,
+                               null,
+                               frame);
+                       VkDescriptorSetLayoutCreateInfo descriptor_layout = VkDescriptorSetLayoutCreateInfo.create(
+                               0,
+                               1, layout_binding,
+                               frame);
+
+                       desc_layout = device.vkCreateDescriptorSetLayout(descriptor_layout, scope);
+                       layout_table.setAtIndex(0, desc_layout);
+
+                       VkPipelineLayoutCreateInfo pipeline_info = VkPipelineLayoutCreateInfo.create(
+                               0,
+                               1, layout_table,
+                               0, null,
+                               frame);
+
+                       pipeline_layout = device.vkCreatePipelineLayout(pipeline_info, scope);
+
+                       VkDescriptorPoolSize type_count = VkDescriptorPoolSize.create(
+                               VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+                               1,
+                               frame);
+
+                       VkDescriptorPoolCreateInfo descriptor_pool = VkDescriptorPoolCreateInfo.create(
+                               0,
+                               1,
+                               1, type_count,
+                               frame);
+
+                       desc_pool = device.vkCreateDescriptorPool(descriptor_pool, scope);
+
+                       VkDescriptorSetAllocateInfo alloc_info = VkDescriptorSetAllocateInfo.create(
+                               desc_pool,
+                               1, layout_table,
+                               frame);
+
+                       System.out.println(alloc_info);
+
+                       desc_set = device.vkAllocateDescriptorSets(alloc_info, (SegmentAllocator)scope, scope);
+
+                       VkDescriptorBufferInfo uniformInfo = VkDescriptorBufferInfo.create(uniform.buffer, 0, uniform.size, frame);
+                       VkWriteDescriptorSet writes = VkWriteDescriptorSet.create(
+                               desc_set.getAtIndex(0),
+                               0,
+                               0,
+                               1,
+                               VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+                               null,
+                               uniformInfo,
+                               null,
+                               frame);
+
+                       device.vkUpdateDescriptorSets(1, writes, 0, null);
+               }
+       }
+
+       void init_render() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       VkAttachmentDescription attachments = VkAttachmentDescription.createArray(2, frame);
+
+                       attachments.setFormat(chainImageFormat);
+                       attachments.setSamples(NUM_SAMPLES);
+                       attachments.setLoadOp(VK_ATTACHMENT_LOAD_OP_CLEAR);
+                       attachments.setStoreOp(VK_ATTACHMENT_STORE_OP_STORE);
+                       attachments.setStencilLoadOp(VK_ATTACHMENT_LOAD_OP_DONT_CARE);
+                       attachments.setStencilStoreOp(VK_ATTACHMENT_STORE_OP_DONT_CARE);
+                       attachments.setInitialLayout(VK_IMAGE_LAYOUT_UNDEFINED);
+                       attachments.setFinalLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
+                       attachments.setFlags(0);
+
+                       attachments.setFormatAtIndex(1, depthFormat);
+                       attachments.setSamplesAtIndex(1, NUM_SAMPLES);
+                       attachments.setLoadOpAtIndex(1, VK_ATTACHMENT_LOAD_OP_CLEAR);
+                       attachments.setStoreOpAtIndex(1, VK_ATTACHMENT_STORE_OP_STORE);
+                       attachments.setStencilLoadOpAtIndex(1, VK_ATTACHMENT_LOAD_OP_DONT_CARE);
+                       attachments.setStencilStoreOpAtIndex(1, VK_ATTACHMENT_STORE_OP_DONT_CARE);
+                       attachments.setInitialLayoutAtIndex(1, VK_IMAGE_LAYOUT_UNDEFINED);
+                       attachments.setFinalLayoutAtIndex(1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+                       attachments.setFlagsAtIndex(1, 0);
+
+                       VkAttachmentReference color_reference = VkAttachmentReference.create(
+                               0,
+                               VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+                               frame);
+
+                       VkAttachmentReference depth_reference = VkAttachmentReference.create(
+                               1,
+                               VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+                               frame);
+
+                       VkSubpassDescription subpass = VkSubpassDescription.create(
+                               0,
+                               VK_PIPELINE_BIND_POINT_GRAPHICS,
+                               0, null,
+                               1, color_reference, null, depth_reference,
+                               0, null,
+                               frame);
+
+                       VkRenderPassCreateInfo rp_info = VkRenderPassCreateInfo.create(
+                               0,
+                               2, attachments,
+                               1, subpass,
+                               0, null,
+                               frame);
+
+                       render_pass = device.vkCreateRenderPass(rp_info, scope);
+               }
+       }
+
+       void init_framebuffer() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       HandleArray<VkImageView> attachments = VkImageView.createArray(2, frame);
+
+                       attachments.setAtIndex(1, depthView);
+
+                       VkFramebufferCreateInfo fb_info = VkFramebufferCreateInfo.create(
+                               0,
+                               render_pass,
+                               2, attachments,
+                               width, height, 1,
+                               frame);
+
+                       framebuffers = VkFramebuffer.createArray(chainImage.length(), (SegmentAllocator)scope);
+                       for (int i = 0; i < chainImage.size(); i++) {
+                               attachments.setAtIndex(0, chainImageView.get(i));
+                               framebuffers.setAtIndex(i, device.vkCreateFramebuffer(fb_info, scope));
+                               System.out.printf("framebuffer[%d] = %s\n", i, framebuffers.getAtIndex(i));
+                       }
+               }
+       }
+
+       void init_vertexbuffer() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       vertex = init_buffer(Cube.data.length * 4, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, MemorySegment.ofArray(Cube.data));
+
+                       vertexBuffer.setAtIndex(0, vertex.buffer);
+
+                       /* ***************************************** */
+                       vi_binding.setBinding(0);
+                       vi_binding.setInputRate(VK_VERTEX_INPUT_RATE_VERTEX);
+                       vi_binding.setStride(Cube.dataStride);
+
+                       vi_attribs.setBinding(0);
+                       vi_attribs.setLocation(0);
+                       vi_attribs.setFormat(VK_FORMAT_R32G32B32A32_SFLOAT);
+                       vi_attribs.setOffset(0);
+                       vi_attribs.setBindingAtIndex(1, 0);
+                       vi_attribs.setLocationAtIndex(1, 1);
+                       vi_attribs.setFormatAtIndex(1, VK_FORMAT_R32G32B32A32_SFLOAT);
+                       vi_attribs.setOffsetAtIndex(1, 16);
+               }
+       }
+
+       void init_pipeline() throws Exception {
+               int res;
+               try ( Frame frame = Frame.frame()) {
+                       IntArray dynamicStateEnables = IntArray.create(frame,
+                               VK_DYNAMIC_STATE_VIEWPORT,
+                               VK_DYNAMIC_STATE_SCISSOR);
+
+                       VkPipelineDynamicStateCreateInfo dynamicState = VkPipelineDynamicStateCreateInfo.create(
+                               0,
+                               2, dynamicStateEnables,
+                               frame);
+
+                       VkPipelineVertexInputStateCreateInfo vi = VkPipelineVertexInputStateCreateInfo.create(
+                               0,
+                               (int)vi_binding.length(), vi_binding,
+                               (int)vi_attribs.length(), vi_attribs,
+                               frame);
+
+                       VkPipelineInputAssemblyStateCreateInfo ia = VkPipelineInputAssemblyStateCreateInfo.create(
+                               0,
+                               VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
+                               0,
+                               frame);
+
+                       VkPipelineRasterizationStateCreateInfo rs = VkPipelineRasterizationStateCreateInfo.create(
+                               0,
+                               VK_TRUE,
+                               VK_FALSE,
+                               VK_POLYGON_MODE_FILL,
+                               VK_CULL_MODE_BACK_BIT,
+                               VK_FRONT_FACE_CLOCKWISE,
+                               VK_FALSE,
+                               0.0f,
+                               0.0f,
+                               0.0f,
+                               1.0f,
+                               frame);
+
+                       VkPipelineColorBlendAttachmentState att_state = VkPipelineColorBlendAttachmentState.create(
+                               VK_FALSE,
+                               VK_BLEND_FACTOR_ZERO,
+                               VK_BLEND_FACTOR_ZERO,
+                               VK_BLEND_OP_ADD,
+                               VK_BLEND_FACTOR_ZERO,
+                               VK_BLEND_FACTOR_ZERO,
+                               VK_BLEND_OP_ADD,
+                               0xf,
+                               frame);
+
+                       VkPipelineColorBlendStateCreateInfo cb = VkPipelineColorBlendStateCreateInfo.create(
+                               0,
+                               VK_FALSE,
+                               VK_LOGIC_OP_NO_OP,
+                               1, att_state,
+                               1.0f, 1.0f, 1.0f, 1.0f,
+                               frame);
+
+                       VkPipelineViewportStateCreateInfo vp = VkPipelineViewportStateCreateInfo.create(
+                               0,
+                               1, null,
+                               1, null,
+                               frame);
+
+                       VkPipelineDepthStencilStateCreateInfo ds = VkPipelineDepthStencilStateCreateInfo.create(
+                               0,
+                               VK_TRUE,
+                               VK_TRUE,
+                               VK_COMPARE_OP_LESS_OR_EQUAL,
+                               VK_FALSE,
+                               VK_FALSE,
+                               0.0f,
+                               0.0f,
+                               frame);
+                       VkStencilOpState back = ds.getBack();
+
+                       back.setFailOp(VK_STENCIL_OP_KEEP);
+                       back.setPassOp(VK_STENCIL_OP_KEEP);
+                       back.setCompareOp(VK_COMPARE_OP_ALWAYS);
+                       back.setCompareMask(0);
+                       back.setReference(0);
+                       back.setDepthFailOp(VK_STENCIL_OP_KEEP);
+                       back.setWriteMask(0);
+
+                       VkStencilOpState front = ds.getFront();
+
+                       front.setFailOp(VK_STENCIL_OP_KEEP);
+                       front.setPassOp(VK_STENCIL_OP_KEEP);
+                       front.setCompareOp(VK_COMPARE_OP_ALWAYS);
+                       front.setCompareMask(0);
+                       front.setReference(0);
+                       front.setDepthFailOp(VK_STENCIL_OP_KEEP);
+                       front.setWriteMask(0);
+
+                       VkPipelineMultisampleStateCreateInfo ms = VkPipelineMultisampleStateCreateInfo.create(
+                               0,
+                               NUM_SAMPLES,
+                               0, //.sampleShadingEnable = VK_FALSE,
+                               0.0f,
+                               null,
+                               0, //.alphaToCoverageEnable = VK_FALSE,
+                               0, //.alphaToOneEnable = VK_FALSE,
+                               frame
+                       );
+
+                       VkShaderModuleCreateInfo vsInfo = VkShaderModuleCreateInfo.create(
+                               0,
+                               cube_vs.length() * 4,
+                               cube_vs,
+                               frame);
+                       VkShaderModuleCreateInfo fsInfo = VkShaderModuleCreateInfo.create(
+                               0,
+                               cube_fs.length() * 4,
+                               cube_fs,
+                               frame);
+
+                       shader.setAtIndex(0, device.vkCreateShaderModule(vsInfo, scope));
+                       shader.setAtIndex(1, device.vkCreateShaderModule(fsInfo, scope));
+
+                       VkPipelineShaderStageCreateInfo shaderStages = VkPipelineShaderStageCreateInfo.createArray(2, (SegmentAllocator)scope);
+
+                       shaderStages.setStage(VK_SHADER_STAGE_VERTEX_BIT);
+                       shaderStages.setName("main", (SegmentAllocator)scope);
+                       shaderStages.setModule(shader.get(0));
+
+                       shaderStages.setStageAtIndex(1, VK_SHADER_STAGE_FRAGMENT_BIT);
+                       shaderStages.setNameAtIndex(1, "main", (SegmentAllocator)scope);
+                       shaderStages.setModuleAtIndex(1, shader.get(1));
+
+                       VkGraphicsPipelineCreateInfo pipeline = VkGraphicsPipelineCreateInfo.create(
+                               0,
+                               2, shaderStages,
+                               vi,
+                               ia,
+                               null,
+                               vp,
+                               rs,
+                               ms,
+                               ds,
+                               cb,
+                               dynamicState,
+                               pipeline_layout,
+                               render_pass,
+                               0,
+                               null,
+                               0,
+                               frame);
+
+                       res = device.vkCreateGraphicsPipelines(null, 1, pipeline, this.pipeline);
+
+                       VkSemaphoreCreateInfo seminfo = VkSemaphoreCreateInfo.create(0, frame);
+                       chainSemaphore = device.vkCreateSemaphore(seminfo, scope);
+
+                       VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.create(0, frame);
+                       drawFence = device.vkCreateFence(fenceInfo, scope);
+               }
+       }
+
+       void execute_begin_command_buffer() throws Exception {
+               /* DEPENDS on init_command() */
+               try ( Frame frame = Frame.frame()) {
+                       VkCommandBufferBeginInfo cmd_buf_info = VkCommandBufferBeginInfo.create(
+                               0,
+                               null,
+                               frame);
+
+                       cmd.getAtIndex(0).vkBeginCommandBuffer(cmd_buf_info);
+               }
+       }
+
+       void execute_end_command_buffer() throws Exception {
+               cmd.getAtIndex(0).vkEndCommandBuffer();
+       }
+
+       final static long FENCE_TIMEOUT = 100000000;
+
+       void execute_queue_command_buffer() throws Exception {
+               int res;
+
+               /* Queue the command buffer for execution */
+ /* FIXME: frame shoudl provide or take explicit scope */
+               try ( ResourceScope scope = ResourceScope.newConfinedScope();  Frame frame = Frame.frame()) {
+                       IntArray pipe_stage_flags = IntArray.create(frame, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
+                       VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.create(0, frame);
+                       HandleArray<VkFence> fences = VkFence.createArray(1, frame);
+
+                       fences.setAtIndex(0, device.vkCreateFence(fenceInfo, scope));
+
+                       VkSubmitInfo submit_info = VkSubmitInfo.create(
+                               1, null, pipe_stage_flags,
+                               (int)cmd.length(), cmd,
+                               0, null,
+                               frame);
+
+                       graphics_queue.vkQueueSubmit(1, submit_info, drawFence);
+
+                       do {
+                               res = device.vkWaitForFences(1, fences, 1, FENCE_TIMEOUT);
+                       } while (res == VK_TIMEOUT);
+
+                       device.vkDestroyFence(fences.getAtIndex(0));
+               }
+       }
+
+       void cmd_viewport() {
+               try ( Frame frame = Frame.frame()) {
+                       VkCommandBuffer cmd = this.cmd.getAtIndex(0);
+                       VkViewport viewport = VkViewport.create(
+                               0, 0, width, height, 0.0f, 1.0f,
+                               frame);
+                       cmd.vkCmdSetViewport(0, 1, viewport);
+               }
+       }
+
+       void cmd_scissors() {
+               try ( Frame frame = Frame.frame()) {
+                       VkCommandBuffer cmd = this.cmd.getAtIndex(0);
+                       VkRect2D scissor = VkRect2D.create(0, 0, width, height, frame);
+                       cmd.vkCmdSetScissor(0, 1, scissor);
+               }
+       }
+
+       void cmd_paint() throws Exception {
+               int res;
+               try ( Frame frame = Frame.frame()) {
+                       int chainIndex;
+                       IntArray chainIndices = IntArray.createArray(1, frame);
+                       VkCommandBuffer cmd = this.cmd.getAtIndex(0);
+
+                       device.vkAcquireNextImageKHR(chain, ~0L, chainSemaphore, null, chainIndices);
+                       chainIndex = chainIndices.getAtIndex(0);
+                       LongArray offsets = LongArray.createArray(1, frame);
+
+                       VkClearValue clear_values = VkClearValue.createArray(2, frame);
+                       FloatArray col = clear_values.getColor().getFloat32();
+                       col.setAtIndex(0, 0.2f);
+                       col.setAtIndex(1, 0.2f);
+                       col.setAtIndex(2, 0.2f);
+                       col.setAtIndex(3, 0.2f);
+                       VkClearDepthStencilValue depthStencil = clear_values.getAtIndex(1).getDepthStencil();
+                       depthStencil.setDepth(1.0f);
+                       depthStencil.setStencil(0);
+
+                       System.out.printf("render framebuffer[%d] = %s\n", chainIndex, framebuffers.getAtIndex(chainIndex));
+
+                       VkRenderPassBeginInfo rp_begin = VkRenderPassBeginInfo.create(
+                               render_pass,
+                               framebuffers.getAtIndex(chainIndex),
+                               0, 0, width, height,
+                               2, clear_values,
+                               frame);
+
+                       cmd.vkCmdBeginRenderPass(rp_begin, VK_SUBPASS_CONTENTS_INLINE);
+
+                       cmd.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.getAtIndex(0));
+                       cmd.vkCmdBindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, NUM_DESCRIPTOR_SETS, desc_set, 0, null);
+                       cmd.vkCmdBindVertexBuffers(0, 1, vertexBuffer, offsets);
+
+                       cmd_viewport();
+                       cmd_scissors();
+
+                       cmd.vkCmdDraw(12 * 3, 1, 0, 0);
+                       cmd.vkCmdEndRenderPass();
+
+                       cmd.vkEndCommandBuffer();
+
+                       IntArray pipe_stage_flags = IntArray.create(frame, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
+                       HandleArray<VkSemaphore> semaphores = VkSemaphore.createArray(1, frame);//, chainSemaphore, scope);
+
+                       semaphores.setAtIndex(0, chainSemaphore);
+
+                       VkSubmitInfo submit_info = VkSubmitInfo.create(
+                               1, semaphores, pipe_stage_flags,
+                               (int)this.cmd.length(), this.cmd,
+                               0, null,
+                               frame);
+
+                       HandleArray<VkFence> fences = VkFence.createArray(1, frame);
+
+                       fences.setAtIndex(0, drawFence);
+
+                       // Queue the command buffer for execution
+                       device.vkResetFences(1, fences);
+
+                       graphics_queue.vkQueueSubmit(1, submit_info, drawFence);
+
+                       // Make sure command buffer is finished before presenting
+                       do {
+                               res = device.vkWaitForFences(1, fences, VK_TRUE, FENCE_TIMEOUT);
+                       } while (res == VK_TIMEOUT);
+
+                       // Now present the image in the window
+                       HandleArray<VkSwapchainKHR> chains = VkSwapchainKHR.createArray(1, frame);
+                       chains.setAtIndex(0, chain);
+                       VkPresentInfoKHR present = VkPresentInfoKHR.create(
+                               0, null,
+                               1, chains, chainIndices, null,
+                               frame);
+
+                       present_queue.vkQueuePresentKHR(present);
+               }
+       }
+
+       /**
+        * Buffers are created in three steps:
+        * 1) create buffer, specifying usage and size
+        * 2) allocate memory based on memory requirements
+        * 3) bind memory
+        * <p>
+        */
+       BufferMemory init_buffer(long dataSize, int usage, int properties, MemorySegment init) throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       VkMemoryRequirements req = VkMemoryRequirements.create(frame);
+                       VkBufferCreateInfo buf_info = VkBufferCreateInfo.create(
+                               0,
+                               dataSize,
+                               usage,
+                               VK_SHARING_MODE_EXCLUSIVE,
+                               0, null,
+                               frame);
+
+                       VkBuffer buffer = device.vkCreateBuffer(buf_info, scope);
+
+                       device.vkGetBufferMemoryRequirements(buffer, req);
+
+                       VkMemoryAllocateInfo alloc = VkMemoryAllocateInfo.create(
+                               req.getSize(),
+                               find_memory_type(memory_properties, req.getMemoryTypeBits(), properties),
+                               frame);
+
+                       VkDeviceMemory memory = device.vkAllocateMemory(alloc, scope);
+
+                       if (init != null) {
+                               MemorySegment mem = device.vkMapMemory(memory, 0, dataSize, 0, scope);
+                               mem.copyFrom(init);
+                               device.vkUnmapMemory(memory);
+                       }
+
+                       device.vkBindBufferMemory(buffer, memory, 0);
+
+                       return new BufferMemory(buffer, memory, dataSize);
+               }
+       }
+
+       void shutdown() {
+               device.vkDestroyFence(drawFence);
+               device.vkDestroySemaphore(chainSemaphore);
+
+               device.vkDestroyPipeline(pipeline.getAtIndex(0));
+               for (int i = 0; i < shader.size(); i++)
+                       device.vkDestroyShaderModule(shader.getAtIndex(i));
+
+               vertex.free(device);
+               uniform.free(device);
+
+               for (int i = 0; i < framebuffers.size(); i++)
+                       device.vkDestroyFramebuffer(framebuffers.getAtIndex(i));
+
+               device.vkDestroyRenderPass(render_pass);
+
+               device.vkDestroyDescriptorPool(desc_pool);
+               device.vkDestroyPipelineLayout(pipeline_layout);
+               device.vkDestroyDescriptorSetLayout(desc_layout);
+
+               device.vkDestroyImageView(depthView);
+               device.vkFreeMemory(depthMemory);
+               device.vkDestroyImage(depthImage);
+
+               for (int i = 0; i < chainImageView.size(); i++)
+                       device.vkDestroyImageView(chainImageView.getAtIndex(i));
+
+               device.vkDestroySwapchainKHR(chain);
+
+               device.vkDestroyCommandPool(cmd_pool);
+               device.vkDestroyDevice();
+
+               instance.vkDestroySurfaceKHR(surface);
+               window.close();
+               display.close();
+
+               if (logger != null)
+                       instance.vkDestroyDebugUtilsMessengerEXT(logger);
+               instance.vkDestroyInstance();
+       }
+
+       /**
+        * This finds the memory type index for the memory on a specific device.
+        */
+       static int find_memory_type(VkPhysicalDeviceMemoryProperties memory, int typeMask, int query) {
+               VkMemoryType mtypes = memory.getMemoryTypes();
+
+               for (int i = 0; i < memory.getMemoryTypeCount(); i++) {
+                       if (((1 << i) & typeMask) != 0 && ((mtypes.getPropertyFlagsAtIndex(i) & query) == query))
+                               return i;
+               }
+               return -1;
+       }
+
+       static int clampi(int v, int min, int max) {
+               return v < min ? min : v < max ? v : max;
+       }
+
+       void init_matrices() {
+               float eye[] = new float[]{-5, 3, -10};
+               float centre[] = new float[]{0, 0, 0};
+               float up[] = new float[]{0, -1, 0};
+               float t0[] = new float[16], t1[] = new float[16];
+
+               perspective(projection, (float)(Math.PI * 0.25), 1.0f, 0.1f, 100.0f);
+               lookAt(view, eye, centre, up);
+               identity4f(model);
+               mult4x4f(t0, clip, projection);
+               mult4x4f(t1, t0, view);
+               mult4x4f(mvp, t1, model);
+       }
+
+       void demo() throws Exception {
+               cube_vs = ShaderIO.loadCube_vert((SegmentAllocator)scope);
+               cube_fs = ShaderIO.loadCube_frag((SegmentAllocator)scope);
+
+               init_matrices();
+
+               init_instance();
+               init_debug();
+               init_surface();
+               init_device();
+               init_device_queue();
+
+               init_command();
+               init_depth();
+               init_uniform();
+
+               init_descriptor();
+               init_render();
+               init_framebuffer();
+               init_vertexbuffer();
+               init_pipeline();
+
+               execute_begin_command_buffer();
+
+               cmd_paint();
+
+               System.out.println("Any key to quit");
+               Event e;
+outer:  while ((e = window.nextEvent(true)) != null) {
+                       switch (e.type) {
+                       case Event.KEY:
+                       case Event.CLOSE:
+                               break outer;
+                       }
+               }
+
+               shutdown();
+       }
+
+       public static void main(String[] args) throws Throwable {
+               System.loadLibrary("vulkan");
+               System.loadLibrary("X11");
+
+               new TestCube().demo();
+       }
+}
diff --git a/src/notzed.vulkan.test/classes/vulkan/test/TestMandelbrot.java b/src/notzed.vulkan.test/classes/vulkan/test/TestMandelbrot.java
new file mode 100755 (executable)
index 0000000..cc139fb
--- /dev/null
@@ -0,0 +1,585 @@
+/*
+The MIT License (MIT)
+
+Copyright (C) 2017 Eric Arnebäck
+Copyright (C) 2019 Michael Zucchi
+
+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, sublicense, 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 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 NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS 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.
+
+ */
+
+ /*
+ * This is a Java conversion of a C conversion of this:
+ * https://github.com/Erkaman/vulkan_minimal_compute
+ *
+ * It's been simplified a bit and converted to the 'zvk' api.
+ */
+package vulkan.test;
+
+import java.io.InputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.Channels;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.awt.image.MemoryImageSource;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import jdk.incubator.foreign.*;
+import au.notzed.nativez.*;
+import java.util.ArrayList;
+import java.util.List;
+
+import vulkan.*;
+import static vulkan.Vulkan.*;
+
+public class TestMandelbrot {
+
+       static final boolean debug = true;
+       ResourceScope scope = ResourceScope.newSharedScope();
+
+       int WIDTH = 1920 * 1;
+       int HEIGHT = 1080 * 1;
+
+       VkInstance instance;
+       VkPhysicalDevice physicalDevice;
+
+       VkDevice device;
+       VkQueue computeQueue;
+
+       long dstBufferSize = WIDTH * HEIGHT * 4;
+       //VkBuffer dstBuffer;
+       //VkDeviceMemory dstMemory;
+       BufferMemory dst;
+
+       VkDescriptorSetLayout descriptorSetLayout;
+       VkDescriptorPool descriptorPool;
+       HandleArray<VkDescriptorSet> descriptorSets;
+
+       int computeQueueIndex;
+       VkPhysicalDeviceMemoryProperties deviceMemoryProperties;
+
+       String mandelbrot_entry = "main";
+       IntArray mandelbrot_cs;
+
+       VkShaderModule mandelbrotShader;
+       VkPipelineLayout pipelineLayout;
+       HandleArray<VkPipeline> computePipeline = VkPipeline.createArray(1, (SegmentAllocator)scope);
+
+       VkCommandPool commandPool;
+       HandleArray<VkCommandBuffer> commandBuffers;
+
+       record BufferMemory(VkBuffer buffer, VkDeviceMemory memory) {
+
+       }
+
+       VkDebugUtilsMessengerEXT logger;
+
+       void init_debug() throws Exception {
+               if (!debug)
+                       return;
+               try ( Frame frame = Frame.frame()) {
+                       var cb = PFN_vkDebugUtilsMessengerCallbackEXT.upcall((severity, flags, data) -> {
+                               System.out.printf("Debug: %d: %s\n", severity, data.getMessage());
+                               return 0;
+                       }, scope);
+                       VkDebugUtilsMessengerCreateInfoEXT info = VkDebugUtilsMessengerCreateInfoEXT.create(
+                               0,
+                               //VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
+                               VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
+                               | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
+                               | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
+                               VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
+                               | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
+                               | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
+                               cb,
+                               null,
+                               frame);
+
+                       logger = instance.vkCreateDebugUtilsMessengerEXT(info, scope);
+               }
+       }
+
+       void init_instance() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       VkInstanceCreateInfo info = VkInstanceCreateInfo.create(
+                               0,
+                               VkApplicationInfo.create("test", 1, "test-engine", 2, VK_API_VERSION_1_0, frame),
+                               1,
+                               new String[]{"VK_LAYER_KHRONOS_validation"},
+                               debug ? 1 : 0,
+                               debug ? new String[]{"VK_EXT_debug_utils"} : null,
+                               frame
+                       );
+
+                       instance = VkInstance.vkCreateInstance(info, scope);
+               }
+       }
+
+       void init_device() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       HandleArray<VkPhysicalDevice> devs;
+                       int count;
+                       int res;
+
+                       devs = instance.vkEnumeratePhysicalDevices(frame, scope);
+
+                       int best = 0;
+                       int devid = -1;
+                       int queueid = -1;
+
+                       for (int i = 0; i < devs.length(); i++) {
+                               VkPhysicalDevice dev = devs.getAtIndex(i);
+                               VkQueueFamilyProperties famprops = dev.vkGetPhysicalDeviceQueueFamilyProperties(frame);
+                               int family_count = (int)famprops.length();
+
+                               for (int j = 0; j < family_count; j++) {
+                                       var flags = famprops.getAtIndex(j).getQueueFlags();
+                                       int score = 0;
+
+                                       if ((flags & VK_QUEUE_COMPUTE_BIT) != 0)
+                                               score += 1;
+                                       if ((flags & VK_QUEUE_GRAPHICS_BIT) == 0)
+                                               score += 1;
+
+                                       if (score > best) {
+                                               score = best;
+                                               devid = i;
+                                               queueid = j;
+                                       }
+                               }
+                       }
+
+                       if (devid == -1)
+                               throw new Exception("Cannot find a suitable device");
+
+                       computeQueueIndex = queueid;
+                       physicalDevice = devs.getAtIndex(devid);
+
+                       FloatArray qpri = FloatArray.create(frame, 0.0f);
+                       VkDeviceQueueCreateInfo qinfo = VkDeviceQueueCreateInfo.create(
+                               0,
+                               queueid,
+                               1,
+                               qpri,
+                               frame);
+                       VkDeviceCreateInfo devinfo = VkDeviceCreateInfo.create(
+                               0,
+                               1, qinfo,
+                               0, null,
+                               0, null,
+                               null,
+                               frame);
+
+                       device = physicalDevice.vkCreateDevice(devinfo, scope);
+
+                       System.out.printf("device = %s\n", device.address());
+
+                       // NOTE: app scope
+                       deviceMemoryProperties = VkPhysicalDeviceMemoryProperties.create((SegmentAllocator)scope);
+                       physicalDevice.vkGetPhysicalDeviceMemoryProperties(deviceMemoryProperties);
+
+                       computeQueue = device.vkGetDeviceQueue(queueid, 0, scope);
+               }
+       }
+
+       /**
+        * Buffers are created in three steps:
+        * 1) create buffer, specifying usage and size
+        * 2) allocate memory based on memory requirements
+        * 3) bind memory
+        * <p>
+        */
+       BufferMemory init_buffer(long dataSize, int usage, int properties) throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       VkMemoryRequirements req = VkMemoryRequirements.create(frame);
+                       VkBufferCreateInfo buf_info = VkBufferCreateInfo.create(
+                               0,
+                               dataSize,
+                               usage,
+                               VK_SHARING_MODE_EXCLUSIVE,
+                               0, null,
+                               frame);
+
+                       VkBuffer buffer = device.vkCreateBuffer(buf_info, scope);
+
+                       device.vkGetBufferMemoryRequirements(buffer, req);
+
+                       VkMemoryAllocateInfo alloc = VkMemoryAllocateInfo.create(
+                               req.getSize(),
+                               find_memory_type(deviceMemoryProperties, req.getMemoryTypeBits(), properties),
+                               frame);
+
+                       VkDeviceMemory memory = device.vkAllocateMemory(alloc, scope);
+
+                       device.vkBindBufferMemory(buffer, memory, 0);
+
+                       return new BufferMemory(buffer, memory);
+               }
+       }
+
+       /**
+        * Descriptors are used to bind and describe memory blocks
+        * to shaders.
+        * <p>
+        * *Pool is used to allocate descriptors, it is per-device.
+        * *Layout is used to group descriptors for a given pipeline,
+        * The descriptors describe individually-addressable blocks.
+        */
+       void init_descriptor() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       /* Create descriptorset layout */
+                       VkDescriptorSetLayoutBinding layout_binding = VkDescriptorSetLayoutBinding.create(
+                               0,
+                               VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+                               1,
+                               VK_SHADER_STAGE_COMPUTE_BIT,
+                               null,
+                               frame);
+
+                       VkDescriptorSetLayoutCreateInfo descriptor_layout = VkDescriptorSetLayoutCreateInfo.create(
+                               0,
+                               1, layout_binding,
+                               frame);
+
+                       descriptorSetLayout = device.vkCreateDescriptorSetLayout(descriptor_layout, scope);
+
+                       /* Create descriptor pool */
+                       VkDescriptorPoolSize type_count = VkDescriptorPoolSize.create(
+                               VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+                               1,
+                               frame);
+
+                       VkDescriptorPoolCreateInfo descriptor_pool = VkDescriptorPoolCreateInfo.create(
+                               0,
+                               1,
+                               1, type_count,
+                               frame);
+
+                       descriptorPool = device.vkCreateDescriptorPool(descriptor_pool, scope);
+
+                       /* Allocate from pool */
+                       HandleArray<VkDescriptorSetLayout> layout_table = VkDescriptorSetLayout.createArray(1, frame);
+
+                       layout_table.setAtIndex(0, descriptorSetLayout);
+
+                       VkDescriptorSetAllocateInfo alloc_info = VkDescriptorSetAllocateInfo.create(
+                               descriptorPool,
+                               1, layout_table,
+                               frame);
+
+                       descriptorSets = device.vkAllocateDescriptorSets(alloc_info, (SegmentAllocator)scope, scope);
+
+                       /* Bind a buffer to the descriptor */
+                       VkDescriptorBufferInfo bufferInfo = VkDescriptorBufferInfo.create(
+                               dst.buffer,
+                               0,
+                               dstBufferSize,
+                               frame);
+
+                       VkWriteDescriptorSet writeSet = VkWriteDescriptorSet.create(
+                               descriptorSets.getAtIndex(0),
+                               0,
+                               0,
+                               1,
+                               VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+                               null,
+                               bufferInfo,
+                               null,
+                               frame);
+
+                       System.out.println(writeSet);
+
+                       device.vkUpdateDescriptorSets(1, writeSet, 0, null);
+               }
+       }
+
+       /**
+        * Create the compute pipeline. This is the shader and data layouts for it.
+        */
+       void init_pipeline() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       /* Set shader code */
+                       VkShaderModuleCreateInfo vsInfo = VkShaderModuleCreateInfo.create(
+                               0,
+                               mandelbrot_cs.length() * 4,
+                               mandelbrot_cs,
+                               frame);
+
+                       mandelbrotShader = device.vkCreateShaderModule(vsInfo, scope);
+
+                       /* Link shader to layout */
+                       HandleArray<VkDescriptorSetLayout> layout_table = VkDescriptorSetLayout.createArray(1, frame);
+
+                       layout_table.setAtIndex(0, descriptorSetLayout);
+
+                       VkPipelineLayoutCreateInfo pipelineinfo = VkPipelineLayoutCreateInfo.create(
+                               0,
+                               1, layout_table,
+                               0, null,
+                               frame);
+
+                       pipelineLayout = device.vkCreatePipelineLayout(pipelineinfo, scope);
+
+                       /* Create pipeline */
+                       VkComputePipelineCreateInfo pipeline = VkComputePipelineCreateInfo.create(
+                               0,
+                               pipelineLayout,
+                               null,
+                               0,
+                               frame);
+
+                       VkPipelineShaderStageCreateInfo stage = pipeline.getStage();
+
+                       stage.setStage(VK_SHADER_STAGE_COMPUTE_BIT);
+                       stage.setModule(mandelbrotShader);
+                       stage.setName(mandelbrot_entry, frame);
+
+                       device.vkCreateComputePipelines(null, 1, pipeline, computePipeline);
+               }
+       }
+
+       /**
+        * Create a command buffer, this is somewhat like a display list.
+        */
+       void init_command_buffer() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       VkCommandPoolCreateInfo poolinfo = VkCommandPoolCreateInfo.create(
+                               0,
+                               computeQueueIndex,
+                               frame);
+
+                       commandPool = device.vkCreateCommandPool(poolinfo, scope);
+
+                       VkCommandBufferAllocateInfo cmdinfo = VkCommandBufferAllocateInfo.create(
+                               commandPool,
+                               VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+                               1,
+                               frame);
+
+                       // should it take a scope?
+                       commandBuffers = device.vkAllocateCommandBuffers(cmdinfo, (SegmentAllocator)scope, scope);
+
+                       /* Fill command buffer with commands for later operation */
+                       VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.create(
+                               VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
+                               null,
+                               frame);
+
+                       commandBuffers.get(0).vkBeginCommandBuffer(beginInfo);
+
+                       /* Bind the compute operation and data */
+                       commandBuffers.get(0).vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline.get(0));
+                       commandBuffers.get(0).vkCmdBindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 1, descriptorSets, 0, null);
+
+                       /* Run it */
+                       commandBuffers.get(0).vkCmdDispatch(WIDTH, HEIGHT, 1);
+
+                       commandBuffers.get(0).vkEndCommandBuffer();
+               }
+       }
+
+       /**
+        * Execute the pre-created command buffer.
+        * <p>
+        * A fence is used to wait for completion.
+        */
+       void execute() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       VkSubmitInfo submitInfo = VkSubmitInfo.create(frame);
+
+                       submitInfo.setCommandBufferCount(1);
+                       submitInfo.setCommandBuffers(commandBuffers);
+
+                       /* Create fence to mark the task completion */
+                       VkFence fence;
+                       HandleArray<VkFence> fences = VkFence.createArray(1, frame);
+                       VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.create(frame);
+
+                       // maybe this should take a HandleArray<Fence> rather than being a constructor
+                       // FIXME: some local scope
+                       fence = device.vkCreateFence(fenceInfo, scope);
+                       fences.set(0, fence);
+
+                       /* Await completion */
+                       computeQueue.vkQueueSubmit(1, submitInfo, fence);
+
+                       int VK_TRUE = 1;
+                       int res;
+                       do {
+                               res = device.vkWaitForFences(1, fences, VK_TRUE, 1000000);
+                       } while (res == VK_TIMEOUT);
+
+                       device.vkDestroyFence(fence);
+               }
+       }
+
+       void shutdown() {
+               device.vkDestroyCommandPool(commandPool);
+               device.vkDestroyPipeline(computePipeline.getAtIndex(0));
+               device.vkDestroyPipelineLayout(pipelineLayout);
+               device.vkDestroyShaderModule(mandelbrotShader);
+
+               device.vkDestroyDescriptorPool(descriptorPool);
+               device.vkDestroyDescriptorSetLayout(descriptorSetLayout);
+
+               device.vkFreeMemory(dst.memory());
+               device.vkDestroyBuffer(dst.buffer());
+
+               device.vkDestroyDevice();
+               if (logger != null)
+                       instance.vkDestroyDebugUtilsMessengerEXT(logger);
+               instance.vkDestroyInstance();
+       }
+
+       /**
+        * Accesses the gpu buffer, converts it to RGB byte, and saves it as a pam file.
+        */
+       void save_result() throws Exception {
+               try ( ResourceScope scope = ResourceScope.newConfinedScope()) {
+                       MemorySegment mem = device.vkMapMemory(dst.memory(), 0, dstBufferSize, 0, scope);
+                       byte[] pixels = new byte[WIDTH * HEIGHT * 3];
+
+                       System.out.printf("map %d bytes\n", dstBufferSize);
+
+                       for (int i = 0; i < WIDTH * HEIGHT; i++) {
+                               pixels[i * 3 + 0] = mem.get(Memory.BYTE, i * 4 + 0);
+                               pixels[i * 3 + 1] = mem.get(Memory.BYTE, i * 4 + 1);
+                               pixels[i * 3 + 2] = mem.get(Memory.BYTE, i * 4 + 2);
+                       }
+
+                       device.vkUnmapMemory(dst.memory());
+
+                       pam_save("mandelbrot.pam", WIDTH, HEIGHT, 3, pixels);
+               }
+       }
+
+       void show_result() throws Exception {
+               try ( ResourceScope scope = ResourceScope.newConfinedScope()) {
+                       MemorySegment mem = device.vkMapMemory(dst.memory(), 0, dstBufferSize, 0, scope);
+                       int[] pixels = new int[WIDTH * HEIGHT];
+
+                       System.out.printf("map %d bytes\n", dstBufferSize);
+
+                       MemorySegment.ofArray(pixels).copyFrom(mem);
+
+                       device.vkUnmapMemory(dst.memory());
+
+                       swing_show(WIDTH, HEIGHT, pixels);
+               }
+       }
+
+       /**
+        * Trivial pnm format image output.
+        */
+       void pam_save(String name, int width, int height, int depth, byte[] pixels) throws IOException {
+               try ( FileOutputStream fos = new FileOutputStream(name)) {
+                       fos.write(String.format("P6\n%d\n%d\n255\n", width, height).getBytes());
+                       fos.write(pixels);
+                       System.out.printf("wrote: %s\n", name);
+               }
+       }
+
+       static class DataImage extends JPanel {
+
+               final int w, h, stride;
+               final MemoryImageSource source;
+               final Image image;
+               final int[] pixels;
+
+               public DataImage(int w, int h, int[] pixels) {
+                       this.w = w;
+                       this.h = h;
+                       this.stride = w;
+                       this.pixels = pixels;
+                       this.source = new MemoryImageSource(w, h, pixels, 0, w);
+                       this.source.setAnimated(true);
+                       this.source.setFullBufferUpdates(true);
+                       this.image = Toolkit.getDefaultToolkit().createImage(source);
+               }
+
+               @Override
+               protected void paintComponent(Graphics g) {
+                       super.paintComponent(g);
+                       g.drawImage(image, 0, 0, this);
+               }
+       }
+
+       void swing_show(int w, int h, int[] pixels) {
+               JFrame window;
+               DataImage image = new DataImage(w, h, pixels);
+
+               window = new JFrame("mandelbrot");
+               window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+               window.setContentPane(image);
+               window.setSize(w, h);
+               window.setVisible(true);
+       }
+
+       /**
+        * This finds the memory type index for the memory on a specific device.
+        */
+       static int find_memory_type(VkPhysicalDeviceMemoryProperties memory, int typeMask, int query) {
+               VkMemoryType mtypes = memory.getMemoryTypes();
+
+               for (int i = 0; i < memory.getMemoryTypeCount(); i++) {
+                       if (((1 << i) & typeMask) != 0 && ((mtypes.getAtIndex(i).getPropertyFlags() & query) == query))
+                               return i;
+               }
+               return -1;
+       }
+
+       void demo() throws Exception {
+               mandelbrot_cs = ShaderIO.loadMandelbrot_comp((SegmentAllocator)scope);
+
+               init_instance();
+               init_debug();
+
+               init_device();
+
+               dst = init_buffer(dstBufferSize,
+                       VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+                       VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+
+               init_descriptor();
+
+               init_pipeline();
+               init_command_buffer();
+
+               System.out.printf("Calculating %dx%d\n", WIDTH, HEIGHT);
+               execute();
+               //System.out.println("Saving ...");
+               //save_result();
+               System.out.println("Showing ...");
+               show_result();
+               System.out.println("Done.");
+
+               shutdown();
+       }
+
+       public static void main(String[] args) throws Throwable {
+               System.loadLibrary("vulkan");
+
+               new TestMandelbrot().demo();
+       }
+}
diff --git a/src/notzed.vulkan.test/classes/vulkan/test/TestSDF.java b/src/notzed.vulkan.test/classes/vulkan/test/TestSDF.java
new file mode 100644 (file)
index 0000000..c292253
--- /dev/null
@@ -0,0 +1,1057 @@
+/*
+The MIT License (MIT)
+
+Copyright (C) 2017 Eric Arnebäck
+Copyright (C) 2019 Michael Zucchi
+
+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, sublicense, 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 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 NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS 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.
+
+ */
+
+ /*
+ * This is a Java conversion of a C conversion of this:
+ * https://github.com/Erkaman/vulkan_minimal_compute
+ *
+ * It's been simplified a bit and converted to the 'zvk' api.
+ */
+package vulkan.test;
+
+import au.notzed.display.Display;
+import au.notzed.display.Event;
+import au.notzed.display.Window;
+import jdk.incubator.foreign.*;
+import au.notzed.nativez.*;
+
+import vulkan.*;
+import static vulkan.Vulkan.*;
+
+import static vulkan.test.GLMaths.*;
+
+public class TestSDF {
+
+       static final boolean debug = true;
+
+       final static int NUM_SAMPLES = VK_SAMPLE_COUNT_1_BIT;
+       final static int NUM_DESCRIPTOR_SETS = 1;
+
+       ResourceScope scope = ResourceScope.newSharedScope();
+
+       int width = 800;
+       int height = 800;
+       float projection[] = new float[16];
+       float view[] = new float[16];
+       float model[] = new float[16];
+       float clip[] = new float[]{
+               1.0f, 0.0f, 0.0f, 0.0f,
+               0.0f, -1.0f, 0.0f, 0.0f,
+               0.0f, 0.0f, 0.5f, 0.0f,
+               0.0f, 0.0f, 0.5f, 1.0f
+       };
+       float mvp[] = new float[16];
+
+       VkInstance instance;
+       VkPhysicalDevice physicalDevice;
+       VkPhysicalDeviceMemoryProperties memory_properties;
+       VkPhysicalDeviceFeatures device_features;
+
+       int present_queue_index;
+       int graphics_queue_index;
+
+       VkDevice device;
+       VkSwapchainKHR chain;
+
+       VkQueue graphics_queue;
+       VkQueue present_queue;
+
+       int chainImageFormat;
+       HandleArray<VkImage> chainImage;
+       HandleArray<VkImageView> chainImageView;
+
+       int depthFormat;
+       VkImage depthImage;
+       VkImageView depthView;
+       VkDeviceMemory depthMemory;
+
+       VkCommandPool cmd_pool;
+       HandleArray<VkCommandBuffer> cmd;
+
+       BufferMemory uniform;
+       VkPipelineLayout pipeline_layout;
+
+       VkDescriptorSetLayout desc_layout;
+       VkDescriptorPool desc_pool;
+       HandleArray<VkDescriptorSet> desc_set;
+
+       VkRenderPass render_pass;
+       HandleArray<VkFramebuffer> framebuffers;
+
+       BufferMemory vertex;
+       HandleArray<VkBuffer> vertexBuffer = VkBuffer.createArray(1, (SegmentAllocator)scope);
+       VkVertexInputBindingDescription vi_binding = VkVertexInputBindingDescription.createArray(1, (SegmentAllocator)scope);
+       VkVertexInputAttributeDescription vi_attribs = VkVertexInputAttributeDescription.createArray(1, (SegmentAllocator)scope);
+
+       IntArray sdf_vs;
+       IntArray sdf_fs;
+       HandleArray<VkShaderModule> shader = VkShaderModule.createArray(2, (SegmentAllocator)scope);
+
+       HandleArray<VkPipeline> pipeline = VkPipeline.createArray(1, (SegmentAllocator)scope);
+
+       VkSemaphore chainSemaphore;
+       VkFence drawFence;
+
+       record BufferMemory(VkBuffer buffer, VkDeviceMemory memory, long size) {
+
+               public void free(VkDevice device) {
+                       device.vkFreeMemory(memory);
+                       device.vkDestroyBuffer(buffer);
+               }
+       }
+
+       VkDebugUtilsMessengerEXT logger;
+
+       void init_debug() throws Exception {
+               if (!debug)
+                       return;
+               try ( Frame frame = Frame.frame()) {
+                       var cb = PFN_vkDebugUtilsMessengerCallbackEXT.upcall((severity, flags, data) -> {
+                               System.out.printf("Debug: %d: %s\n", severity, data.getMessage());
+                               return 0;
+                       }, scope);
+                       VkDebugUtilsMessengerCreateInfoEXT info = VkDebugUtilsMessengerCreateInfoEXT.create(
+                               0,
+                               VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
+                               | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
+                               | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
+                               VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
+                               cb,
+                               null,
+                               frame);
+
+                       logger = instance.vkCreateDebugUtilsMessengerEXT(info, scope);
+               }
+
+               //typedef VkBool32 (*PFN_vkDebugUtilsMessengerCallbackEXT)(VkDebugUtilsMessageSeverityFlagBitsEXT, VkDebugUtilsMessageTypeFlagsEXT, const VkDebugUtilsMessengerCallbackDataEXT *, void *);
+       }
+
+       void init_instance() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       VkInstanceCreateInfo info = VkInstanceCreateInfo.create(
+                               0,
+                               VkApplicationInfo.create("cube", 1, "cube-engine", 2, VK_API_VERSION_1_0, frame),
+                               1, new String[]{"VK_LAYER_KHRONOS_validation"},
+                               3, new String[]{"VK_KHR_surface", "VK_KHR_xlib_surface", "VK_EXT_debug_utils"},
+                               frame
+                       );
+
+                       instance = VkInstance.vkCreateInstance(info, scope);
+                       System.out.printf("instance = %s\n", instance);
+               }
+       }
+
+       Display display;
+       Window window;
+       VkSurfaceKHR surface;
+
+       void init_surface() throws Exception {
+               display = Display.createX11Display();
+               window = display.createWindow(width, height);
+               surface = window.createVulkanSurface(instance, scope);
+               System.out.printf("surface: %s\n", surface);
+       }
+
+       void init_device() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       IntArray count$h = IntArray.create(frame, 1);
+                       IntArray present$h = IntArray.create(frame, 1);
+                       HandleArray<VkPhysicalDevice> devs;
+                       int count;
+                       int res;
+
+                       devs = instance.vkEnumeratePhysicalDevices(frame, scope);
+
+                       // Search for device and queue indices
+                       int devid = -1;
+                       int present_queue = -1;
+                       int graphics_queue = -1;
+                       for (int i = 0; i < devs.length(); i++) {
+                               VkPhysicalDevice dev = devs.getAtIndex(i);
+                               VkQueueFamilyProperties famprops;
+
+                               famprops = dev.vkGetPhysicalDeviceQueueFamilyProperties(frame);
+
+                               for (int j = 0; j < famprops.length(); j++) {
+                                       boolean present = dev.vkGetPhysicalDeviceSurfaceSupportKHR(j, surface);
+
+                                       if (present && present_queue == -1)
+                                               present_queue = j;
+                                       if ((famprops.getQueueFlagsAtIndex(j) & VK_QUEUE_GRAPHICS_BIT) != 0) {
+                                               graphics_queue = j;
+                                               if (present) {
+                                                       present_queue = j;
+                                                       break;
+                                               }
+                                       }
+                               }
+                               if (present_queue != -1 && graphics_queue != -1) {
+                                       devid = i;
+                                       break;
+                               }
+                       }
+
+                       if (devid == -1)
+                               throw new Exception("Cannot find a suitable device");
+
+                       physicalDevice = devs.getAtIndex(devid);
+                       present_queue_index = present_queue;
+                       graphics_queue_index = graphics_queue;
+
+                       // NOTE: app scope
+                       memory_properties = VkPhysicalDeviceMemoryProperties.create((SegmentAllocator)scope);
+                       physicalDevice.vkGetPhysicalDeviceMemoryProperties(memory_properties);
+                       device_features = VkPhysicalDeviceFeatures.create((SegmentAllocator)scope);
+                       physicalDevice.vkGetPhysicalDeviceFeatures(device_features);
+
+                       FloatArray qpri = FloatArray.create(frame, 0.0f);
+                       VkDeviceQueueCreateInfo qinfo = VkDeviceQueueCreateInfo.create(
+                               0,
+                               graphics_queue,
+                               1, qpri,
+                               frame);
+                       String[] extensions = {
+                               "VK_KHR_swapchain"
+                       };
+                       VkPhysicalDeviceFeatures features = VkPhysicalDeviceFeatures.create(frame);
+                       features.setDepthClamp(1);
+                       VkDeviceCreateInfo devinfo = VkDeviceCreateInfo.create(
+                               0,
+                               1, qinfo,
+                               0, null,
+                               extensions.length, extensions,
+                               features,
+                               frame);
+
+                       device = physicalDevice.vkCreateDevice(devinfo, scope);
+
+                       System.out.printf("device = %s\n", device);
+
+                       /* ************************************************************** */
+                       int format;
+                       VkSurfaceFormatKHR surfFormats = physicalDevice.vkGetPhysicalDeviceSurfaceFormatsKHR(surface, frame);
+                       // If the format list includes just one entry of VK_FORMAT_UNDEFINED,
+                       // the surface has no preferred format.  Otherwise, at least one
+                       // supported format will be returned.
+                       if (surfFormats.length() == 1 && surfFormats.getFormatAtIndex(0) == VK_FORMAT_UNDEFINED) {
+                               format = VK_FORMAT_B8G8R8A8_UNORM;
+                       } else {
+                               format = surfFormats.getFormatAtIndex(0);
+                       }
+
+                       VkSurfaceCapabilitiesKHR surfCapabilities = VkSurfaceCapabilitiesKHR.create(frame);
+
+                       physicalDevice.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(surface, surfCapabilities);
+                       IntArray presentModes = physicalDevice.vkGetPhysicalDeviceSurfacePresentModesKHR(surface, frame);
+
+                       VkExtent2D swapchainExtent;
+                       // width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF.
+                       if (surfCapabilities.getCurrentExtent().getWidth() == 0xFFFFFFFF) {
+                               // If the surface size is undefined, the size is set to
+                               // the size of the images requested.
+                               swapchainExtent = VkExtent2D.create(
+                                       clampi(width, surfCapabilities.getMinImageExtent().getWidth(), surfCapabilities.getMaxImageExtent().getWidth()),
+                                       clampi(height, surfCapabilities.getMinImageExtent().getHeight(), surfCapabilities.getMaxImageExtent().getHeight()),
+                                       frame);
+                       } else {
+                               // If the surface size is defined, the swap chain size must match
+                               swapchainExtent = surfCapabilities.getCurrentExtent();
+                       }
+                       int compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+                       int compositeAlphaFlags[] = {
+                               VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
+                               VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
+                               VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
+                               VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,};
+                       for (int flag: compositeAlphaFlags) {
+                               if ((surfCapabilities.getSupportedCompositeAlpha() & flag) != 0) {
+                                       compositeAlpha = flag;
+                                       break;
+                               }
+                       }
+
+                       VkSwapchainCreateInfoKHR chaininfo = VkSwapchainCreateInfoKHR.create(
+                               0,
+                               surface,
+                               surfCapabilities.getMinImageCount(),
+                               format,
+                               VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
+                               swapchainExtent.getWidth(), swapchainExtent.getHeight(),
+                               1, //.imageArrayLayers = 1,
+                               VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+                               VK_SHARING_MODE_EXCLUSIVE,
+                               // assumes queues are same.
+                               0, null,
+                               (surfCapabilities.getSupportedTransforms() & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) != 0
+                               ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : surfCapabilities.getCurrentTransform(),
+                               compositeAlpha,
+                               VK_PRESENT_MODE_FIFO_KHR,
+                               VK_TRUE,
+                               null,
+                               frame);
+
+                       chain = device.vkCreateSwapchainKHR(chaininfo, scope);
+
+                       int chainImageCount;
+
+                       chainImage = device.vkGetSwapchainImagesKHR(chain, (SegmentAllocator)scope, scope);
+                       chainImageCount = (int)chainImage.length();
+                       chainImageView = VkImageView.createArray(chainImageCount, (SegmentAllocator)scope);
+
+                       VkImageViewCreateInfo viewinfo = VkImageViewCreateInfo.create(
+                               0,
+                               null,
+                               VK_IMAGE_VIEW_TYPE_2D,
+                               format,
+                               VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A,
+                               VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1,
+                               frame);
+
+                       for (int i = 0; i < chainImageCount; i++) {
+                               viewinfo.setImage(chainImage.get(i));
+
+                               chainImageView.setAtIndex(i, device.vkCreateImageView(viewinfo, scope));
+                       }
+
+                       chainImageFormat = format;
+               }
+       }
+
+       void init_device_queue() {
+               graphics_queue = device.vkGetDeviceQueue(graphics_queue_index, 0, scope);
+               if (graphics_queue_index == present_queue_index) {
+                       present_queue = graphics_queue;
+               } else {
+                       present_queue = device.vkGetDeviceQueue(present_queue_index, 0, scope);
+               }
+       }
+
+       void init_command() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       VkCommandPoolCreateInfo poolinfo = VkCommandPoolCreateInfo.create(
+                               0,
+                               graphics_queue_index,
+                               frame);
+
+                       cmd_pool = device.vkCreateCommandPool(poolinfo, scope);
+
+                       VkCommandBufferAllocateInfo cmdinfo = VkCommandBufferAllocateInfo.create(
+                               cmd_pool,
+                               VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+                               1,
+                               frame);
+
+                       cmd = device.vkAllocateCommandBuffers(cmdinfo, (SegmentAllocator)scope, scope);
+               }
+       }
+
+       // parameterise as init_image?
+       void init_depth() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       int format = VK_FORMAT_D16_UNORM;
+                       VkMemoryRequirements req = VkMemoryRequirements.create(frame);
+                       VkImageCreateInfo imageinfo = VkImageCreateInfo.create(
+                               0,
+                               VK_IMAGE_TYPE_2D,
+                               format,
+                               width, height, 1,
+                               1,
+                               1,
+                               NUM_SAMPLES,
+                               0,
+                               VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
+                               VK_SHARING_MODE_EXCLUSIVE,
+                               0, null,
+                               VK_IMAGE_LAYOUT_UNDEFINED,
+                               frame);
+
+                       depthImage = device.vkCreateImage(imageinfo, scope);
+
+                       device.vkGetImageMemoryRequirements(depthImage, req);
+                       VkMemoryAllocateInfo alloc = VkMemoryAllocateInfo.create(
+                               req.getSize(),
+                               find_memory_type(memory_properties, req.getMemoryTypeBits(), VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT),
+                               frame);
+
+                       depthMemory = device.vkAllocateMemory(alloc, scope);
+
+                       device.vkBindImageMemory(depthImage, depthMemory, 0);
+
+                       VkImageViewCreateInfo viewinfo = VkImageViewCreateInfo.create(
+                               0,
+                               depthImage,
+                               VK_IMAGE_VIEW_TYPE_2D,
+                               VK_FORMAT_D16_UNORM,
+                               VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A,
+                               VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1,
+                               frame);
+
+                       depthView = device.vkCreateImageView(viewinfo, scope);
+                       depthFormat = format;
+               }
+       }
+
+       void init_uniform() throws Exception {
+               uniform = init_buffer(mvp.length * 4, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, MemorySegment.ofArray(mvp));
+       }
+
+       void init_descriptor() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       HandleArray<VkDescriptorSetLayout> layout_table = VkDescriptorSetLayout.createArray(1, frame);
+                       VkDescriptorSetLayoutBinding layout_binding = VkDescriptorSetLayoutBinding.create(
+                               0,
+                               VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+                               1,
+                               VK_SHADER_STAGE_VERTEX_BIT,
+                               null,
+                               frame);
+                       VkDescriptorSetLayoutCreateInfo descriptor_layout = VkDescriptorSetLayoutCreateInfo.create(
+                               0,
+                               1, layout_binding,
+                               frame);
+
+                       desc_layout = device.vkCreateDescriptorSetLayout(descriptor_layout, scope);
+                       layout_table.setAtIndex(0, desc_layout);
+
+                       VkPipelineLayoutCreateInfo pipeline_info = VkPipelineLayoutCreateInfo.create(
+                               0,
+                               1, layout_table,
+                               0, null,
+                               frame);
+
+                       pipeline_layout = device.vkCreatePipelineLayout(pipeline_info, scope);
+
+                       VkDescriptorPoolSize type_count = VkDescriptorPoolSize.create(
+                               VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+                               1,
+                               frame);
+
+                       VkDescriptorPoolCreateInfo descriptor_pool = VkDescriptorPoolCreateInfo.create(
+                               0,
+                               1,
+                               1, type_count,
+                               frame);
+
+                       desc_pool = device.vkCreateDescriptorPool(descriptor_pool, scope);
+
+                       VkDescriptorSetAllocateInfo alloc_info = VkDescriptorSetAllocateInfo.create(
+                               desc_pool,
+                               1, layout_table,
+                               frame);
+
+                       System.out.println(alloc_info);
+
+                       desc_set = device.vkAllocateDescriptorSets(alloc_info, (SegmentAllocator)scope, scope);
+
+                       VkDescriptorBufferInfo uniformInfo = VkDescriptorBufferInfo.create(uniform.buffer, 0, uniform.size, frame);
+                       VkWriteDescriptorSet writes = VkWriteDescriptorSet.create(
+                               desc_set.getAtIndex(0),
+                               0,
+                               0,
+                               1,
+                               VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+                               null,
+                               uniformInfo,
+                               null,
+                               frame);
+
+                       device.vkUpdateDescriptorSets(1, writes, 0, null);
+               }
+       }
+
+       void init_render() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       VkAttachmentDescription attachments = VkAttachmentDescription.createArray(2, frame);
+
+                       attachments.setFormat(chainImageFormat);
+                       attachments.setSamples(NUM_SAMPLES);
+                       attachments.setLoadOp(VK_ATTACHMENT_LOAD_OP_CLEAR);
+                       attachments.setStoreOp(VK_ATTACHMENT_STORE_OP_STORE);
+                       attachments.setStencilLoadOp(VK_ATTACHMENT_LOAD_OP_DONT_CARE);
+                       attachments.setStencilStoreOp(VK_ATTACHMENT_STORE_OP_DONT_CARE);
+                       attachments.setInitialLayout(VK_IMAGE_LAYOUT_UNDEFINED);
+                       attachments.setFinalLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
+                       attachments.setFlags(0);
+
+                       attachments.setFormatAtIndex(1, depthFormat);
+                       attachments.setSamplesAtIndex(1, NUM_SAMPLES);
+                       attachments.setLoadOpAtIndex(1, VK_ATTACHMENT_LOAD_OP_CLEAR);
+                       attachments.setStoreOpAtIndex(1, VK_ATTACHMENT_STORE_OP_STORE);
+                       attachments.setStencilLoadOpAtIndex(1, VK_ATTACHMENT_LOAD_OP_DONT_CARE);
+                       attachments.setStencilStoreOpAtIndex(1, VK_ATTACHMENT_STORE_OP_DONT_CARE);
+                       attachments.setInitialLayoutAtIndex(1, VK_IMAGE_LAYOUT_UNDEFINED);
+                       attachments.setFinalLayoutAtIndex(1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+                       attachments.setFlagsAtIndex(1, 0);
+
+                       VkAttachmentReference color_reference = VkAttachmentReference.create(
+                               0,
+                               VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+                               frame);
+
+                       VkAttachmentReference depth_reference = VkAttachmentReference.create(
+                               1,
+                               VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+                               frame);
+
+                       VkSubpassDescription subpass = VkSubpassDescription.create(
+                               0,
+                               VK_PIPELINE_BIND_POINT_GRAPHICS,
+                               0, null,
+                               1, color_reference,
+                               null,
+                               depth_reference,
+                               0, null,
+                               frame);
+
+                       VkRenderPassCreateInfo rp_info = VkRenderPassCreateInfo.create(
+                               0,
+                               2, attachments,
+                               1, subpass,
+                               0, null,
+                               frame);
+
+                       render_pass = device.vkCreateRenderPass(rp_info, scope);
+               }
+       }
+
+       void init_framebuffer() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       HandleArray<VkImageView> attachments = VkImageView.createArray(2, frame);
+
+                       attachments.setAtIndex(1, depthView);
+
+                       VkFramebufferCreateInfo fb_info = VkFramebufferCreateInfo.create(
+                               0,
+                               render_pass,
+                               2, attachments,
+                               width, height, 1,
+                               frame);
+
+                       framebuffers = VkFramebuffer.createArray(chainImage.length(), (SegmentAllocator)scope);
+                       for (int i = 0; i < chainImage.size(); i++) {
+                               attachments.setAtIndex(0, chainImageView.get(i));
+                               framebuffers.setAtIndex(i, device.vkCreateFramebuffer(fb_info, scope));
+                               System.out.printf("framebuffer[%d] = %s\n", i, framebuffers.getAtIndex(i));
+                       }
+               }
+       }
+
+       void init_vertexbuffer() throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       float[] image = {
+                               -1, -1, 0, 1,
+                               1, -1, 0, 1,
+                               -1, 1, 0, 1,
+                               -1, 1, 0, 1,
+                               1, -1, 0, 1,
+                               1, 1, 0, 1
+                       //      1, -1, 0, 1,
+                       //      -1, 1, 0, 1,
+                       //      1, 1, 0, 1
+                       };
+                       vertex = init_buffer(image.length * 4, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, MemorySegment.ofArray(image));
+
+                       vertexBuffer.setAtIndex(0, vertex.buffer);
+
+                       /* ***************************************** */
+                       vi_binding.setBinding(0);
+                       vi_binding.setInputRate(VK_VERTEX_INPUT_RATE_VERTEX);
+                       vi_binding.setStride(4 * 4);
+
+                       vi_attribs.setBinding(0);
+                       vi_attribs.setLocation(0);
+                       vi_attribs.setFormat(VK_FORMAT_R32G32B32A32_SFLOAT);
+                       vi_attribs.setOffset(0);
+                       //vi_attribs.setBindingAtIndex(1, 0);
+                       //vi_attribs.setLocationAtIndex(1, 1);
+                       //vi_attribs.setFormatAtIndex(1, VK_FORMAT_R32G32B32A32_SFLOAT);
+                       //vi_attribs.setOffsetAtIndex(1, 16);
+               }
+       }
+
+       void init_pipeline() throws Exception {
+               int res;
+               try ( Frame frame = Frame.frame()) {
+                       IntArray dynamicStateEnables = IntArray.create(frame,
+                               VK_DYNAMIC_STATE_VIEWPORT,
+                               VK_DYNAMIC_STATE_SCISSOR);
+
+                       VkPipelineDynamicStateCreateInfo dynamicState = VkPipelineDynamicStateCreateInfo.create(
+                               0,
+                               2, dynamicStateEnables,
+                               frame);
+
+                       VkPipelineVertexInputStateCreateInfo vi = VkPipelineVertexInputStateCreateInfo.create(
+                               0,
+                               (int)vi_binding.length(), vi_binding,
+                               (int)vi_attribs.length(), vi_attribs,
+                               frame);
+
+                       VkPipelineInputAssemblyStateCreateInfo ia = VkPipelineInputAssemblyStateCreateInfo.create(
+                               0,
+                               VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
+                               0,
+                               frame);
+
+                       VkPipelineRasterizationStateCreateInfo rs = VkPipelineRasterizationStateCreateInfo.create(
+                               0,
+                               VK_TRUE,
+                               VK_FALSE,
+                               VK_POLYGON_MODE_FILL,
+                               VK_CULL_MODE_BACK_BIT,
+                               VK_FRONT_FACE_CLOCKWISE,
+                               VK_FALSE,
+                               0.0f,
+                               0.0f,
+                               0.0f,
+                               1.0f,
+                               frame);
+
+                       VkPipelineColorBlendAttachmentState att_state = VkPipelineColorBlendAttachmentState.create(
+                               VK_FALSE,
+                               VK_BLEND_FACTOR_ZERO,
+                               VK_BLEND_FACTOR_ZERO,
+                               VK_BLEND_OP_ADD,
+                               VK_BLEND_FACTOR_ZERO,
+                               VK_BLEND_FACTOR_ZERO,
+                               VK_BLEND_OP_ADD,
+                               0xf,
+                               frame);
+
+                       VkPipelineColorBlendStateCreateInfo cb = VkPipelineColorBlendStateCreateInfo.create(
+                               0,
+                               VK_FALSE,
+                               VK_LOGIC_OP_NO_OP,
+                               1, att_state,
+                               1.0f, 1.0f, 1.0f, 1.0f,
+                               frame);
+
+                       VkPipelineViewportStateCreateInfo vp = VkPipelineViewportStateCreateInfo.create(
+                               0,
+                               1, null,
+                               1, null,
+                               frame);
+
+                       VkPipelineDepthStencilStateCreateInfo ds = VkPipelineDepthStencilStateCreateInfo.create(
+                               0,
+                               VK_TRUE,
+                               VK_TRUE,
+                               VK_COMPARE_OP_LESS_OR_EQUAL,
+                               VK_FALSE,
+                               VK_FALSE,
+                               0.0f,
+                               0.0f,
+                               frame);
+                       VkStencilOpState back = ds.getBack();
+
+                       back.setFailOp(VK_STENCIL_OP_KEEP);
+                       back.setPassOp(VK_STENCIL_OP_KEEP);
+                       back.setCompareOp(VK_COMPARE_OP_ALWAYS);
+                       back.setCompareMask(0);
+                       back.setReference(0);
+                       back.setDepthFailOp(VK_STENCIL_OP_KEEP);
+                       back.setWriteMask(0);
+
+                       VkStencilOpState front = ds.getFront();
+
+                       front.setFailOp(VK_STENCIL_OP_KEEP);
+                       front.setPassOp(VK_STENCIL_OP_KEEP);
+                       front.setCompareOp(VK_COMPARE_OP_ALWAYS);
+                       front.setCompareMask(0);
+                       front.setReference(0);
+                       front.setDepthFailOp(VK_STENCIL_OP_KEEP);
+                       front.setWriteMask(0);
+
+                       VkPipelineMultisampleStateCreateInfo ms = VkPipelineMultisampleStateCreateInfo.create(
+                               0,
+                               NUM_SAMPLES,
+                               0, //.sampleShadingEnable = VK_FALSE,
+                               0.0f,
+                               null,
+                               0, //.alphaToCoverageEnable = VK_FALSE,
+                               0, //.alphaToOneEnable = VK_FALSE,
+                               frame
+                       );
+
+                       VkShaderModuleCreateInfo vsInfo = VkShaderModuleCreateInfo.create(
+                               0,
+                               sdf_vs.length() * 4,
+                               sdf_vs,
+                               frame);
+                       VkShaderModuleCreateInfo fsInfo = VkShaderModuleCreateInfo.create(
+                               0,
+                               sdf_fs.length() * 4,
+                               sdf_fs,
+                               frame);
+
+                       shader.setAtIndex(0, device.vkCreateShaderModule(vsInfo, scope));
+                       shader.setAtIndex(1, device.vkCreateShaderModule(fsInfo, scope));
+
+                       VkPipelineShaderStageCreateInfo shaderStages = VkPipelineShaderStageCreateInfo.createArray(2, (SegmentAllocator)scope);
+
+                       shaderStages.setStage(VK_SHADER_STAGE_VERTEX_BIT);
+                       shaderStages.setName("main", (SegmentAllocator)scope);
+                       shaderStages.setModule(shader.get(0));
+
+                       shaderStages.setStageAtIndex(1, VK_SHADER_STAGE_FRAGMENT_BIT);
+                       shaderStages.setNameAtIndex(1, "main", (SegmentAllocator)scope);
+                       shaderStages.setModuleAtIndex(1, shader.get(1));
+
+                       VkGraphicsPipelineCreateInfo pipeline = VkGraphicsPipelineCreateInfo.create(
+                               0,
+                               2, shaderStages,
+                               vi,
+                               ia,
+                               null,
+                               vp,
+                               rs,
+                               ms,
+                               ds,
+                               cb,
+                               dynamicState,
+                               pipeline_layout,
+                               render_pass,
+                               0,
+                               null,
+                               0,
+                               frame);
+
+                       res = device.vkCreateGraphicsPipelines(null, 1, pipeline, this.pipeline);
+
+                       VkSemaphoreCreateInfo seminfo = VkSemaphoreCreateInfo.create(0, frame);
+                       chainSemaphore = device.vkCreateSemaphore(seminfo, scope);
+
+                       VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.create(0, frame);
+                       drawFence = device.vkCreateFence(fenceInfo, scope);
+               }
+       }
+
+       void execute_begin_command_buffer() throws Exception {
+               /* DEPENDS on init_command() */
+               try ( Frame frame = Frame.frame()) {
+                       VkCommandBufferBeginInfo cmd_buf_info = VkCommandBufferBeginInfo.create(
+                               0,
+                               null,
+                               frame);
+
+                       cmd.getAtIndex(0).vkBeginCommandBuffer(cmd_buf_info);
+               }
+       }
+
+       void execute_end_command_buffer() throws Exception {
+               cmd.getAtIndex(0).vkEndCommandBuffer();
+       }
+
+       final static long FENCE_TIMEOUT = 100000000;
+
+       void execute_queue_command_buffer() throws Exception {
+               int res;
+
+               /* Queue the command buffer for execution */
+ /* FIXME: frame shoudl provide or take explicit scope */
+               try ( ResourceScope scope = ResourceScope.newConfinedScope();  Frame frame = Frame.frame()) {
+                       IntArray pipe_stage_flags = IntArray.create(frame, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
+                       VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.create(0, frame);
+                       HandleArray<VkFence> fences = VkFence.createArray(1, frame);
+
+                       fences.setAtIndex(0, device.vkCreateFence(fenceInfo, scope));
+
+                       VkSubmitInfo submit_info = VkSubmitInfo.create(
+                               1, null, pipe_stage_flags,
+                               (int)cmd.length(), cmd,
+                               0, null,
+                               frame);
+
+                       graphics_queue.vkQueueSubmit(1, submit_info, drawFence);
+
+                       do {
+                               res = device.vkWaitForFences(1, fences, 1, FENCE_TIMEOUT);
+                       } while (res == VK_TIMEOUT);
+
+                       device.vkDestroyFence(fences.getAtIndex(0));
+               }
+       }
+
+       void cmd_viewport() {
+               try ( Frame frame = Frame.frame()) {
+                       VkCommandBuffer cmd = this.cmd.getAtIndex(0);
+                       VkViewport viewport = VkViewport.create(
+                               0, 0, width, height, 0.0f, 1.0f,
+                               frame);
+                       cmd.vkCmdSetViewport(0, 1, viewport);
+               }
+       }
+
+       void cmd_scissors() {
+               try ( Frame frame = Frame.frame()) {
+                       VkCommandBuffer cmd = this.cmd.getAtIndex(0);
+                       VkRect2D scissor = VkRect2D.create(frame);
+                       VkExtent2D extent = scissor.getExtent();
+
+                       extent.setWidth(width);
+                       extent.setHeight(height);
+
+                       cmd.vkCmdSetScissor(0, 1, scissor);
+               }
+       }
+
+       void cmd_paint() throws Exception {
+               int res;
+               try ( Frame frame = Frame.frame()) {
+                       int chainIndex;
+                       IntArray chainIndices = IntArray.createArray(1, frame);
+                       VkCommandBuffer cmd = this.cmd.getAtIndex(0);
+
+                       device.vkAcquireNextImageKHR(chain, ~0L, chainSemaphore, null, chainIndices);
+                       chainIndex = chainIndices.getAtIndex(0);
+                       LongArray offsets = LongArray.createArray(1, frame);
+
+                       VkClearValue clear_values = VkClearValue.createArray(2, frame);
+                       clear_values.getColor().setFloat32(0.2f, 0.2f, 0.2f, 0.2f, frame);
+                       VkClearDepthStencilValue depthStencil = clear_values.getAtIndex(1).getDepthStencil();
+                       depthStencil.setDepth(1.0f);
+                       depthStencil.setStencil(0);
+
+                       System.out.printf("render framebuffer[%d] = %s\n", chainIndex, framebuffers.getAtIndex(chainIndex));
+
+                       VkRenderPassBeginInfo rp_begin = VkRenderPassBeginInfo.create(
+                               render_pass,
+                               framebuffers.getAtIndex(chainIndex),
+                               0, 0, width, height,
+                               2, clear_values,
+                               frame);
+
+                       cmd.vkCmdBeginRenderPass(rp_begin, VK_SUBPASS_CONTENTS_INLINE);
+
+                       cmd.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.getAtIndex(0));
+                       cmd.vkCmdBindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, NUM_DESCRIPTOR_SETS, desc_set, 0, null);
+                       cmd.vkCmdBindVertexBuffers(0, 1, vertexBuffer, offsets);
+
+                       cmd_viewport();
+                       cmd_scissors();
+
+                       cmd.vkCmdDraw(12 * 3, 1, 0, 0);
+                       cmd.vkCmdEndRenderPass();
+
+                       cmd.vkEndCommandBuffer();
+
+                       IntArray pipe_stage_flags = IntArray.create(frame, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
+                       HandleArray<VkSemaphore> semaphores = VkSemaphore.createArray(1, frame);//, chainSemaphore, scope);
+
+                       semaphores.setAtIndex(0, chainSemaphore);
+
+                       VkSubmitInfo submit_info = VkSubmitInfo.create(
+                               1, semaphores, pipe_stage_flags,
+                               (int)this.cmd.length(), this.cmd,
+                               0, null,
+                               frame);
+
+                       HandleArray<VkFence> fences = VkFence.createArray(1, frame);
+
+                       fences.setAtIndex(0, drawFence);
+
+                       // Queue the command buffer for execution
+                       device.vkResetFences(1, fences);
+
+                       graphics_queue.vkQueueSubmit(1, submit_info, drawFence);
+
+                       // Make sure command buffer is finished before presenting
+                       do {
+                               res = device.vkWaitForFences(1, fences, VK_TRUE, FENCE_TIMEOUT);
+                       } while (res == VK_TIMEOUT);
+
+                       // Now present the image in the window
+                       HandleArray<VkSwapchainKHR> chains = VkSwapchainKHR.createArray(1, frame);
+                       chains.setAtIndex(0, chain);
+                       VkPresentInfoKHR present = VkPresentInfoKHR.create(
+                               0, null,
+                               1, chains, chainIndices, null,
+                               frame);
+
+                       present_queue.vkQueuePresentKHR(present);
+               }
+       }
+
+       /**
+        * Buffers are created in three steps:
+        * 1) create buffer, specifying usage and size
+        * 2) allocate memory based on memory requirements
+        * 3) bind memory
+        * <p>
+        */
+       BufferMemory init_buffer(long dataSize, int usage, int properties, MemorySegment init) throws Exception {
+               try ( Frame frame = Frame.frame()) {
+                       VkMemoryRequirements req = VkMemoryRequirements.create(frame);
+                       VkBufferCreateInfo buf_info = VkBufferCreateInfo.create(
+                               0,
+                               dataSize,
+                               usage,
+                               VK_SHARING_MODE_EXCLUSIVE,
+                               0, null,
+                               frame);
+
+                       VkBuffer buffer = device.vkCreateBuffer(buf_info, scope);
+
+                       device.vkGetBufferMemoryRequirements(buffer, req);
+
+                       VkMemoryAllocateInfo alloc = VkMemoryAllocateInfo.create(
+                               req.getSize(),
+                               find_memory_type(memory_properties, req.getMemoryTypeBits(), properties),
+                               frame);
+
+                       VkDeviceMemory memory = device.vkAllocateMemory(alloc, scope);
+
+                       if (init != null) {
+                               MemorySegment mem = device.vkMapMemory(memory, 0, dataSize, 0, scope);
+                               mem.copyFrom(init);
+                               device.vkUnmapMemory(memory);
+                       }
+
+                       device.vkBindBufferMemory(buffer, memory, 0);
+
+                       return new BufferMemory(buffer, memory, dataSize);
+               }
+       }
+
+       void shutdown() {
+               device.vkDestroyFence(drawFence);
+               device.vkDestroySemaphore(chainSemaphore);
+
+               device.vkDestroyPipeline(pipeline.getAtIndex(0));
+               for (int i = 0; i < shader.size(); i++)
+                       device.vkDestroyShaderModule(shader.getAtIndex(i));
+
+               vertex.free(device);
+               uniform.free(device);
+
+               for (int i = 0; i < framebuffers.size(); i++)
+                       device.vkDestroyFramebuffer(framebuffers.getAtIndex(i));
+
+               device.vkDestroyRenderPass(render_pass);
+
+               device.vkDestroyDescriptorPool(desc_pool);
+               device.vkDestroyPipelineLayout(pipeline_layout);
+               device.vkDestroyDescriptorSetLayout(desc_layout);
+
+               device.vkDestroyImageView(depthView);
+               device.vkFreeMemory(depthMemory);
+               device.vkDestroyImage(depthImage);
+
+               for (int i = 0; i < chainImageView.size(); i++)
+                       device.vkDestroyImageView(chainImageView.getAtIndex(i));
+
+               device.vkDestroySwapchainKHR(chain);
+
+               device.vkDestroyCommandPool(cmd_pool);
+               device.vkDestroyDevice();
+
+               instance.vkDestroySurfaceKHR(surface);
+               window.close();
+               display.close();
+
+               if (logger != null)
+                       instance.vkDestroyDebugUtilsMessengerEXT(logger);
+               instance.vkDestroyInstance();
+       }
+
+       /**
+        * This finds the memory type index for the memory on a specific device.
+        */
+       static int find_memory_type(VkPhysicalDeviceMemoryProperties memory, int typeMask, int query) {
+               VkMemoryType mtypes = memory.getMemoryTypes();
+
+               for (int i = 0; i < memory.getMemoryTypeCount(); i++) {
+                       if (((1 << i) & typeMask) != 0 && ((mtypes.getAtIndex(i).getPropertyFlags() & query) == query))
+                               return i;
+               }
+               return -1;
+       }
+
+       static int clampi(int v, int min, int max) {
+               return v < min ? min : v < max ? v : max;
+       }
+
+       void init_matrices() {
+               //float eye[] = new float[] {-5, 3, -10};
+               float eye[] = new float[]{0, 0, -10};
+               float centre[] = new float[]{0, 0, 0};
+               float up[] = new float[]{0, -1, 0};
+               float t0[] = new float[16], t1[] = new float[16];
+
+               perspective(projection, (float)(Math.PI * 0.25), 1.0f, 0.1f, 100.0f);
+               lookAt(view, eye, centre, up);
+               identity4f(model);
+               mult4x4f(t0, clip, projection);
+               mult4x4f(t1, t0, view);
+               mult4x4f(mvp, t1, model);
+       }
+
+       void demo() throws Exception {
+               sdf_vs = ShaderIO.loadSdf_vert((SegmentAllocator)scope);
+               sdf_fs = ShaderIO.loadSdf_frag((SegmentAllocator)scope);
+
+               init_matrices();
+
+               init_instance();
+               init_debug();
+
+               init_surface();
+               init_device();
+               init_device_queue();
+               init_command();
+               init_depth();
+               init_uniform();
+
+               init_descriptor();
+               init_render();
+               init_framebuffer();
+               init_vertexbuffer();
+               init_pipeline();
+
+               execute_begin_command_buffer();
+
+               cmd_paint();
+
+               System.out.println("Any key to quit");
+               Event e;
+outer:  while ((e = window.nextEvent(true)) != null) {
+                       switch (e.type) {
+                       case Event.KEY:
+                       case Event.CLOSE:
+                               break outer;
+                       }
+               }
+
+               shutdown();
+       }
+
+       public static void main(String[] args) throws Throwable {
+               System.loadLibrary("vulkan");
+               System.loadLibrary("X11");
+
+               new TestSDF().demo();
+       }
+}
diff --git a/src/notzed.vulkan.test/classes/vulkan/test/Tutorial.java b/src/notzed.vulkan.test/classes/vulkan/test/Tutorial.java
new file mode 100644 (file)
index 0000000..09d77b7
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+       Going through the Vulkan Tutorial.
+*/
+package vulkan.test;
+
+import au.notzed.display.Display;
+import au.notzed.display.Event;
+import au.notzed.display.Window;
+import au.notzed.nativez.Frame;
+
+import au.notzed.nativez.HandleArray;
+import au.notzed.nativez.IntArray;
+import au.notzed.nativez.LongArray;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+import jdk.incubator.foreign.MemorySegment;
+import jdk.incubator.foreign.ResourceScope;
+import jdk.incubator.foreign.SegmentAllocator;
+import vulkan.VkBuffer;
+import vulkan.VkClearValue;
+import vulkan.VkCommandBuffer;
+import vulkan.VkCommandBufferBeginInfo;
+import vulkan.VkDevice;
+import vulkan.VkFence;
+import vulkan.VkFramebuffer;
+import vulkan.VkInstance;
+import vulkan.VkPhysicalDeviceFeatures;
+import vulkan.VkPipeline;
+import vulkan.VkPipelineLayout;
+import vulkan.VkPresentInfoKHR;
+import vulkan.VkRect2D;
+import vulkan.VkRenderPassBeginInfo;
+import vulkan.VkSemaphore;
+import vulkan.VkShaderModule;
+import vulkan.VkSubmitInfo;
+import vulkan.VkSurfaceKHR;
+import vulkan.VkSwapchainKHR;
+import vulkan.VkViewport;
+import vulkan.Vulkan;
+import static vulkan.Vulkan.VK_API_VERSION_1_2;
+import static vulkan.Vulkan.VK_FENCE_CREATE_SIGNALED_BIT;
+import static vulkan.Vulkan.VK_KHR_SWAPCHAIN_EXTENSION_NAME;
+import static vulkan.Vulkan.VK_PIPELINE_BIND_POINT_GRAPHICS;
+import static vulkan.Vulkan.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+import static vulkan.Vulkan.VK_PRESENT_MODE_FIFO_KHR;
+import static vulkan.Vulkan.VK_SUBOPTIMAL_KHR;
+import static vulkan.Vulkan.VK_SUBPASS_CONTENTS_INLINE;
+import static vulkan.Vulkan.VK_SUCCESS;
+import static vulkan.Vulkan.VK_TRUE;
+//import static vulkan.Vulkan.*;
+
+public class Tutorial {
+
+       static int width = 724, height = 580;
+       static final int MAX_FRAMES_INFLIGHT = 2;
+
+       static String[] deviceTypes = {
+               "VK_PHYSICAL_DEVICE_TYPE_OTHER",
+               "VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU",
+               "VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU",
+               "VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU",
+               "VK_PHYSICAL_DEVICE_TYPE_CPU"
+       };
+
+       static final float[] vertices = {
+               0.0f, -0.5f, 1, 0, 0,
+               0.5f, 0.5f, 0, 1, 0,
+               -0.5f, 0.5f, 0, 0, 1
+       };
+
+       public static record RenderInfo(
+               Demo.DemoDevice device,
+               Demo.DemoChain chain,
+               VkPipeline pipeline,
+               VkPipelineLayout pipelineLayout,
+               HandleArray<VkCommandBuffer> commandBuffers,
+               HandleArray<VkFramebuffer> frameBuffers,
+               Runnable[] resources,
+               ResourceScope scope) {
+
+               public void close() {
+                       VkDevice d = device.device();
+                       frameBuffers.stream().forEach(d::vkDestroyFramebuffer);
+                       d.vkFreeCommandBuffers(device.pools()[0], commandBuffers.size(), commandBuffers);
+                       d.vkDestroyPipeline(pipeline);
+                       d.vkDestroyPipelineLayout(pipelineLayout);
+
+                       for (int i = resources.length - 1; i >= 0; i--)
+                               resources[i].run();
+
+                       // here? chain?
+                       d.vkDestroyRenderPass(chain.renderPass());
+                       chain.views().forEach(d::vkDestroyImageView);
+                       d.vkDestroySwapchainKHR(chain.swapchain());
+
+                       scope.close();
+               }
+       }
+
+       static void rebuildCommandBuffersx(RenderInfo info, int extWidth, int extHeight) {
+               Demo.DemoChain chain = info.chain();
+               HandleArray<VkCommandBuffer> commandBuffers = info.commandBuffers();
+               HandleArray<VkFramebuffer> frameBuffers = info.frameBuffers();
+               VkPipeline pipeline = info.pipeline;
+
+               Demo.resetCommandBuffers(info.device, 0, commandBuffers);
+
+               try ( Frame frame = Frame.frame()) {
+                       VkCommandBufferBeginInfo begin = VkCommandBufferBeginInfo.create(0, null, frame);
+                       VkClearValue clear = VkClearValue.createColour(0, 0, 0, 1, frame);
+                       VkRenderPassBeginInfo render = VkRenderPassBeginInfo.create(
+                               chain.renderPass(), null,
+                               0, 0, extWidth, extHeight,
+                               1, clear,
+                               frame);
+                       VkViewport viewports = VkViewport.create(0, 0, extWidth, extHeight, 0, 1, frame);
+                       VkRect2D scissors = VkRect2D.create(0, 0, extWidth, extHeight, frame);
+
+                       for (int i = 0; i < commandBuffers.size(); i++) {
+                               VkCommandBuffer cmd = commandBuffers.get(i);
+
+                               render.setFramebuffer(frameBuffers.get(i));
+
+                               cmd.vkBeginCommandBuffer(begin);
+                               cmd.vkCmdBeginRenderPass(render, VK_SUBPASS_CONTENTS_INLINE);   // SECONDARY... for sub-rendering?
+
+                               cmd.vkCmdSetViewport(0, 1, viewports);
+                               cmd.vkCmdSetScissor(0, 1, scissors);
+
+                               cmd.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+                               cmd.vkCmdDraw(3, 1, 0, 0);
+
+                               cmd.vkCmdEndRenderPass();
+                               cmd.vkEndCommandBuffer();
+                       }
+               }
+       }
+
+       static RenderInfo createRenderInfo(Demo.DemoDevice device, int pixWidth, int pixHeight, VkShaderModule vertex, VkShaderModule fragment) {
+               ResourceScope scope = ResourceScope.newSharedScope();
+               try ( Frame frame = Frame.frame()) {
+                       SegmentAllocator alloc = SegmentAllocator.newNativeArena(scope);
+                       List<Runnable> cleanup = new ArrayList<>();
+
+                       Demo.DemoChain chain = Demo.createSwapchain(
+                               device,
+                               Demo.ColourFormat.SRGB,
+                               //VK_PRESENT_MODE_IMMEDIATE_KHR,
+                               VK_PRESENT_MODE_FIFO_KHR,
+                               pixWidth, pixHeight, alloc, scope);
+
+                       VkPipelineLayout layout = Demo.dumbPipelineLayout(device.device(), scope);
+                       VkPipeline pipeline = Demo.dumbPipeline(device.device(), vertex, fragment, layout, chain.renderPass(), scope);
+
+                       // could move to chain?
+                       HandleArray<VkFramebuffer> frameBuffers = Demo.createFramebuffers(device, chain, alloc, scope);
+                       HandleArray<VkCommandBuffer> commandBuffers = Demo.createCommandBuffers(device, 0, frameBuffers.size(), alloc, scope);
+
+                       // could move creation to Chain?
+                       VkCommandBufferBeginInfo begin = VkCommandBufferBeginInfo.create(0, null, frame);
+                       VkClearValue clear = VkClearValue.createColour(0, 0, 0.25f, 1, frame);
+                       VkRenderPassBeginInfo render = VkRenderPassBeginInfo.create(
+                               chain.renderPass(), null,
+                               0, 0, chain.extWidth(), chain.extHeight(),
+                               1, clear,
+                               frame);
+                       VkViewport viewports = VkViewport.create(0, 0, chain.extWidth(), chain.extHeight(), 0, 1, frame);
+                       VkRect2D scissors = VkRect2D.create(0, 0, chain.extWidth(), chain.extHeight(), frame);
+
+                       Demo.Buffer vertexStaging = Demo.createBuffer(device, vertices.length * 4, Vulkan.VK_BUFFER_USAGE_TRANSFER_SRC_BIT, Vulkan.VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, device.queueFamilies(), MemorySegment.ofArray(vertices), scope);
+                       Demo.Buffer vertexBuffer = Demo.createBuffer(device, vertices.length * 4, Vulkan.VK_BUFFER_USAGE_TRANSFER_DST_BIT | Vulkan.VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, Vulkan.VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, device.queueFamilies(), null, scope);
+                       HandleArray<VkBuffer> buffers = VkBuffer.createArray(1, alloc, scope);
+                       LongArray buffersOffset = LongArray.createArray(1, alloc);
+
+                       buffers.set(0, vertexBuffer.buffer());
+
+                       Demo.copyBuffer(device, vertexStaging, vertexBuffer);
+                       vertexStaging.close(device.device());
+                       cleanup.add(() -> vertexBuffer.close(device.device()));
+
+                       for (int i = 0; i < commandBuffers.size(); i++) {
+                               VkCommandBuffer cmd = commandBuffers.get(i);
+
+                               render.setFramebuffer(frameBuffers.get(i));
+
+                               cmd.vkBeginCommandBuffer(begin);
+                               cmd.vkCmdBeginRenderPass(render, VK_SUBPASS_CONTENTS_INLINE);   // SECONDARY... for sub-rendering?
+
+                               cmd.vkCmdSetViewport(0, 1, viewports);
+                               cmd.vkCmdSetScissor(0, 1, scissors);
+
+                               cmd.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+                               cmd.vkCmdBindVertexBuffers(0, 1, buffers, buffersOffset);
+                               cmd.vkCmdDraw(3, 1, 0, 0);
+
+                               cmd.vkCmdEndRenderPass();
+                               cmd.vkEndCommandBuffer();
+                       }
+                       return new RenderInfo(device, chain, pipeline, layout, commandBuffers, frameBuffers, cleanup.toArray(Runnable[]::new), scope);
+               } catch (Throwable t) {
+                       scope.close();
+                       throw new RuntimeException(t);
+               }
+       }
+
+       public static void main(String[] args) throws IOException, InterruptedException {
+               ResourceScope scope = ResourceScope.globalScope();
+
+               System.loadLibrary("vulkan");
+               System.loadLibrary("X11");
+
+               try ( Frame frame = Frame.frame()) {
+                       VkInstance instance = Demo.createInstance(VK_API_VERSION_1_2,
+                               new String[]{"VK_LAYER_KHRONOS_validation"},
+                               new String[]{"VK_KHR_surface", "VK_KHR_xlib_surface", "VK_EXT_debug_utils"},
+                               scope);
+
+                       Display display;
+                       Window window;
+                       VkSurfaceKHR surface;
+
+                       display = Display.createX11Display();
+                       window = display.createWindow(width, height);
+                       surface = window.createVulkanSurface(instance, scope);
+
+                       VkPhysicalDeviceFeatures features = VkPhysicalDeviceFeatures.create(frame);
+
+                       features.setDepthClamp(VK_TRUE);
+
+                       // Select 3 application queues
+                       // graphics, compute, present
+                       Demo.DemoDevice device = Demo.createDevice(instance, features, surface,
+                               new int[]{Demo.QUEUE_GRAPHICS | Demo.QUEUE_PRESENT, Demo.QUEUE_TRANSFER},
+                               new String[]{
+                                       VK_KHR_SWAPCHAIN_EXTENSION_NAME
+                               }, scope);
+
+                       VkShaderModule vertex = Demo.createShaderModule(device.device(), ShaderIO.loadDemo_vert(frame), scope);
+                       VkShaderModule fragment = Demo.createShaderModule(device.device(), ShaderIO.loadDemo_frag(frame), scope);
+                       HandleArray<VkSwapchainKHR> chains = VkSwapchainKHR.createArray(1, frame, scope);
+
+                       HandleArray<VkSemaphore> imageReady = device.createSemaphores(MAX_FRAMES_INFLIGHT, frame, scope);
+                       HandleArray<VkSemaphore> renderDone = device.createSemaphores(MAX_FRAMES_INFLIGHT, frame, scope);
+                       HandleArray<VkFence> renderFence = device.createFences(MAX_FRAMES_INFLIGHT, VK_FENCE_CREATE_SIGNALED_BIT, frame, scope);
+
+                       RenderInfo render = createRenderInfo(device, width, height, vertex, fragment);
+
+                       Demo.DemoChain chain = render.chain();
+                       HandleArray<VkCommandBuffer> commandBuffers = render.commandBuffers();
+
+                       chains.setAtIndex(0, chain.swapchain());
+
+                       HandleArray<VkFence> imageFence = VkFence.createArray(chain.images().size(), frame, scope);
+
+                       VkSubmitInfo submitDraw = VkSubmitInfo.create(
+                               1, null, // imageRead.current
+                               IntArray.create(frame, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT),
+                               1, null, // commandBuffers.current
+                               1, null, // renderDone.current
+                               frame);
+
+                       IntArray imageIndex = IntArray.createArray(1, frame);
+                       VkPresentInfoKHR present = VkPresentInfoKHR.create(
+                               1, null, //renderDone,
+                               1, chains, imageIndex,
+                               null,
+                               frame);
+
+                       int currentFrame = 0;
+                       long last = System.nanoTime();
+outer:      while (true) {
+                               int res;
+
+                               System.out.println(currentFrame);
+                               for (Event e = window.nextEvent(true); e != null; e = window.nextEvent(false)) {
+                                       System.out.println(e);
+                                       switch (e.type) {
+                                       case Event.KEY:
+                                               if (e.key == 9)
+                                                       break outer;
+                                               break;
+                                       case Event.CLOSE:
+                                               break outer;
+                                       case Event.RESIZE:
+                                               if (e.width == 0) {
+                                                       System.out.println("iconise");
+                                                       while (true) {
+                                                               e = window.nextEvent(true);
+                                                               if (e.type == Event.RESIZE && e.width > 0 && e.height > 0)
+                                                                       break;
+                                                               if (e.type == Event.CLOSE)
+                                                                       break outer;
+                                                       }
+                                               }
+                                               width = e.width;
+                                               height = e.height;
+                                               System.out.printf("resize: %dx%d\n", width, height);
+                                               break;
+                                       }
+                               }
+
+                               long next = System.nanoTime();
+                               //System.out.printf(" %12.9f\n", (next - last) * 1E-9);
+                               last = next;
+
+                               res = device.device().vkAcquireNextImageKHR(chain.swapchain(), ~0, imageReady.get(currentFrame), null, imageIndex);
+                               if (res == VK_SUCCESS || res == VK_SUBOPTIMAL_KHR) {
+                                       int index = imageIndex.get(0);
+                                       VkFence renderBusy = renderFence.get(currentFrame);
+
+                                       // This logic is to handle multiple inflight frames
+                                       {
+                                               HandleArray<VkFence> imageBusy = imageFence.asSlice(index, 1);
+                                               HandleArray<VkFence> renderCurrent = renderFence.asSlice(currentFrame, 1);
+
+                                               if (imageFence.get(index) != null)
+                                                       device.device().vkWaitForFences(1, imageBusy, VK_TRUE, ~0);
+                                               else
+                                                       device.device().vkWaitForFences(1, renderCurrent, VK_TRUE, ~0);
+
+                                               imageFence.set(index, renderBusy);
+                                               device.device().vkResetFences(1, renderCurrent);
+                                       }
+
+                                       submitDraw.setWaitSemaphores(imageReady.asSlice(currentFrame));
+                                       submitDraw.setSignalSemaphores(renderDone.asSlice(currentFrame));
+                                       submitDraw.setCommandBuffers(commandBuffers.asSlice(index));
+
+                                       present.setWaitSemaphores(renderDone.asSlice(currentFrame));
+
+                                       device.queues()[0].vkQueueSubmit(1, submitDraw, renderBusy);
+                                       device.queues()[0].vkQueuePresentKHR(present);
+
+                                       //device.device().vkWaitForFences(1, renderFence, VK_TRUE, ~0);
+                                       // resize
+                                       if (res == VK_SUBOPTIMAL_KHR) {
+                                               device.device().vkDeviceWaitIdle();
+
+                                               System.out.println("reconfigure");
+                                               render.close();
+                                               render = createRenderInfo(device, width, height, vertex, fragment);
+                                               chain = render.chain();
+                                               chains.setAtIndex(0, chain.swapchain());
+                                               commandBuffers = render.commandBuffers();
+                                       }
+
+                               } else {
+                                       System.out.println(Vulkan.getErrorMessage(res));
+                               }
+
+                               currentFrame = (currentFrame + 1) % MAX_FRAMES_INFLIGHT;
+                       }
+                       device.device().vkDeviceWaitIdle();
+
+                       Stream.concat(imageReady.stream(), renderDone.stream()).forEach(device.device()::vkDestroySemaphore);
+                       renderFence.stream().forEach(device.device()::vkDestroyFence);
+
+                       device.device().vkDestroyShaderModule(fragment);
+                       device.device().vkDestroyShaderModule(vertex);
+
+                       render.close();
+                       device.close();
+                       instance.vkDestroySurfaceKHR(surface);
+                       instance.vkDestroyInstance();
+               }
+       }
+}
diff --git a/src/notzed.vulkan.test/gen/gen.make b/src/notzed.vulkan.test/gen/gen.make
new file mode 100644 (file)
index 0000000..b0c5235
--- /dev/null
@@ -0,0 +1,24 @@
+
+notzed.vulkan.test_SHADERS =                   \
+       vulkan/test/mandelbrot.comp.bin         \
+       vulkan/test/cube.vert.bin               \
+       vulkan/test/cube.frag.bin               \
+       vulkan/test/sdf.vert.bin                \
+       vulkan/test/sdf.frag.bin                \
+       vulkan/test/demo.vert.bin               \
+       vulkan/test/demo.frag.bin
+
+notzed.vulkan.test_JAVA_GENERATED =            \
+       vulkan/test/ShaderIO.java
+
+notzed.vulkan.test_RESOURCES_GENERATED =       \
+       $(notzed.vulkan.test_SHADERS)
+
+notzed.vulkan.test_SPIRV = $(notzed.vulkan.test_SHADERS:%=bin/gen/notzed.vulkan.test/classes/%)
+
+bin/gen/notzed.vulkan.test/classes/%.bin: src/notzed.vulkan.test/shaders/%
+       mkdir -p $(@D)
+       glslangValidator --target-env vulkan1.0 -V -o $@ $<
+
+bin/gen/notzed.vulkan.test/classes/vulkan/test/ShaderIO.java: $(notzed.vulkan.test_SPIRV) src/notzed.vulkan.test/gen/generate-shaderio
+       src/notzed.vulkan.test/gen/generate-shaderio $@ $(notzed.vulkan.test_SPIRV)
diff --git a/src/notzed.vulkan.test/gen/generate-shaderio b/src/notzed.vulkan.test/gen/generate-shaderio
new file mode 100755 (executable)
index 0000000..c2c2485
--- /dev/null
@@ -0,0 +1,52 @@
+#!/usr/bin/perl
+
+# probably should take scope
+
+use strict;
+
+use File::Path qw(make_path);
+use File::Basename;
+
+my $path = shift;
+
+open my $f, ">", "$path~" || die;
+
+print $f <<END;
+package vulkan.test;
+
+import au.notzed.nativez.IntArray;
+import au.notzed.nativez.Memory;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.Channels;
+import jdk.incubator.foreign.MemorySegment;
+import jdk.incubator.foreign.SegmentAllocator;
+
+public class ShaderIO {
+       static IntArray loadSPIRV(String name, long size, SegmentAllocator alloc) throws IOException {
+               try ( InputStream is = TestMandelbrot.class.getResourceAsStream(name)) {
+                       MemorySegment seg = alloc.allocateArray(Memory.INT, size);
+                       int length = Channels.newChannel(is).read(seg.asByteBuffer());
+                       return IntArray.create(seg.asSlice(0, length));
+               }
+       }
+END
+
+while ($#ARGV >= 0) {
+       my $file = shift;
+       my $func = ucfirst(basename($file, '.bin'));
+       my $name = basename($file);
+       my $size = (stat($file))[7];
+
+       die "No such file: $file" if !-f $file;
+
+       $func =~ s/\./_/gon;
+
+       print $f "\tstatic IntArray load$func(SegmentAllocator alloc) throws IOException { return loadSPIRV(\"$name\", $size, alloc); }\n";
+}
+
+print $f "}\n";
+
+close $f;
+
+rename "$path~",$path;
diff --git a/src/notzed.vulkan.test/shaders/vulkan/test/cube.frag b/src/notzed.vulkan.test/shaders/vulkan/test/cube.frag
new file mode 100644 (file)
index 0000000..de24544
--- /dev/null
@@ -0,0 +1,8 @@
+#version 400
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+layout (location = 0) in vec4 color;
+layout (location = 0) out vec4 outColor;
+void main() {
+   outColor = color;
+}
diff --git a/src/notzed.vulkan.test/shaders/vulkan/test/cube.vert b/src/notzed.vulkan.test/shaders/vulkan/test/cube.vert
new file mode 100644 (file)
index 0000000..5d21e1e
--- /dev/null
@@ -0,0 +1,14 @@
+#version 400
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+layout (std140, binding = 0) uniform bufferVals {
+    mat4 mvp;
+} data;
+layout (location = 0) in vec4 pos;
+layout (location = 1) in vec4 inColor;
+layout (location = 0) out vec4 outColor;
+
+void main() {
+   outColor = inColor;
+   gl_Position = data.mvp * pos;
+}
diff --git a/src/notzed.vulkan.test/shaders/vulkan/test/demo.frag b/src/notzed.vulkan.test/shaders/vulkan/test/demo.frag
new file mode 100644 (file)
index 0000000..e335fa8
--- /dev/null
@@ -0,0 +1,8 @@
+#version 450
+
+layout (location = 0) in vec3 colour;
+layout (location = 0) out vec4 pixel;
+
+void main() {
+       pixel = vec4(colour, 1);
+}
diff --git a/src/notzed.vulkan.test/shaders/vulkan/test/demo.vert b/src/notzed.vulkan.test/shaders/vulkan/test/demo.vert
new file mode 100644 (file)
index 0000000..48bd441
--- /dev/null
@@ -0,0 +1,11 @@
+#version 450
+
+layout (location=0) in vec2 inPosition;
+layout (location=1) in vec3 inColour;
+
+layout (location=0) out vec3 colour;
+
+void main() {
+       gl_Position = vec4(inPosition, 0.0, 1.0);
+       colour = inColour;
+}
diff --git a/src/notzed.vulkan.test/shaders/vulkan/test/mandelbrot.comp b/src/notzed.vulkan.test/shaders/vulkan/test/mandelbrot.comp
new file mode 100644 (file)
index 0000000..6a45590
--- /dev/null
@@ -0,0 +1,60 @@
+#version 450
+
+#define WIDTH (1920*1)
+#define HEIGHT (1080*1)
+#define LWS_X 8
+#define LWS_Y 8
+#define LIMIT 10000
+
+layout (local_size_x = LWS_X, local_size_y = LWS_Y, local_size_z = 1 ) in;
+
+layout(std430, binding = 0) buffer buf {
+       uint imageData[];
+};
+
+void main() {
+
+       /*
+         In order to fit the work into workgroups, some unnecessary threads are launched.
+         We terminate those threads here.
+       */
+       if(gl_GlobalInvocationID.x >= WIDTH || gl_GlobalInvocationID.y >= HEIGHT)
+               return;
+
+       float x = float(gl_GlobalInvocationID.x) / float(WIDTH);
+       float y = float(gl_GlobalInvocationID.y) / float(HEIGHT);
+
+       /*
+         What follows is code for rendering the mandelbrot set.
+       */
+       vec2 uv = vec2(x, (y - 0.5) * (12.0 / 19.0) + 0.5);
+       float n = 0.0;
+       vec2 c = vec2(-.445, 0.0) + (uv - 0.5)*(4.0);
+       vec2 z = vec2(0.0);
+       const int M = LIMIT;
+
+       for (int i = 0; i<M; i++) {
+               z = vec2(z.x*z.x - z.y*z.y, 2.*z.x*z.y) + c;
+
+               if (dot(z, z) > 4)
+                       break;
+               n++;
+       }
+
+       // we use a simple cosine palette to determine color:
+       // http://iquilezles.org/www/articles/palettes/palettes.htm
+       float t = float(n) * 500.0 / float(M);
+       vec3 d = vec3(0.5, 0.5, 0.5);
+       vec3 e = vec3(0.5, 0.5, 0.5);
+       vec3 f = vec3(1.0, 1.0, 1.0);
+       vec3 g = vec3(0.00, 0.33, 0.67);
+
+       vec4 color = vec4( d + e*cos( 6.28318*(f*t+g) ) ,1.0);
+
+       if (n == M)
+               color = vec4(0, 0, 0, 1);
+
+       // store the rendered mandelbrot set into a storage buffer:
+       imageData[WIDTH * gl_GlobalInvocationID.y + gl_GlobalInvocationID.x] = packUnorm4x8(color);
+       //imageData[WIDTH * gl_GlobalInvocationID.y + gl_GlobalInvocationID.x].value = color;
+}
diff --git a/src/notzed.vulkan.test/shaders/vulkan/test/sdf.frag b/src/notzed.vulkan.test/shaders/vulkan/test/sdf.frag
new file mode 100644 (file)
index 0000000..b453bd2
--- /dev/null
@@ -0,0 +1,21 @@
+#version 400
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+layout (location = 0) out vec4 outColor;
+
+void main() {
+       vec2 res = vec2(800, 800);
+       vec2 uv = (gl_FragCoord.xy - 0.5 * res) * (1.0 / 400.0);
+
+       float d = max(-(length(uv) - 0.5), length(uv + vec2(0.1, 0.1)) - 0.6);
+
+       if (d < 0) {
+               outColor = vec4(0, 0, 1, 1);
+       } else {
+               outColor = vec4(1, 0, 0, 1);
+       }
+
+
+       //outColor = vec4(1, 0, 1, 1);
+       //outColor = vec4(gl_FragCoord.xy * 1.0 / 500.0, 0, 1);
+}
diff --git a/src/notzed.vulkan.test/shaders/vulkan/test/sdf.vert b/src/notzed.vulkan.test/shaders/vulkan/test/sdf.vert
new file mode 100644 (file)
index 0000000..8019a0a
--- /dev/null
@@ -0,0 +1,9 @@
+#version 400
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec4 pos;
+
+void main() {
+   gl_Position = pos;
+}
diff --git a/src/notzed.vulkan/classes/module-info.java b/src/notzed.vulkan/classes/module-info.java
new file mode 100644 (file)
index 0000000..3e7cdec
--- /dev/null
@@ -0,0 +1,8 @@
+
+module notzed.vulkan {
+       requires transitive notzed.nativez;
+       requires notzed.xlib;
+       requires notzed.xcb;
+
+       exports vulkan;
+}
diff --git a/src/notzed.vulkan/gen/command-types.api b/src/notzed.vulkan/gen/command-types.api
new file mode 100644 (file)
index 0000000..e8f02f1
--- /dev/null
@@ -0,0 +1,680 @@
+# -*- Mode:text; tab-width:4; electric-indent-mode: nil; indent-line-function:insert-tab; -*-
+
+# things to check; vkCmdSetFragmentShadingRateKHR
+
+# types for function calls
+
+code method {
+  invoke {{
+       /* method:invoke */
+       static final MethodHandle {name}$FH = Memory.downcall("{name}",
+               {function-descriptor});
+       /**
+       * success: {successcodes}
+       * errors: {errorcodes}
+       */
+       public {static}{java-result} {rename}(
+               {java-arg}) {
+               {native-result-define}
+               try {create-frame}{
+                       {native-init}
+                       {native-result-assign}{name}$FH.invokeExact({invoke-arg});
+                       {result-test}{
+                               {java-result-assign}
+                               {java-result-return}
+                       }
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+               {result-throw}
+       }
+  }}
+}
+
+code method-query {
+  invoke {{
+       /* method-query:invoke */
+       static final MethodHandle {name}$FH = Memory.downcall("{name}",
+               {function-descriptor});
+       /**
+       * success: {successcodes}
+       * errors: {errorcodes}
+       */
+       public {static}{java-result} {rename}(
+               {java-arg}) {
+               {native-result-define}
+               try {create-frame}{
+                       {native-init}
+                       {native-result-assign}{name}$FH.invokeExact({query-arg});
+                       {result-test}{
+                               {query-init}
+                               {native-result-assign}{name}$FH.invokeExact({invoke-arg});
+                               {java-result-assign}
+                               {java-result-return}
+                       }
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+               {result-throw}
+       }
+  }}
+}
+
+code method-extension {
+  dispatch {{
+       final NativeSymbol {name}$NS;
+  }}
+  invoke {{
+       /* method-extension:invoke */
+       final static MethodHandle {name}$DH = Memory.downcall(
+               {function-descriptor});
+       /**
+       * success: {successcodes}
+       * errors: {errorcodes}
+       */
+       public {static}{java-result} {rename}(
+               {java-arg}) {
+               {native-result-define}
+               try {create-frame}{
+                       {native-init}
+                       {native-result-assign}{name}$DH.invokeExact(dispatch.{name}$NS, {invoke-arg});
+                       {result-test}{
+                               {java-result-assign}
+                               {java-result-return}
+                       }
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+               {result-throw}
+       }
+  }}
+}
+
+code method-extension-query {
+  dispatch {{
+       final NativeSymbol {name}$NS;
+  }}
+  invoke {{
+       /* method-extension:invoke */
+       final static MethodHandle {name}$DH = Memory.downcall(
+               {function-descriptor});
+       /**
+       * success: {successcodes}
+       * errors: {errorcodes}
+       */
+       public {static}{java-result} {rename}(
+               {java-arg}) {
+               {native-result-define}
+               try {create-frame}{
+                       {native-init}
+                       {native-result-assign}{name}$DH.invokeExact(dispatch.{name}$NS, {query-arg});
+                       {result-test}{
+                               {query-init}
+                               {native-result-assign}{name}$DH.invokeExact(dispatch.{name}$NS, {invoke-arg});
+                               {java-result-assign}
+                               {java-result-return}
+                       }
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+               {result-throw}
+       }
+  }}
+}
+
+# ###################################################################### #
+
+# common parts for funcpointer
+code funcpointer {
+  common {{
+       public {java-result} call({java-arg});
+
+       public final static FunctionDescriptor DESCRIPTOR =
+               {function-descriptor};
+  }}
+  upcall {{
+       public static FunctionPointer<{rename}> upcall({rename} target$, ResourceScope scope$) {
+               interface Trampoline {
+                       {native-result} call({native-arg});
+               }
+               Trampoline trampoline = ({native-arg}) -> {
+                       // frame?  scope?
+                       try {trampoline-scope} {
+                               {trampoline-result-define}
+                               {trampoline-result-assign}target$.call({trampoline-arg});
+                               {trampoline-result-return}
+                       } catch (Throwable t) {
+                               throw new RuntimeException(t);
+                       }
+               };
+               return new FunctionPointer<>(
+                       Memory.upcall(
+                               MethodHandles.lookup(),
+                               trampoline,
+                               "call",
+                               "{trampoline-signature}",
+                               DESCRIPTOR,
+                               scope$),
+                       target$);
+       }
+  }}
+  # vulkan has none
+  downcall {{ }}
+}
+
+code funcpointer-readwrite insert=funcpointer:common,funcpointer:upcall,funcpointer:downcall {
+  class {{
+       package {package};
+
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       @FunctionalInterface
+       public interface {name} {
+
+               {common}
+
+               {upcall}
+               {downcall}
+       }
+  }}
+}
+
+# ###################################################################### #
+
+# values required
+
+#  java-arg                    java parameter type and and name
+#  invoke-arg          native value for invokeExact argument
+#  native-arg          native parameter type and name
+#  trampoline-arg      java value for upcall invocation
+#  layout                      MemoryLayout
+#  sig                         JNI signature string
+
+# parameterisation values
+#  type                                java type
+#  carrier                     native (jdk.foriegn) type
+#  length                      array length
+
+type value {
+       java-arg        {{ {type} {name} }}
+       invoke-arg      {{ {name} }}
+
+       native-arg      {{ {carrier} {name}$ }}
+       trampoline-arg  {{ {name}$ }}
+}
+
+type value-array {
+       java-arg        {{ {type} {name} }}
+       invoke-arg      {{ {name} }}
+
+       native-arg      {{ {carrier} {name}$ }}
+       trampoline-arg  {{ {name}$ }}
+}
+
+type value-array2d {
+       java-arg        {{ {type} {name} }}
+       invoke-arg      {{ {name} }}
+
+       native-arg      {{ {carrier} {name}$ }}
+       trampoline-arg  {{ {name}$ }}
+}
+
+type uint8_t,char value {
+       carrier {{ byte }}
+       type    {{ byte }}
+       layout  {{ Memory.BYTE }}
+}
+
+type uint16_t value {
+       carrier {{ short }}
+       type    {{ short }}
+       layout  {{ Memory.SHORT }}
+}
+
+type uint32_t,int,int32_t value {
+       carrier {{ int }}
+       type    {{ int }}
+       layout  {{ Memory.INT }}
+       sig             {{ I }}
+}
+
+type uint64_t,int64_t,size_t value {
+       carrier {{ long }}
+       type    {{ long }}
+       layout  {{ Memory.LONG }}
+       sig             {{ J }}
+}
+
+type float value {
+       carrier {{ float }}
+       type    {{ float }}
+       layout  {{ Memory.FLOAT }}
+}
+
+type double value {
+       carrier {{ double }}
+       type    {{ double }}
+       layout  {{ Memory.DOUBLE }}
+}
+
+# ###################################################################### #
+
+# type uint8_t[],char[] value-array {
+#      type    {{ ByteArray }}
+#      layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.BYTE) }}
+#      typei   {{ byte }}
+# }
+
+#type uint32_t[],int32_t[] value-array {
+#      type    {{ IntArray }}
+#      layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.INT) }}
+#      typei   {{ int }}
+#}
+
+# type uint64_t[] value-array {
+#      type    {{ LongArray }}
+#      layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.LONG) }}
+#      typei   {{ long }}
+# }
+
+# calls float[] =-> pointer
+#type float[] value-array {
+#      # or should it be float[] ?
+#      type    {{ FloatArray }}
+#      layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.FLOAT) }}
+#      typei   {{ float }}
+#}
+
+# type struct[] inline-array {
+#      type    {{ {baseType} }}
+#      layout  {{ MemoryLayout.sequenceLayout({len1}, {baseType}.LAYOUT) }}
+# }
+
+# type float[][] value-array2d {
+#      type    {{ FloatArray }}
+#      typei   {{ float }}
+#      layout  {{ MemoryLayout.sequenceLayout({len1}, MemoryLayout.sequenceLayout({len2}, Memory.FLOAT)) }}
+# }
+
+# select=len?  or what?
+
+type pointer value {
+       carrier {{ MemoryAddress }}
+       layout  {{ Memory.POINTER }}
+       type    {{ MemoryAddress }}
+       sig             {{ Ljdk/incubator/foreign/MemoryAddress; }}
+
+       invoke-arg      {{ Memory.address({name}) }}
+}
+
+type void {
+       type            {{ void }}
+       sig                     {{ V }}
+       java-arg        {{ }}
+}
+
+type void* pointer;
+
+type funcpointer pointer {
+       type            {{ NativeSymbol }}
+}
+
+# FIXME: clenaup, value-pointer does nothing
+type value-pointer pointer {
+}
+
+type uint8_t* value-pointer {
+       type    ByteArray;
+}
+
+type uint32_t*,int32_t*,int* value-pointer {
+       type    IntArray;
+}
+
+type uint64_t* value-pointer {
+       type    LongArray;
+}
+
+type float* value-pointer {
+       type    FloatArray;
+}
+
+type size_t* value-pointer {
+       type    LongArray;
+}
+
+type pointer-length pointer {
+}
+
+type void*-length pointer-length {
+       type    MemorySegment;
+}
+
+type uint8_t*-length pointer-length {
+       type    ByteArray;
+}
+
+type uint32_t*-length,int32_t*-length pointer-length {
+       type    IntArray;
+}
+
+type uint32_t[] pointer-length {
+       type    IntArray;
+       length  {{ {len1} }}
+}
+
+type uint64_t*-length pointer-length {
+       type    LongArray;
+}
+
+type float*-length pointer-length {
+       type    FloatArray;
+}
+
+type float[] pointer-length {
+       type    FloatArray;
+       length  {{ {len1} }}
+}
+
+# special handling for strings, will fail if it isn't
+type char* pointer need-frame {
+       type            {{ String }}
+
+       invoke-arg      {{ (Addressable)Memory.copyString({name}, frame$) }}
+
+       # null check?
+       trampoline-arg  {{ {name}$.getUtf8String(0) }}
+
+       # this just verifies it's a string type
+       length eval     {{
+               if ($v->{len} =~ m/null-terminated/) {
+                       1;
+               } else {
+                       print "Warning: not sure if this is a string: $s->{name} $m->{name}\n";
+               }
+       }}
+
+}
+
+# type XXchar**-length pointer-length accessor=value-alloc {
+#      type    {{ String[] }}
+
+#      java-set        {{ {name}$VH.set(segment, Memory.copyStringArray({name}, alloc$)); {set-length} }}
+#      java-get        {{ Memory.copyStringArray((MemoryAddress){name}$VH.get(segment), {length}) }}
+
+#      set-length eval {{
+#              if ($v->{len} =~ m/(.*),null-terminated/) {
+#                      'set'.ucfirst($1).'({name}.length)';
+#              } else {
+#                      die Dumper($v, $s);
+#              }
+#      }}
+# }
+
+# FIXME: wrong
+type Xuint32_t** pointer {
+       type    {{ HandleArray<IntArray> }}
+       typei   {{ IntArray }}
+}
+
+type uint32_t**-length pointer {
+       type    {{ HandleArray<IntArray> }}
+       typei   {{ IntArray }}
+}
+
+type void** pointer {
+       type    PointerArray;
+}
+
+type void**-length pointer-length {
+       type    PointerArray;
+}
+
+type handle pointer {
+       type            {{ {baseType} }}
+}
+
+type handle* pointer {
+       type            {{ HandleArray<{typei}> }}
+       typei           {{ {baseType} }}
+
+       invoke-arg      {{ Memory.address({name}) }}
+}
+
+type handle*-length pointer-length {
+       type            {{ HandleArray<{baseType}> }}
+       typei           {{ {baseType} }}
+}
+
+#type struct inline {
+#      type    {{ {baseType} }}
+#      layout  {{ {baseType}.LAYOUT }}
+#}
+
+type struct* pointer trampoline-scope {
+       type            {{ {baseType} }}
+
+       trampoline-arg  {{ {baseType}.create({name}$, scope$$) }}
+}
+
+type struct*-length pointer-length {
+       type    {{ {baseType} }}
+       typei   {{ {baseType} }}
+}
+
+type struct**-length pointer-length {
+       type    {{ HandleArray<{baseType}> }}
+       typei   {{ {baseType} }}
+}
+
+type struct** pointer {
+}
+
+# xlib
+type XID,Window,VisualID uint64_t;
+
+type Display* handle {
+       type    xlib.XDisplay;
+}
+
+# xcb
+type xcb_window_t uint32_t;
+type xcb_visualid_t uint32_t;
+type xcb_connection_t* handle {
+       type    xcb.Connection;
+}
+
+# special types for call overrides
+type instance handle is-instance {
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable)self }}
+       native-arg      {{ }}
+}
+
+# A pointer type which is always ignored/supressed, e.g. VkAllocationCallbacks *
+type struct*-ignore,void*-ignore void* {
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable)MemoryAddress.NULL }}
+       trampoline-arg  {{ }}
+}
+
+type void*-return void* {
+       native-result-define    {{ MemoryAddress result$; }}
+       native-result-assign    {{ result$ = (MemoryAddress) }}
+       java-result-return              {{ return result$; }}
+}
+
+type funcpointer-return funcpointer need-scope {
+       native-result-define    {{ MemoryAddress result$; }}
+       native-result-assign    {{ result$ = (MemoryAddress) }}
+       java-result-return              {{ return NativeSymbol.ofAddress(pName, result$, scope$); }}
+}
+
+type uint32_t-return uint32_t {
+       native-result-define    {{ int result$; }}
+       native-result-assign    {{ result$ = (int) }}
+       java-result-return              {{ return result$; /* uint32_t */ }}
+
+       trampoline-result-define        {{ int result$$; }}
+       trampoline-result-assign        {{ result$$ = (int) }}
+       trampoline-result-return        {{ return result$$; /* uint32_t */ }}
+}
+
+type uint64_t-return,size_t-return uint64_t {
+       native-result-define    {{ long result$; }}
+       native-result-assign    {{ result$ = (long) }}
+       java-result-return              {{ return result$; /* uint64_t */ }}
+}
+
+# for handle constructors
+type handle*-output handle* need-frame need-scope {
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name} }}
+
+       java-result                     {{ {baseType} }}
+       native-init                     {{ MemorySegment {name} = frame$.allocate(Memory.POINTER); }}
+       java-result-assign      {{ {baseType} result$$ = {baseType}.create({name}.get(Memory.POINTER, 0), scope$); }}
+       java-result-return      {{ return result$$; }}
+}
+
+type uint32_t*-output,int*-output uint32_t* need-frame {
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name}.address() }}
+
+       java-result                     {{ int }}
+       native-init                     {{ MemorySegment {name} = frame$.allocate(Memory.INT); }}
+       java-result-assign      {{ int result$$ = {name}.getAtIndex(Memory.INT, 0); }}
+       java-result-return      {{ return result$$; }}
+}
+
+type uint64_t*-output uint64_t* need-frame {
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name}.address() }}
+
+       java-result                     {{ long }}
+       native-init                     {{ MemorySegment {name} = frame$.allocate(Memory.LONG); }}
+       java-result-assign      {{ long result$$ = {name}.getAtIndex(Memory.LONG, 0); }}
+       java-result-return      {{ return result$$; }}
+}
+
+type void*-output handle*-output need-frame {
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name}.address() }}
+
+       java-result                     {{ MemoryAddress }}
+       native-init                     {{ MemorySegment {name} = frame$.allocate(Memory.POINTER); }}
+       java-result-assign      {{ MemoryAddress result$$ = {name}.getAtIndex(Memory.POINTER, 0); }}
+       java-result-return      {{ return result$$; }}
+}
+
+type VkBool32*-output uint32_t*-output {
+       java-result                     {{ boolean }}
+       java-result-return      {{ return result$$ != 0; }}
+}
+
+# for handle constructors of dispatchable types
+type dispatch*-output handle*-output {
+       java-result-assign      {{ {baseType} result$$ = {baseType}.create({name}.get(Memory.POINTER, 0), dispatch, scope$); }}
+}
+
+# for query and return types
+type handle*-length-query handle*-length need-alloc need-scope {
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name}.address() }}
+       query-arg       {{ (Addressable)MemoryAddress.NULL }}
+
+       java-result                     {{ {type} }}
+       query-init                      {{ {type} {name} = {typei}.createArray({length}, alloc$, scope$); }}
+       java-result-return      {{ return {name}; }}
+}
+
+type dispatch*-length-query handle*-length-query need-scope {
+       query-init                      {{ {type} {name} = {typei}.createArray({length}, alloc$, dispatch, scope$); }}
+}
+
+type struct*-length-query struct*-length need-alloc {
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name}.address() }}
+       query-arg       {{ (Addressable)MemoryAddress.NULL }}
+
+       java-result                     {{ {type} }}
+       query-init                      {{ {type} {name} = {typei}.createArray({length}, alloc$); }}
+       java-result-return      {{ return {name}; }}
+}
+
+type void*-length-query void*-length need-alloc {
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name} }}
+       query-arg       {{ (Addressable)MemoryAddress.NULL }}
+
+       java-result                     {{ {type} }}
+       query-init                      {{ {type} {name} = alloc$.allocate({length}); }}
+       java-result-return      {{ return {name}; }}
+}
+
+type uint32_t*-length-query uint32_t*-length need-alloc {
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name}.address() }}
+       query-arg       {{ (Addressable)MemoryAddress.NULL }}
+
+       java-result                     {{ {type} }}
+       query-init                      {{ {type} {name} = IntArray.createArray({length}, alloc$); }}
+       java-result-return      {{ return {name}; }}
+}
+
+type uint32_t*-querylen uint32_t* need-frame {
+       type            {{ MemorySegment }}
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name}$ }}
+
+       native-init     {{ {type} {name}$ = alloc$.allocate(Memory.INT, 0); }}
+       query-init      {{ long {name} = {name}$.get(Memory.INT, 0); }}
+}
+
+type size_t*-querylen uint32_t* need-frame {
+       type            {{ MemorySegment }}
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name}$ }}
+
+       native-init     {{ {type} {name}$ = alloc$.allocate(Memory.LONG, 0); }}
+       query-init      {{ long {name} = {name}$.get(Memory.LONG, 0); }}
+}
+
+type void**-output void** need-frame need-scope {
+       type            {{ MemorySegment }}
+
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name} }}
+
+       native-init     {{ MemorySegment {name} = frame$.allocate(Memory.POINTER); }}
+
+       java-result                     {{ {type} }}
+       java-result-assign      {{ {type} result$$ = MemorySegment.ofAddress({name}.get(Memory.POINTER, 0), {length}, scope$); }}
+       java-result-return      {{ return result$$; }}
+}
+
+type vkMapMemory-output void**-output {
+       length  {{ size }}
+}
+
+# some tweaks that the auto-discovery code misses/isn't worth adding to
+override commands {
+       vkMapMemory device=type:instance ppData=type:vkMapMemory-output;
+
+       # Don't need userData
+       PFN_vkDebugUtilsMessengerCallbackEXT pUserData=type:void*-ignore;
+       PFN_vkDebugReportCallbackEXT pUserData=type:void*-ignore;
+       PFN_vkDeviceMemoryReportCallbackEXT pUserData=type:void*-ignore;
+
+       # Mapped to a NativeSymbol via 'funcpointer' type
+       PFN_vkVoidFunction ignore;
+
+       # Don't need VkAllocationCallbacks, don't need these
+       PFN_vkAllocationFunction ignore;
+       PFN_vkReallocationFunction ignore;
+       PFN_vkFreeFunction ignore;
+       PFN_vkInternalAllocationNotification ignore;
+       PFN_vkInternalFreeNotification ignore;
+}
diff --git a/src/notzed.vulkan/gen/gen.make b/src/notzed.vulkan/gen/gen.make
new file mode 100644 (file)
index 0000000..372ccea
--- /dev/null
@@ -0,0 +1,18 @@
+
+bin/status/notzed.vulkan.depjava: \
+       bin/status/notzed.vulkan.export
+
+bin/status/notzed.vulkan.export: \
+       $(NATIVEZ_HOME)/lib/config.pm \
+       $(NATIVEZ_HOME)/lib/tokenise.pm \
+       $(NATIVEZ_HOME)/lib/code.pm \
+       $(NATIVEZ_HOME)/lib/api.pm \
+       src/notzed.vulkan/gen/generate-vulkan \
+       src/notzed.vulkan/gen/vulkan.pm \
+       src/notzed.vulkan/gen/struct-types.api \
+       src/notzed.vulkan/gen/command-types.api
+
+bin/status/notzed.vulkan.export:
+       NATIVEZ_HOME=$(NATIVEZ_HOME) src/notzed.vulkan/gen/generate-vulkan -t vulkan -d bin/gen/notzed.vulkan/classes
+       mkdir -p $(@D)
+       touch $@
diff --git a/src/notzed.vulkan/gen/generate-vulkan b/src/notzed.vulkan/gen/generate-vulkan
new file mode 100755 (executable)
index 0000000..89d8876
--- /dev/null
@@ -0,0 +1,1445 @@
+#!/usr/bin/perl
+
+use Data::Dumper;
+
+use File::Path qw(make_path);
+use File::Basename;
+use Time::HiRes qw(clock_gettime CLOCK_REALTIME);
+
+use strict;
+
+use Carp 'verbose';
+use FindBin;
+use lib "$FindBin::Bin", 'bin/linux-amd64/lib', "$ENV{NATIVEZ_HOME}/lib";
+
+use vulkan;
+use config;
+use code;
+
+$SIG{__DIE__} = sub { Carp::confess( @_ ) };
+$SIG{'INT'} = sub { Carp::confess() };
+$Data::Dumper::Indent = 1;
+
+# ###################################################################### #
+
+my $enumInfo = {
+       'uint32_t' => {
+               type => 'int'
+       },
+               'uint64_t' => {
+                       type => 'long',
+                       suffix => 'L'
+       },
+               'float' => {
+                       type => 'float',
+                       suffix => 'f'
+       },
+       'const char *' => {
+               type => 'String'
+       },
+};
+
+my %primitiveMap = (
+       'char' => 'byte',
+       'uint8_t' => 'byte',
+       'uint16_t' => 'short',
+       'int32_t' => 'int',
+       'uint32_t' => 'int',
+       'int' => 'int',
+       'int64_t' => 'long',
+       'uint64_t' => 'long',
+       'size_t' => 'long',
+       'float' => 'float',
+       'double' => 'double',
+       'VkFlags' => 'int',
+       'VkFlags64' => 'long'
+);
+
+# ###################################################################### #
+my $now = clock_gettime(CLOCK_REALTIME);
+my $next;
+my $sys = {};
+
+$sys->{output} = 'bin/gen/notzed.vulkan/classes';
+$sys->{package} = 'vulkan';
+$sys->{verbose} = 0;
+
+my $vk = new vulkan($sys);
+
+printf "%9.6f load registry\n", (($next = clock_gettime(CLOCK_REALTIME)) - $now); $now = $next;
+
+my $api = $vk->buildFeatures(
+       [ 'VK_VERSION_1_0', 'VK_VERSION_1_1', 'VK_VERSION_1_2', 'VK_VERSION_1_3' ],
+       [ 'xlib',
+         #'wayland',
+         'xcb' ]);
+
+printf "%9.6f build api view\n", (($next = clock_gettime(CLOCK_REALTIME)) - $now); $now = $next;
+
+my $structTypes = loadTypes($api, 'struct-types.api');
+my $commandTypes = loadTypes($api, 'command-types.api');
+
+printf "%9.6f load templates\n", (($next = clock_gettime(CLOCK_REALTIME)) - $now); $now = $next;
+
+analyseTypes($vk, $api);
+
+printf "%9.6f analyse types\n", (($next = clock_gettime(CLOCK_REALTIME)) - $now); $now = $next;
+
+# Use some heuristics to create overrides for improving the api
+
+# - object constructor functions return values rather take pointer* values
+# - array query functions perform the query and return the array
+# - extension functions use a different invocation mechanism
+# - drop any 'ignore' types from argument lists
+
+# - [attempt to] determine which types need to be read/write/arrays
+
+# Scan structs
+my %defaultTemplate;
+
+foreach my $s (values %{$api->{types}}) {
+       my $overrides = $structTypes->{overrides};
+       my $types = $structTypes->{types};
+
+       next if (defined($overrides->{$s->{name}}));
+
+       foreach my $m (@{$s->{items}}) {
+               my $nstar = $m->{deref} =~ tr/*/*/;
+
+               if ($m->{deref} eq 'struct*-length') {
+                       $defaultTemplate{$m->{baseType}}->{array} = 1;
+               } elsif ($m->{deref} eq 'struct[]') {
+                       $defaultTemplate{$m->{baseType}}->{array} = 1;
+               }
+       }
+
+       $defaultTemplate{$s->{name}}->{name} = 'struct-readonly' if ($s->{returnedonly} eq 'true');
+}
+
+# build default overrides for commands
+foreach my $s (values %{$api->{commands}}) {
+       my $overrides = $commandTypes->{overrides};
+       my $types = $commandTypes->{types};
+       my $first = $s->{items}->[0];
+       my $last = $s->{items}->[$#{$s->{items}}];
+       my $llast = $s->{items}->[$#{$s->{items}}-1];
+       my $result = $s->{proto};
+       my $index = $s->{index};
+
+       # check type updates anyway
+       foreach my $m (@{$s->{items}}) {
+               if ($m->{deref} eq 'struct*-length') {
+                       $defaultTemplate{$m->{baseType}}->{name} = 'struct-readwrite' if !($m->{fullType} =~ m/const/n) && $api->{types}->{$m->{baseType}}->{returnedonly} ne 'true';
+                       $defaultTemplate{$m->{baseType}}->{array} = 1;
+               } elsif ($m->{deref} eq 'struct*') {
+                       $defaultTemplate{$m->{baseType}}->{name} = 'struct-readwrite' if !($m->{fullType} =~ m/const/n) && $api->{types}->{$m->{baseType}}->{returnedonly} ne 'true';
+               }
+       }
+
+       next if (defined($overrides->{$s->{name}}));
+
+       my $override = {};
+
+       # force handles to be instance types
+       if ($first->{deref} eq 'handle') {
+               $override->{$first->{name}}->{type} = 'instance';
+       }
+
+       # extension function default template
+       if (defined($s->{extensions})) {
+               $override->{template} = 'method-extension';
+       }
+
+       # Constructor
+       if ($last->{deref} eq 'handle*') {
+               if (!$last->{len}) {
+                       my $t = $api->{handles}->{$last->{baseType}};
+                       print "constructor: $s->{name}\n" if $sys->{verbose};
+                       $override->{$last->{name}}->{type} =
+                               $t->{type} eq 'VK_DEFINE_HANDLE' && $t->{name} ne 'VkInstance' ?
+                               'dispatch*-output' : 'handle*-output';
+               } elsif ($index->{$last->{len}}) {
+                       print "constructor?: $s->{name}\n";# if $sys->{verbose};
+                       die;
+               } else {
+                       print "allocate-constructor?: $s->{name} $last->{len}\n";# if $sys->{verbose};
+               }
+       }
+
+       # turn array-query functions into auto-allocate/return types
+       # ones we care about with output
+       #       handle*-length
+       #       struct*-length
+       #       uint32_t*-length
+       #       void*-length
+
+       if ($last->{deref} =~ m/-length$/ && (my $len = $index->{$last->{len}})) {
+               if (index($len->{deref}, '*') >= 0) {
+                       my $protoa = "$result->{fullType} $s->{name}("
+                               .join(', ', map { "$_->{fullType}" } @{$s->{items}})
+                               .")";
+                       print "array-constructor: $protoa\n" if $sys->{verbose} > 1;
+
+                       my $otype = ($last->{deref} eq 'handle*-length' && $api->{handles}->{$last->{baseType}}->{type} eq 'VK_DEFINE_HANDLE')
+                               ? 'dispatch*-length-query' : $last->{deref}.'-query';
+                       my $ltype = $len->{deref}.'-querylen';
+
+                       die "no template $otype" if !defined($commandTypes->{types}->{$otype});
+                       die "no template $ltype" if !defined($commandTypes->{types}->{$ltype});
+
+                       $override->{template} = defined($s->{extensions}) ? 'method-extension-query' : 'method-query';
+                       $override->{$last->{name}}->{type} = $otype;
+                       $override->{$len->{name}}->{type} = $ltype;
+               }
+       }
+
+       # A whole bunch of query functions
+       if ($s->{name} =~ m/^vkGet/ && !($last->{fullType} =~ m/const/n) && !(defined($override->{$last->{name}}) && defined($override->{$last->{name}}->{type}))) {
+               if ($last->{fullType} eq 'VkBool32 *') {
+                       print "output: [$last->{fullType}] [$last->{deref}] $s->{name}\n" if $sys->{verbose};
+                       $override->{$last->{name}}->{type} = 'VkBool32*-output';
+               } elsif ($last->{deref} =~ m/^(int|uint32_t|uint64_t|void)\*$/on) {
+                       print "output: [$last->{fullType}] [$last->{deref}] $s->{name}\n" if $sys->{verbose};
+                       $override->{$last->{name}}->{type} = $last->{deref}."-output";
+               } else {
+                       print "output? [$last->{fullType}] [$last->{deref}] $s->{name}\n" if $sys->{verbose};
+               }
+       }
+
+       # other per-item things
+       foreach my $m (@{$s->{items}}) {
+               my $so = $structTypes->{overrides}->{$m->{baseType}};
+
+               if ($so->{ignore}) {
+                       die "No type '$m->{deref}-ignore'" if !defined($types->{$m->{deref}.'-ignore'});
+                       $override->{$m->{name}}->{type} = $m->{deref}.'-ignore';
+               }
+       }
+
+       $overrides->{$s->{name}} = $override if %{$override};
+}
+
+{
+       my $overrides = $structTypes->{overrides};
+       my $templates = $structTypes->{templates};
+
+       foreach my $k (keys %defaultTemplate) {
+               next if defined($overrides->{$k}) && defined($overrides->{$k}->{template});
+
+               my $t = $defaultTemplate{$k};
+               my $name = $t->{name} || "struct-writeonly";
+
+               $name .= "-array" if $t->{array};
+
+               print "$name: $k\n" if $sys->{verbose} > 1;
+               die "No override $k $name" if !$templates->{$name};
+               $overrides->{$k}->{template} = $name;
+       }
+}
+
+printf "%9.6f guess overrides\n", (($next = clock_gettime(CLOCK_REALTIME)) - $now); $now = $next;
+
+if (0) {
+       analyseAccessors($api);
+       exit 1;
+}
+
+if (0) {
+       open(my $f, '>', 'types.pm');
+       print $f Dumper($commandTypes, $structTypes);
+       close $f;
+}
+
+if (0) {
+       open(my $f, '>', 'api.pm');
+       print $f Dumper($api);
+       close $f;
+       print "Dumped api.pm\n";
+       exit 1;
+}
+
+if (0) {
+       open(my $f, '>', 'vk.pm');
+       print $f Dumper($vk);
+       close $f;
+       die;
+}
+
+exportVulkan($vk, $api, $structTypes);
+
+# dump out the extension function-pointer tables
+{
+       my $f = openOutput($sys, 'DispatchInstance');
+       my $template = $structTypes->{templates}->{dispatch};
+       my @init = ();
+       my @fieldInit = ();
+
+       foreach my $k (sort keys %{$api->{commands}}) {
+               my $c = $api->{commands}->{$k};
+
+               next if !defined($c->{extensions});
+
+               push @fieldInit, code::formatTemplate($template->{'field-init'}, $c);
+               push @init, code::formatTemplate($template->{'init'}, $c);
+
+       }
+
+       my $vars = {
+               package => 'vulkan',
+               Name => 'DispatchInstance',
+               init => join("\n\t\t", @init),
+               'field-init' => join("\n\t", @fieldInit),
+       };
+       print $f code::formatTemplateStream($template->{class}, $vars);
+
+       closeOutput($sys, 'DispatchInstance', $f);
+}
+
+# structs and unions
+foreach my $k (sort keys %{$api->{types}}) {
+       my $s = $api->{data}->{$k};
+
+       die if !defined $s;
+       next if $s->{alias};
+
+       my $override = $structTypes->{overrides}->{$s->{name}};
+
+       next if $override->{ignore};
+
+       my $f = openOutput($sys, $s->{Name});
+       print $f formatStruct($vk, $api, $s);
+       closeOutput($sys, $s->{Name}, $f);
+}
+
+# handles
+foreach my $k (sort keys %{$api->{handles}}) {
+       my $s = $api->{data}->{$k};
+
+       die if !defined $s;
+       next if $s->{alias};
+
+       my $f = openOutput($sys, $s->{name});
+       print $f formatHandle($vk, $api, $s);
+       closeOutput($sys, $s->{name}, $f);
+}
+
+# upcalls
+foreach my $k (sort keys %{$api->{funcpointers}}) {
+       my $s = $api->{data}->{$k};
+
+       die if !defined $s;
+       next if $s->{alias};
+
+       my $override = $commandTypes->{overrides}->{$s->{name}};
+
+       next if $override->{ignore};
+
+       my $f = openOutput($sys, $s->{name});
+       print $f formatFunctionPointer($vk, $api, $s);
+       closeOutput($sys, $s->{name}, $f);
+}
+
+printf "%9.6f export classes\n", (($next = clock_gettime(CLOCK_REALTIME)) - $now); $now = $next;
+
+exit 0;
+
+# ###################################################################### #
+
+sub formatStructLayout {
+       my $types = shift;
+       my $s = shift;
+       my $offset = 0;
+       my @fields = ();
+
+       # This doens't need to worry about overrides
+
+       if ($s->{category} eq 'struct') {
+               foreach my $m (@{$s->{items}}) {
+                       my $type = $types->{$m->{deref}};
+                       my $diff = $m->{bitOffset} - $offset;
+
+                       push @fields, "MemoryLayout.paddingLayout($diff)" if $diff;
+
+                       if ($type) {
+                               my $v = buildVars($s, $m, $type);
+
+                               push @fields, code::formatTemplate($v->{layout}, $v).".withName(\"$m->{name}\") /* $m->{deref} $m->{fullType} */";
+                               $offset = $m->{bitOffset} + $m->{bitSize};
+                       } else {
+                               push @fields, "/* Missing: $m->{deref} $m->{name} */";
+                       }
+               }
+       } else {
+               foreach my $m (@{$s->{items}}) {
+                       my $type = $types->{$m->{deref}};
+
+                       if ($type) {
+                               my $v = buildVars($s, $m, $type);
+
+                               push @fields, code::formatTemplate($v->{layout}, $v).".withName(\"$m->{name}\") /* $m->{deref} $m->{fullType} */";
+                               $offset = ($m->{bitOffset} + $m->{bitSize}) if ($m->{bitOffset} + $m->{bitSize}) > $offset;
+                       } else {
+                               push @fields, "/* Missing: $m->{deref} $m->{name} */";
+                       }
+               }
+       }
+
+       my $diff = $s->{bitSize} - $offset;
+
+       push @fields, "MemoryLayout.paddingLayout($diff)" if $diff;
+
+       return "MemoryLayout.".$s->{category}."Layout(\n\t\t".join(",\n\t\t", @fields).").withName(\"$s->{name}\")";
+}
+
+# Experiment: see if sharing varhandles would be of any benefit
+#  - analyse all pointer/int fields for cross-overs
+# for vulkan 1.3:
+#  Total fields: 3370
+#  Unique fields: 368
+# so very significant savings possible
+sub analyseAccessors {
+       my $api = shift;
+       my %handles;
+       my $total;
+       my %lookat;
+
+       map { $lookat{$_} = 1 } qw(Memory.POINTER Memory.BYTE Memory.SHORT Memory.INT Memory.LONG Memory.FLOAT Memory.DOUBLE);
+
+       foreach my $s (values %{$api->{types}}) {
+               my $override = $structTypes->{overrides}->{$s->{name}};
+
+               next if $s->{alias};
+               next if $override->{ignore};
+
+               foreach my $m (@{$s->{items}}) {
+                       my $deref = $m->{deref};
+                       my $type = $structTypes->{types}->{$deref};
+                       my $v = buildVars($s, $m, $type);
+                       my $layout = code::formatTemplate($v->{layout}, $v);
+                       my $index = $m->{bitOffset} / 8;
+
+                       die if ($m->{bitoffset} & ($m->{bitSize}-1));
+
+                       if ($lookat{$layout}) {
+                               $index = $m->{bitOffset} / $m->{bitSize};
+                       }
+
+                       my $k = sprintf "$layout \@ %03d", $index;
+
+                       $handles{$k}++;
+                       $total++;
+               }
+       }
+
+       print "Other unique handles =\n";
+       foreach my $h (sort keys %handles) {
+               $h =~ m/^(.*) \@/;
+               my $x = $lookat{$1};
+               if (!$lookat{$1}) {
+                       printf " %5d $h\n", $handles{$h};
+               }
+       }
+       print "Unique handles of interest =\n";
+       foreach my $h (sort keys %handles) {
+               $h =~ m/^(.*) \@/;
+               if ($lookat{$1}) {
+                       printf " %5d $h\n", $handles{$h};
+               }
+       }
+       my @keys = keys %handles;
+       print "Total fields: $total\n";
+       print "Unique fields: $#keys\n";
+}
+
+# ###################################################################### #
+
+sub loadTypes {
+       my $api = shift;
+       my $file = shift;
+       my $config = new config({ includes=>[ $FindBin::Bin ] }, "$FindBin::Bin/$file");
+
+       my $types = {};
+       my $templates = {};
+       my $overrides = {};
+
+       foreach my $t (@{$config->{objects}}) {
+               if ($t->{type} eq 'type') {
+                       my $nopts = $#{$t->{options}};
+                       my $type = {};
+
+                       die ("No prototype provided/found for empty type ".Dumper($t)) if ($#{$t->{items}} < 0 && $#{$t->{options}} < 0);
+
+                       # Check options / load inherited values
+                       foreach my $o (@{$t->{options}}) {
+                               if ($o =~ m/^accessor=(.*)$/o) {
+                                       die "No template $1" if !defined($templates->{$1});
+                                       $type->{accessor} = $templates->{$1};
+                               } elsif ($o eq 'need-frame') {
+                                       $type->{'need-frame'} = 1;
+                               } elsif ($o eq 'need-scope') {
+                                       $type->{'need-scope'} = 1;
+                               } elsif ($o eq 'trampoline-scope') {
+                                       $type->{'trampoline-scope'} = 1;
+                               } elsif ($o eq 'need-alloc') {
+                                       $type->{'need-alloc'} = 1;
+                               } elsif ($o eq 'is-instance') {
+                                       $type->{'is-instance'} = 1;
+                               } elsif (defined($types->{$o})) {
+                                       $type = { %$type, %{$types->{$o}} };
+                               } else {
+                                       die "Unknown option '$o' in ".Dumper($t);
+                               }
+                       }
+
+                       # Load the fields
+                       foreach my $s (@{$t->{items}}) {
+                               if (defined $s->{literal}) {
+                                       $type->{$s->{match}} = $s->{literal};
+                               } elsif ($#{$s->{options}} >= 0) {
+                                       $type->{$s->{match}} = $s->{options}->[$#{$s->{options}}];
+                               } else {
+                                       $type->{$s->{match}} = 0;
+                               }
+
+                               $type->{"$s->{match}:eval"} = 1 if config::optionFlag('eval', $s);
+                       }
+
+                       # Write to all aliases
+                       foreach my $k (split /,/,$t->{name}) {
+                               $types->{$k} = $type;
+                       }
+               } elsif ($t->{type} eq 'code') {
+                       my $code = {
+                               insert => {},
+                       };
+
+                       foreach my $o (@{$t->{options}}) {
+                               if ($o =~ m/insert=(.*)/) {
+                                       foreach my $x (split /,/,$1) {
+                                               if ($x =~ m/(.*):(.*)/) {
+                                                       die "$x $t->{name} ".Dumper($templates->{$1}) if !defined $templates->{$1}->{$2};
+                                                       $code->{insert}->{$2} = $templates->{$1}->{$2};
+                                               }
+                                       }
+                               } elsif ($o =~ m/^fields=(.*)/) {
+                                       $code->{fields} = $1;
+                               } elsif (defined($templates->{$o})) {
+                                       $code = { %$code, %{$templates->{$o}} };
+                               } else {
+                                       die ("Unknown option '$o'");
+                               }
+                       }
+
+                       foreach my $s (@{$t->{items}}) {
+                               if (defined  $s->{literal}) {
+                                       my $t = $s->{literal};
+
+                                       $t =~ s/^\t//gm;
+                                       $code->{$s->{match}} = $t;
+                               } else {
+                                       delete $code->{$s->{match}};
+                               }
+
+                               $code->{"$s->{match}:eval"} = 1 if config::optionFlag('eval', $s);
+                       }
+
+                       $templates->{$t->{name}} = $code;
+               } elsif ($t->{type} eq 'override') {
+                       foreach my $s (@{$t->{items}}) {
+                               my $c = { };
+                               foreach my $o (@{$s->{options}}) {
+                                       if ($o =~ m/^(.*)=type:(.*)/) {
+                                               die "No such type $s->{match} $2\n" if !defined $types->{$2};
+                                               $c->{$1}->{type} = $2;
+                                       } elsif ($o =~ m/^(.*)=accessor:(.*)/) {
+                                               die "No accessor template $o" if !defined($templates->{$2});
+                                               $c->{$1}->{accessor} = $templates->{$2};
+                                       } elsif ($o =~ m/^template=(.*)/) {
+                                               die "No template $o" if !defined($templates->{$1});
+                                               $c->{template} = $1;
+                                       } elsif ($o eq 'expand') {
+                                               $c->{expand} = 1;
+                                       } elsif ($o eq 'ignore') {
+                                               $c->{ignore} = 1;
+                                       } elsif ($o eq 'append') {
+                                               $c->{append} = $s->{literal};
+                                       }
+                               }
+                               $overrides->{$s->{match}} = $c;
+                       }
+               }
+       }
+
+       # templates should probably just go in one
+       {
+               types => $types,
+               templates => $templates,
+               overrides => $overrides,
+       };
+}
+
+sub uniq {
+  my %seen;
+  return grep { !$seen{$_}++ } @_;
+}
+
+# generate Vulkan.java
+sub exportVulkan {
+       my $vk = shift;
+       my $api = shift;
+       my $structTypes = shift;
+       my $seen = {};
+       my $template = $structTypes->{templates}->{Vulkan};
+
+       my @constants = ();
+       my @defines = ();
+
+       # create error code->string mappings
+       {
+               my $s = $api->{enums}->{VkResult};
+               my @infos;
+
+               foreach my $m (sort { $a->{value} <=> $b->{value} } @{$s->{items}}) {
+                       next if ($m->{alias});
+                       push @infos, "new ErrorInfo($m->{value}, \"$m->{name}\")";
+               }
+               push @constants, "static final ErrorInfo errors[] = {";
+               push @constants, join(",\n\t\t", @infos);
+               push @constants, "};\n";
+       }
+
+       # special case for api constants
+       {
+               my $s = $api->{data}->{'API Constants'};
+
+               push @constants, "// API Constants\n";
+
+               foreach my $m (@{$s->{items}}) {
+                       next if defined($m->{alias});
+                       next if $seen->{$m->{name}}++;
+
+                       my $i = $enumInfo->{$m->{type}};
+                       my $v = $m->{value};
+
+                       # convert to java
+                       $v =~ s/[()ULF]+//gon if (!($v =~ m/^"/));
+
+                       push @constants,"public final static $i->{type} $m->{name} = $v$i->{suffix};";
+               }
+       }
+
+       # include any defines we have a template for
+       foreach my $k (sort keys %{$api->{defines}}) {
+               if ($template->{$k}) {
+                       push @defines, $template->{$k};
+               } else {
+                       print "Ignored: $k\n";
+               }
+       }
+       foreach my $k (sort keys %{$api->{enums}}) {
+               my $s = $api->{data}->{$k};
+               my $type = $s->{fullType} ? $s->{fullType} : 'VkFlags';
+               my $i = $enumInfo->{$vk->{data}->{$type}->{type}};
+               my $ext;
+
+               next if $s->{alias};
+
+               push @constants, "";
+               push @constants, "// $s->{name} $type";
+
+               my $index = {};
+               map { $index->{$_->{name}} = $_ } (@{$s->{items}});
+
+               foreach my $m (@{$s->{items}}) {
+                       next if defined($m->{alias});
+                       next if $seen->{$m->{name}}++;
+
+                       my $v = $m->{value};
+
+                       $v = 1<<$m->{bitpos} if !defined($v) && defined($m->{bitpos});
+
+                       die Dumper("Ca't work out value", $m, $s) if !defined($v);
+
+                       if (defined($m->{extension}) && $m->{extension} ne $ext) {
+                               $ext = $m->{extension};
+                               push @constants, "// from $ext";
+                       }
+
+                       push @constants, "public final static $i->{type} $m->{name} = $v$i->{suffix};";
+               }
+               $ext = "";
+               foreach my $m (@{$s->{items}}) {
+                       next if !defined($m->{alias});
+                       next if $seen->{$m->{name}}++;
+                       next if !defined($index->{$m->{alias}});
+
+                       if (defined($m->{extension}) && $m->{extension} ne $ext) {
+                               $ext = $m->{extension};
+                               push @constants, "// from $ext";
+                       }
+
+                       push @constants, "public final static $i->{type} $m->{name} = $m->{alias};";
+               }
+       }
+
+       my $v = {
+               package => $sys->{package},
+               defines => join("\n\t", @defines),
+               constants => join("\n\t", @constants),
+       };
+
+       my $f = openOutput($sys, 'Vulkan');
+
+       print $f code::formatTemplateStream($template->{class}, $v);
+
+       closeOutput($sys, 'Vulkan', $f);
+}
+
+# ###################################################################### #
+# class.name to class/name.java
+sub classToPath {
+       my $sys = shift;
+       my $name = shift;
+
+       $name = $sys->{package}.'.'.$name;
+       $name =~ s@\.@/@g;
+       $name = $sys->{output}.'/'.$name.'.java';
+       $name;
+}
+
+sub closeOutput {
+       my $sys = shift;
+       my $name = shift;
+       my $f = shift;
+       my $path = classToPath($sys, $name);
+
+       close($f) || die;
+       rename($path.'~', $path) || die ("rename failed: $!");
+}
+
+sub openOutput {
+       my $sys = shift;
+       my $name = shift;
+
+       my $path = classToPath($sys, $name);
+       my $dir = dirname($path);
+
+       make_path($dir) if (!-d $dir);
+
+       open(my $f, ">", $path.'~') || die ("Cannot open '$path' for writing");
+       print "writing '$path'\n" if $sys->{verbose} > 0;
+       $f;
+}
+
+
+# Calculate canonical derefernce types for each field and parameter
+sub analyseFields {
+       my $vk = shift;
+       my $api = shift;
+       my $seen = shift;
+       my $s = shift;
+
+       # what about const?
+       # FIXME: bitfields
+
+       my $index = {};
+
+       map { $index->{$_->{name}} = $_ } @_;
+
+       $s->{index} = $index;
+
+       foreach my $m (@_) {
+               my $t = $api->{data}->{$m->{baseType}};
+               my $nstar = $m->{fullType} =~ tr/*/*/;
+               my $type;
+               my $array = '';
+
+               # Check array sizes
+               if ($m->{fullType} =~ m/\[(.*)\]\[(.*)\]$/o) {
+                       $array = '[][]';
+                       $m->{len1} = $1;
+                       $m->{len2} = $2;
+               } elsif ($m->{fullType} =~ m/\[(.*)\]$/o) {
+                       my $isize = $1;
+                       my $size;
+                       if ($isize =~ m/^\d+$/n) {
+                               $size = $isize;
+                       } else {
+                               $size = $vk->{data}->{'API Constants'}->{index}->{$isize}->{value};
+                       }
+                       $array = '[]';
+                       $m->{len1} = $size;
+               } elsif ($m->{fullType} =~ m/[\[\]]/on) {
+                       die Dumper($m)
+               }
+
+               if (!defined($t)) {
+                       # will be primitive or external
+                       $type = $m->{baseType} if ($nstar == 0);
+                       $type = "$m->{baseType}*" if ($nstar == 1);
+                       $type = "$m->{baseType}**" if ($nstar == 2);
+                       die if $nstar > 2;
+               } else {
+                       # unmap aliases
+                       while ($t->{alias}) {
+                               print "alias $t->{name} -> $t->{alias}\n";
+                               $t = $api->{data}->{$t->{alias}};
+                               die if !defined $t;
+                               $m->{baseType} = $t->{name};
+                       }
+
+                       if ($t->{category} eq 'enum') {
+                               $t = $vk->{data}->{$t->{fullType}};
+                               $type = $t->{type} if ($nstar == 0);
+                               $type = "$t->{type}*" if ($nstar == 1);
+                               die if $nstar > 1;
+                       } elsif ($t->{category} =~ m/struct|union/on) {
+                               $m->{type} = $t->{name};
+                               $type = 'struct' if ($nstar == 0);
+                               $type = 'struct*' if ($nstar == 1);
+                               $type = 'struct**' if ($nstar == 2);
+                               die if $nstar > 2;
+                       } elsif ($t->{category} eq 'handle') {
+                               $m->{type} = $t->{name};
+                               #if ($t->{type} eq 'VK_DEFINE_HANDLE') {
+                               #       $type = 'dhandle' if ($nstar == 0);
+                               #       $type = 'dhandle*' if ($nstar == 1);
+                               #} else {
+                                       $type = 'handle' if ($nstar == 0);
+                                       $type = 'handle*' if ($nstar == 1);
+                               #}
+                               die if $nstar > 1;
+                       } elsif ($t->{category} eq 'basetype') {
+                               # ??
+                               $type = $t->{type} if ($nstar == 0);
+                               $type = "$t->{type}*" if ($nstar == 1);
+                               die Dumper($m, $t) if $nstar > 1;
+                       } elsif ($t->{category} eq 'funcpointer') {
+                               $m->{type} = $t->{name};
+                               $type = "funcpointer" if ($nstar == 0);
+                               die if $nstar > 0;
+                       } else {
+                               die Dumper($m, $t);
+                       }
+               }
+
+               if ($s->{category} eq 'struct') {
+                       my $mover = $structTypes->{overrides}->{$m->{baseType}};
+                       $type .= "-expand" if $nstar == 0 && $mover->{expand};
+               } elsif ($s->{category} eq 'union') {
+                       # VkClearValue - handled manually
+               }
+
+               # an array type with a length
+               if ($nstar > 0 && $m->{len}) {
+                       if ($s->{category} =~ m/struct|union/on) {
+                               if ($m->{altlen}) {
+                                       if ($m->{altlen} =~ m/^(.*)(VK_UUID_SIZE)(.*)$/) {
+                                               $m->{length} = $1.'Vulkan.'.$2.$3;
+                                       } elsif ($m->{altlen} =~ m/^([^a-zA-Z_]*)([a-zA-Z_]+)(.*)$/) {
+                                               my $len = $index->{$2};
+                                               if (defined $len) {
+                                                       $m->{length} = $1.'get'.ucfirst($2).'()'.$3;
+                                               }
+                                       } else {
+                                               die "Unhandled len/altlen: ".Dumper($m);
+                                       }
+                               } elsif ($m->{len} =~ m/(.*),null-terminated/) {
+                                       my $len = $index->{$1};
+                                       if (defined $len) {
+                                               $m->{length} = "get$len->{Name}()";
+                                       }
+                               } elsif ($m->{len} eq 'null-terminated') {
+                                       # ignore
+                               } elsif ($m->{len} =~ m/^(.*),(\d+)$/) {
+                                       # ignore?
+                               } else {
+                                       my $len = $index->{$m->{len}};
+                                       if (defined $len) {
+                                               my $cast = ($len->{fullType} eq 'uint32_t') ? '(int)' : '';
+
+                                               die "Not simple type" if ($len->{fullType} ne $len->{baseType});
+                                               $m->{length} = "get$len->{Name}()";
+                                       } else {
+                                               die "what?".Dumper($m);
+                                       }
+                               }
+                       } elsif ($s->{category} eq 'command') {
+                               if ($m->{altlen}) {
+                                       die;
+                               } else {
+                                       $m->{length} = $m->{len} if $index->{$m->{len}};
+                                       $index->{$m->{len}}->{lengthfor} = $m->{name} if $index->{$m->{len}};
+                               }
+                       } else {
+                               die Dumper($s);
+                       }
+                       $type = $type.'-length' if $m->{length};
+               }
+
+               $seen->{$m->{fullType}} = $type.$array;
+               $m->{deref} = $type.$array;
+
+               # Calculate name, with some fixup hacks
+               my $name = $m->{name};
+               #if ($s->{type} =~ m/struct|union/on)
+               {
+                       # Strip leading 'p' for pointers
+                       if ($name eq 'ppGeometries') { # && $s->{name} eq 'VkAccelerationStructureBuildGeometryInfoKHR') {
+                               $name = 'PGeometries';
+                       } elsif ($nstar > 0 && $name =~ m/^p{$nstar}/) {
+                               my $strip = $nstar;
+
+                               if ($t->{category} eq 'handle' && $type ne 'handle*-length') {
+                                       $strip -= 1;
+                               }
+
+                               $name = substr $name, $strip;
+                       }
+
+                       $name =~ s/^pfn//o if $type eq 'funcpointer';
+                       # CamelCase
+                       $name =~ s/(?:^|_)(.)/\U$1/og;
+               }
+               $m->{Name} = $name;
+       }
+}
+
+sub analyseTypes {
+       my $vk = shift;
+       my $api = shift;
+       my $seen = {};
+
+       foreach my $s (grep { $_->{items} } values %{$api->{types}}) {
+               analyseFields($vk, $api, $seen, $s, @{$s->{items}});
+
+               my $name = $s->{name};
+               $name =~ s/(?:^|_)(.)/\U$1/og;
+               $s->{Name} = $name;
+
+               my $first = $s->{items}->[0];
+               my $second = $s->{items}->[1];
+
+               if ($first->{name} eq 'sType' && $second && $second->{name} eq 'pNext') {
+                       $first->{'no-setall'} = 1;
+                       $second->{'no-setall'} = 1;
+                       print "typed: $s->{name}\n" if $sys->{verbose} > 1;
+               } else {
+                       print "untyped: $s->{name}\n" if $sys->{verbose} > 1;
+               }
+       }
+
+       foreach my $c (values %{$api->{funcpointers}}) {
+               analyseFields($vk, $api, $seen, $c, $c->{proto}, @{$c->{items}});
+       }
+
+       foreach my $c (values %{$api->{commands}}) {
+               $c->{proto}->{name} = 'result$';
+               $c->{proto}->{Name} = 'result$';
+               analyseFields($vk, $api, $seen, $c, $c->{proto}, @{$c->{items}});
+
+               # collect all member functions on handles
+               my $first = $c->{items}->[0];
+               my $last = $c->{items}->[$#{$c->{items}}];
+               if ($first->{deref} eq 'handle') {
+                       my $t = $api->{handles}->{$first->{baseType}};
+                       while ($t->{alias}) {
+                               $t = $api->{handles}->{$t->{alias}};
+                       }
+                       die "No handle found ".Dumper($c) if !defined $t;
+                       push @{$t->{commands}}, $c->{name};
+               } elsif ($c->{name} =~ m/vkEnumerateInstance|vkCreateInstance/) {
+                       push @{$api->{handles}->{'VkInstance'}->{commands}}, $c->{name};
+               } else {
+                       die "No owner for call ".Dumper($c);
+               }
+       }
+
+       if ($sys->{verbose}) {
+               print "Unique Types:\n";
+               my $base = {};
+               map { $base->{$_} = 1 } values %$seen;
+
+               foreach my $k (sort keys %$base) {
+                       print " $k\n";
+               }
+       }
+}
+
+# what else?
+# vk? api?
+# this way-over-evaluates, probably only call once on every field and member instead
+sub buildVars {
+       my $s = shift;
+       my $m = shift;
+       my $type = shift;
+       my $v = { %{$m} };
+
+       foreach my $k (grep { index($_, ':') == -1 } keys %$type) {
+               my $t = $type->{$k};
+
+               if ($type->{"$k:eval"}) {
+                       $v->{$k} = $t;
+                       die "Eval failed: $! $@: ".Dumper($m, $type) if !defined($v->{$k});
+               } elsif ($t) {
+                       $v->{$k} = $t;
+               }
+       }
+
+       $v;
+}
+
+sub formatStructLayout {
+       my $types = shift;
+       my $s = shift;
+       my $offset = 0;
+       my @fields = ();
+
+       # This doens't need to worry about overrides
+
+       if ($s->{category} eq 'struct') {
+               foreach my $m (@{$s->{items}}) {
+                       my $type = $types->{$m->{deref}};
+                       my $diff = $m->{bitOffset} - $offset;
+
+                       push @fields, "MemoryLayout.paddingLayout($diff)" if $diff;
+
+                       if ($type) {
+                               my $v = buildVars($s, $m, $type);
+
+                               push @fields, code::formatTemplate($v->{layout}, $v).".withName(\"$m->{name}\") /* $m->{deref} $m->{fullType} */";
+                               $offset = $m->{bitOffset} + $m->{bitSize};
+                       } else {
+                               push @fields, "/* Missing: $m->{deref} $m->{name} */";
+                       }
+               }
+       } else {
+               foreach my $m (@{$s->{items}}) {
+                       my $type = $types->{$m->{deref}};
+
+                       if ($type) {
+                               my $v = buildVars($s, $m, $type);
+
+                               push @fields, code::formatTemplate($v->{layout}, $v).".withName(\"$m->{name}\") /* $m->{deref} $m->{fullType} */";
+                               $offset = ($m->{bitOffset} + $m->{bitSize}) if ($m->{bitOffset} + $m->{bitSize}) > $offset;
+                       } else {
+                               push @fields, "/* Missing: $m->{deref} $m->{name} */";
+                       }
+               }
+       }
+
+       my $diff = $s->{bitSize} - $offset;
+
+       push @fields, "MemoryLayout.paddingLayout($diff)" if $diff;
+
+       return "MemoryLayout.".$s->{category}."Layout(\n\t\t".join(",\n\t\t", @fields).").withName(\"$s->{name}\")";
+}
+
+sub formatAccessorIf {
+       my $s = shift;
+       my $m = shift;
+       my $info = shift;
+       my $type = shift;
+       my $field = shift;
+       my $v = shift;
+       my $accessor = $type->{accessor};
+       my $template = $accessor->{$field};
+
+       if ($template) {
+               $template = eval($template) if ($accessor->{"$field:eval"});
+
+               die "Error executing template $field $accessor->{$field}: $@" if (!defined($template));
+
+               push @{$info->{$field}}, code::formatTemplate($template, $v) if $template;
+       }
+}
+
+sub formatStruct {
+       my $vk = shift;
+       my $api = shift;
+       my $s = shift;
+       my $templates = $structTypes->{templates};
+       my $types = $structTypes->{types};
+       my $overrides = $structTypes->{overrides};
+
+       my $override = $overrides->{$s->{name}};
+       my $tempname = $override->{template} ? $override->{template} : 'struct-writeonly';
+       my $template = $templates->{$tempname};
+       my @fields = split(/,/, $template->{fields});
+       my $info;
+
+       map { $info->{$_} = [] } @fields;
+
+       my $setall = defined $info->{'java-setall'};
+
+       # unions need multiple constructors
+       if ($setall) {
+               if ($s->{category} eq 'struct') {
+                       $info->{create}->{create} = {
+                               'setall-arg' => [],
+                               setall => [],
+                               setallat => [],
+                       };
+               } elsif ($s->{category} eq 'union') {
+                       foreach my $m (@{$s->{items}}) {
+                               $info->{create}->{"create$m->{Name}"} = {
+                                       'setall-arg' => [],
+                                       setall => [],
+                                       setallat => [],
+                               };
+                       }
+               } else {
+                       die;
+               }
+       }
+
+       my $needAlloc = 0;
+
+       # Collect all fields
+       foreach my $m (@{$s->{items}}) {
+               my $nstar = $m->{deref} =~ tr/*/*/;
+               my $mover = $override->{$m->{name}};
+               my $deref = defined($mover->{type}) ? $mover->{type} : $m->{deref};
+               my $type = $types->{$deref};
+
+               die "No type $deref ".Dumper($m, $s) if !$type;
+               die Dumper($mover, $type) if $mover->{accessor};
+
+               if ($type->{accessor}) {
+                       my $v = buildVars($s, $m, $type);
+                       my @todump;
+
+                       if ($m->{values}) {
+                               @todump = qw(init initat init-array set setat);
+                       } else {
+                               @todump = qw(get getat set setat getorset getorsetat);
+
+                               if ($setall && !$m->{'no-setall'}) {
+                                       my $create = $s->{category} eq 'struct' ? $info->{create}->{create} : $info->{create}->{"create$m->{Name}"};
+                                       formatAccessorIf($s, $m, $create, $type, 'setall-arg', $v);
+                                       formatAccessorIf($s, $m, $create, $type, 'setall', $v);
+                                       formatAccessorIf($s, $m, $create, $type, 'setallat', $v);
+
+                                       $needAlloc = 1 if $type->{'need-alloc'};
+                               }
+                       }
+
+                       push @{$info->{handle}}, code::formatTemplate($v->{handle}, $v)." /* $m->{name} $m->{fullType} ($m->{deref}) */" if $info->{handle} && $v->{handle};
+                       push @{$info->{handleat}}, code::formatTemplate($v->{handleat}, $v) if $info->{handleat} && $v->{handleat};
+
+                       foreach my $field (@todump) {
+                               if ($info->{$field} && $type->{accessor}->{$field}) {
+                                       my $t = $type->{accessor}->{$field};
+
+                                       $t = eval $t if $type->{accessor}->{"$field:eval"};
+                                       die "$type->{accessor}->{$field}\n---\n$@" if !defined($t);
+
+                                       push @{$info->{$field}}, code::formatTemplate($t, $v) if $t;
+                               }
+                       }
+               }
+       }
+
+       # create constructors
+       my $v = {
+               package => 'vulkan',
+               name => $s->{name},
+               Name => $s->{Name},
+               layout => formatStructLayout($types, $s),
+               append =>  defined($override->{append}) ? $override->{append} : '',
+       };
+
+       foreach my $field (@fields) {
+               $v->{$field} = join("\n", @{$info->{$field}});
+       }
+
+       # build sub-components using the full $v
+       my @createAll = ();
+       foreach my $k (keys %{$template->{insert}}) {
+               my $t = $template->{insert}->{$k};
+
+               die if (!defined($t));
+
+               if ($k eq 'create-all' || $k eq 'set-all' || $k eq 'setat-all') {
+                       if ($setall) {
+                               foreach my $kk (keys %{$info->{create}}) {
+                                       my $create = $info->{create}->{$kk};
+
+                                       if ($#{$create->{'setall-arg'}} >= 0) {
+                                               my $v = {
+                                                       create => $kk,
+                                                       set => $kk =~ s/create/set/r,
+                                                       Name => $s->{Name},
+                                                       'java-setall-arguments' => join (', ', @{$create->{'setall-arg'}}),
+                                                       'java-setall' => join ("\n\t\t", @{$create->{setall}}),
+                                                       'java-setallat' => join ("\n\t\t", @{$create->{setallat}}),
+                                               };
+                                               push @createAll, code::formatTemplateStream($t, $v);
+                                       }
+                               }
+                       }
+               } else {
+                       $v->{$k} = code::formatTemplate($t, $v);
+               }
+       }
+       $v->{'create-all'} = join("\n", @createAll) if $setall;
+
+       #die Dumper($info, $v) if $s->{name} eq 'VkRect2D';
+
+       join("\n", map { '// '.$_ } split(/\n/,Dumper($s)))."\n".
+               code::formatTemplateStream($template->{class}, $v);
+}
+
+# TODO: the template here could be mapped from types.api perhaps?
+# also same for the various fields, init/getset/etc.
+sub formatHandle {
+       my $vk = shift;
+       my $api = shift;
+       my $s = shift;
+       my $templates = $structTypes->{templates};
+       my $overrides = $structTypes->{overrides};
+       my $override = $overrides->{$s->{name}};
+       my $tempname = $override->{template} ? $override->{template} : 'handle';
+       my $template = $templates->{$tempname};
+
+       my $info = {
+               init => [],
+               commands => [],
+       };
+
+       if (defined $s->{commands}) {
+               foreach my $k (sort @{$s->{commands}}) {
+                       my $c = $api->{commands}->{$k};
+                       push @{$info->{commands}}, formatFunction($api, $commandTypes, $c);
+               }
+       }
+
+       my $v = {
+               package => 'vulkan',
+               name => $s->{name},
+               Name => $s->{Name},
+               init => join ("\n", @{$info->{init}}),
+               commands => join ("\n", @{$info->{commands}}),
+               append =>  defined($override->{append}) ? $override->{append} : '',
+       };
+
+       code::formatTemplateStream($template->{class}, $v);
+}
+
+sub formatSignature {
+       my $s = shift;
+       my $types = $commandTypes->{types};
+       my $d = '(';
+
+       foreach my $m (@{$s->{items}}) {
+               my $x = $types->{$m->{deref}};
+
+               die "No sig defined ".Dumper($m) if !defined($x) || !defined($x->{sig});
+
+               $d .= $x->{sig};
+       }
+       $d .= ')';
+
+       my $m = $s->{proto};
+       my $x = $types->{$m->{deref}};
+       die "No sig defined ".Dumper($m) if !defined($x) || !defined($x->{sig});
+       $d .= $x->{sig};
+}
+
+# Forms all the parameter templates
+sub collectFunctionInfo {
+       my $api = shift;
+       my $ct = shift;
+       my $s = shift;
+       my $types = $ct->{types};
+       my $overrides = $ct->{overrides};
+       my $void = $s->{proto}->{fullType} eq 'void';
+       my $override = $overrides->{$s->{name}};
+
+       my @javaArgs = ();
+       my @invokeArgs = ();
+       my @nativeInit = ();
+       my @queryInit = ();
+       my @queryArgs = ();
+
+       my @nativeArgs = ();
+       my @trampArgs = ();
+
+       my $info = {
+               rename => $s->{name},
+               name => $s->{name},
+               'function-descriptor' => formatFunctionDescriptor($ct, $s),
+               'native-result-define' => '',
+               'native-result-assign' => '',
+               'native-result' => 'void',
+               'trampoline-result-define' => '',
+               'trampoline-result-assign' => '',
+               'trampoline-scope' => '',
+               'result-test' => '',
+               'create-frame' => '',
+               'java-result' => 'void',
+               'java-result-assign' => '',
+               'result-throw' => '',
+
+               'java-result-return' => 'return;',
+               'trampoline-result-return' => 'return;',
+       };
+
+       my $hasInstance = 0;
+       my $needFrame = 0;
+       my $needAlloc = 0;
+       my $needScope = 0;
+       my $trampScope = 0;
+
+       my @arrayFields = qw(java-arg invoke-arg native-init query-init query-arg native-arg trampoline-arg);
+       my @fixedFields = qw(java-result java-result-return java-result-assign);
+
+       map { $info->{$_} = [] } @arrayFields;
+
+       # Calculate parameters
+       foreach my $m (@{$s->{items}}) {
+               my $deref = defined($override->{$m->{name}}) && defined($override->{$m->{name}}->{type}) ? $override->{$m->{name}}->{type} : $m->{deref};
+               my $type = $types->{$deref};
+
+               die "No type found ".Dumper($m, $s, $override) if !$type;
+
+               my $v = buildVars($s, $m, $type);
+
+               foreach my $field (@arrayFields) {
+                       if ($v->{$field}) {
+                               push @{$info->{$field}}, code::formatTemplate($v->{$field}, $v);
+                       } elsif ($field eq 'query-arg') {
+                               push @{$info->{$field}}, code::formatTemplate($v->{'invoke-arg'}, $v);
+                       }
+               }
+
+               foreach my $field (@fixedFields) {
+                       $info->{$field} = code::formatTemplate($v->{$field}, $v) if ($v->{$field});
+               }
+
+               $needScope = 1 if $type->{'need-scope'};
+               $needFrame = 1 if $type->{'need-frame'};
+               $needAlloc = 1 if $type->{'need-alloc'};
+               $hasInstance = 1 if $type->{'is-instance'};
+               $trampScope = 1 if $type->{'trampoline-scope'};
+       }
+
+       $info->{'static'} = $hasInstance ? '' : 'static ';
+
+       # Handle default return types, others are handled by the fixedFields above
+       if ($s->{successcodes}) {
+               my @codes = split(/,/,$s->{successcodes});
+
+               $info->{'native-result-define'} = 'int result$;';
+               $info->{'native-result-assign'} = 'result$ = (int)';
+               $info->{'result-test'} = 'if ('.join("||", map { '(result$ == Vulkan.'.$_.')' } @codes).')';
+               $info->{'result-throw'} = 'throw new RuntimeException(Vulkan.getErrorMessage(result$));';
+
+               if ($#codes > 0 && $info->{'java-result'} eq 'void') {
+                       $info->{'java-result'} = 'int';
+                       $info->{'java-result-return'} = 'return result$;';
+               }
+       } elsif ($s->{proto}->{fullType} ne 'void') {
+               my $m = $s->{proto};
+               my $type = defined($override->{$m->{name}}) ? $override->{$m->{name}}->{type} : $types->{$m->{deref}.'-return'};
+
+               die "No type '$m->{deref}-return' ".Dumper($m, $s) if !defined($type);
+               die Dumper($m, $s) if !defined($type->{'java-result-return'});
+
+               my $v = buildVars($s, $m, $type);
+
+               $info->{'native-result-define'} = code::formatTemplate($v->{'native-result-define'}, $v);
+               $info->{'native-result-assign'} = code::formatTemplate($v->{'native-result-assign'}, $v);
+               #$info->{'native-result-define'}.= join("", map { "// $_\n" } split(/\n/, Dumper($m)));
+               $info->{'java-result'} = code::formatTemplate($v->{type}, $v);
+               $info->{'java-result-return'} = code::formatTemplate($v->{'java-result-return'}, $v);
+
+               $info->{'native-result'} = code::formatTemplate($v->{'carrier'}, $v);
+               $info->{'trampoline-result-define'} = code::formatTemplate($v->{'trampoline-result-define'}, $v);
+               $info->{'trampoline-result-assign'} = code::formatTemplate($v->{'trampoline-result-assign'}, $v);
+               $info->{'trampoline-result-return'} = code::formatTemplate($v->{'trampoline-result-return'}, $v);
+
+               $needScope = 1 if $type->{'need-scope'};
+       }
+
+       $info->{'create-frame'} = '(Frame frame$ = Frame.frame())' if $needFrame;
+       $info->{'trampoline-scope'} = '(ResourceScope scope$$ = ResourceScope.newConfinedScope())' if $trampScope;
+
+       push @{$info->{'java-arg'}}, 'SegmentAllocator alloc$' if $needAlloc;
+       push @{$info->{'java-arg'}}, 'ResourceScope scope$' if $needScope;
+
+       foreach my $field (@arrayFields) {
+               my $with = $field =~ m/-arg$/n ? ",\n\t" : "\n\t";
+               $info->{$field} = join $with, @{$info->{$field}};
+       }
+
+       $info->{successcodes} = $s->{successcodes} ? $s->{successcodes} : '';
+       $info->{errorcodes} = $s->{errorcodes} ? $s->{errorcodes}: '';
+
+       if ($s->{category} eq 'funcpointer') {
+               $info->{'trampoline-signature'} = formatSignature($s);
+       }
+
+       $info;
+}
+
+sub formatFunctionPointer {
+       my $vk = shift;
+       my $api = shift;
+       my $s = shift;
+       my $template = $commandTypes->{templates}->{'funcpointer-readwrite'};
+       my $info = collectFunctionInfo($api, $commandTypes, $s);
+
+       my $v = {
+               package => 'vulkan',
+               name => $s->{name},
+               Name => $s->{Name},
+       };
+
+       foreach my $k (keys %{$template->{insert}}) {
+               my $t = $template->{insert}->{$k};
+
+               $v->{$k} = code::formatTemplate($t, $info);
+       }
+
+       code::formatTemplateStream($template->{class}, $v);
+}
+
+sub formatFunctionDescriptor {
+       my $ct = shift;
+       my $s = shift;
+       my $types = $ct->{types};
+       my @fields = ();
+       my $void = $s->{proto}->{fullType} eq 'void';
+       my $override = $ct->{overrides}->{$s->{name}};
+
+       foreach my $m ($void ? () : $s->{proto}, @{$s->{items}}) {
+               my $deref = defined($override->{$m->{name}}) && defined($override->{$m->{name}}->{type}) ? $override->{$m->{name}}->{type} : $m->{deref};
+               my $type = $types->{$deref};
+
+               die "No type '$deref' found ".Dumper($m, $s, $override) if !$type;
+
+               my $v = buildVars($s, $m, $type);
+
+               push @fields, code::formatTemplate($v->{layout}, $v)." /* $m->{deref} $m->{name} */";
+       }
+
+       return ($void ? 'FunctionDescriptor.ofVoid(' : 'FunctionDescriptor.of(')
+               .join(",\n\t\t", @fields).')';
+}
+
+sub formatFunction {
+       my $api = shift;
+       my $ct = shift;
+       my $s = shift;
+       my $void = $s->{proto}->{fullType} eq 'void';
+       my $override = $ct->{overrides}->{$s->{name}};
+       my $tempname = $override->{template} ? $override->{template} : 'method';
+       my $template = $ct->{templates}->{$tempname};
+
+       my $info = collectFunctionInfo($api, $ct, $s);
+
+       #join("\n", map { '// '.$_ } split(/\n/,Dumper($s)))."\n".
+       " /* template: $tempname */\n".
+               code::formatTemplate($template->{invoke}, $info);
+}
diff --git a/src/notzed.vulkan/gen/struct-types.api b/src/notzed.vulkan/gen/struct-types.api
new file mode 100644 (file)
index 0000000..77c61ae
--- /dev/null
@@ -0,0 +1,1454 @@
+# -*- Mode:text; tab-width:4; electric-indent-mode: nil; indent-line-function:insert-tab; -*-
+
+# types for structs and unions
+
+# ###################################################################### #
+
+#accessor=code template
+# {type} - java type
+# {java-get}
+# {java-set}
+# {init}*
+# {init-array}*
+# {setall-arg}
+# {setall}
+
+# ###################################################################### #
+
+code value {
+  get {{
+       /* {deref} */
+       public {type} get{Name}() {
+               return {java-get};
+       }
+  }}
+  set {{
+       /* {deref} */
+       public void set{Name}({type} {name}) {
+               {java-set};
+       }
+  }}
+  getat {{
+       public {type} get{Name}AtIndex(long i$) {
+               return {java-getat};
+       }
+  }}
+  setat {{
+       public void set{Name}AtIndex(long i$, {type} {name}) {
+               {java-setat};
+       }
+  }}
+
+  # Initialise the sType field if it has one, also include sub-
+  init eval    {{
+       #if ($tempname =~ m/write/) {
+               my $list = [ 'self$.segment', $s->{bitSize} / 8, "Vulkan.$m->{values}" ];
+
+               foreach my $x (@{$s->{items}}) {
+                       if ($x->{deref} eq 'struct') {
+                               my $y = $api->{types}->{$x->{baseType}};
+                               if ($#{$y->{items}} >= 0 && $y->{items}->[0]->{values}) {
+                                       my $z = $y->{items}->[0];
+                                       push @$list, ($x->{bitOffset} + $z->{bitOffset}) / 8, "Vulkan.$z->{values}";
+                               }
+                       }
+               }
+
+               'Vulkan.initType('.join(", ", @$list).');';
+       #} else {
+       #       ''
+       #}
+  }}
+
+  # for complex constructors
+  #setall-arg  {{ {type} {name} }}
+  setall-arg   {{ {type} {name} }}
+  setall               {{ self$.set{Name}({name}); }}
+  setallat             {{ self$.set{Name}AtIndex(index$, {name}); }}
+}
+
+# ###################################################################### #
+
+code value-array {
+  getorset {{
+       /* value-array.getorset {deref} */
+       public {type} get{Name}() {
+               try {
+                       return {java-get};
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+       }
+       public {typei} get{Name}Element(int i$) {
+               return {java-geti};
+       }
+       public void set{Name}Element(int i$, {typei} {name}) {
+               {java-seti};
+       }
+  }}
+
+  getat {{
+       /* value-array.getat {deref} */
+       public {type} get{Name}AtIndex(long i$) {
+               try {
+                       return {java-getat};
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+       }
+  }}
+
+  setall-arg eval {{
+       if ($m->{len1} <= 4) {
+               join ", ", map { '{typei} {name}$'.$_ } (0 .. $m->{len1}-1);
+       } else {
+               "{typei}[] {name} /* $m->{deref} $m->{len1} */";
+       }
+  }}
+  setall eval {{
+       if ($m->{len1} <= 4) {
+               join "\n\t", map { "self\$.set{Name}Element($_, {name}\$$_);" } (0 .. $m->{len1}-1);
+    } else {
+               <<END;
+               if ({name} != null) {
+                       try {
+                               ((MemorySegment){name}\$SH.invokeExact(self\$.segment)).copyFrom(MemorySegment.ofArray({name}));
+                       } catch (Throwable t) {
+                               System.err.println("Copy error: " + t.getLocalizedMessage());
+                       }
+               }
+END
+    }
+  }}
+}
+
+# ###################################################################### #
+# don't like having to pass scope here, not sure what else to do though
+code handle-array {
+  getorset {{
+       /* value-array {deref} */
+       public {type} get{Name}(VkInstance instance$, ResourceScope scope$) {
+               try {
+                       return {java-get};
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+       }
+       public {typei} get{Name}Element(int i$, VkInstance instance$, ResourceScope scope$) {
+               return {java-geti};
+       }
+       public void set{Name}Element(int i$, {typei} {name}) {
+               {java-seti};
+       }
+  }}
+}
+
+# ###################################################################### #
+
+code value-array2d {
+  getorset {{
+       /* value-array2d {deref} */
+       public {type} get{Name}() {
+               try {
+                       return {java-get};
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+       }
+       public {typei} get{Name}Element(int i$, int j$) {
+               return {java-geti};
+       }
+       public void set{Name}Element(int i$, int j$, {typei} {name}) {
+               {java-seti};
+       }
+  }}
+}
+
+# ###################################################################### #
+
+code inline {
+  getorset {{
+       /* inline {deref} */
+       public {type} get{Name}() {
+               try {
+                       return {java-get};
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+       }
+  }}
+  getorsetat {{
+       /* inline {deref} */
+       public {type} get{Name}AtIndex(long i$) {
+               try {
+                       return {java-getat};
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+       }
+  }}
+}
+
+# ###################################################################### #
+# inline type that we also want to include expanded in initialiser
+# We also create expanded setters and a getter for each simple embedded field.
+
+code inline-expand {
+
+  # These are basic object based accessors
+
+  getorset {{
+       /* inline {deref} */
+       public {type} get{Name}() {
+               try {
+                       return {java-get};
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+       }
+  }}
+
+  getorsetat {{
+       /* inline {deref} */
+       public {type} get{Name}AtIndex(long i$) {
+               try {
+                       return {java-getat};
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+       }
+  }}
+
+  # subroutine to the all-args info, only eval if len1 == 0
+  # this will also include it's own inline struct's fields
+  analyse-args eval {{
+       use integer;
+       my $list = [];
+
+       foreach my $a (@{$api->{data}->{$m->{baseType}}->{items}}) {
+               my $name = "$m->{name}\$$a->{name}";
+               my $offset = ($m->{bitOffset} + $a->{bitOffset}) / 8;
+               my $atype = $primitiveMap{$a->{baseType}};
+
+               if ($atype) {
+                       push @$list, {
+                               name=> $name,
+                               type => $atype,
+                               offset => $offset,
+                       };
+               } else {
+                       my $as = $api->{data}->{$a->{baseType}};
+                       if ($as->{category} eq 'struct') {
+                               foreach my $b (@{$as->{items}}) {
+                                       my $btype = $primitiveMap{$b->{baseType}};
+                                       if ($btype) {
+                                               push @$list, {
+                                                       name => "$name\$$b->{name}",
+                                                       type => $btype,
+                                                       offset =>  $offset + $b->{bitOffset} / 8
+                                               };
+                                       }
+                               }
+                       } elsif ($as->{category} eq 'enum') {
+                               $atype = $primitiveMap{$as->{fullType}};
+                               if ($atype) {
+                                       push @$list, {
+                                               name=> $name,
+                                               type => $atype,
+                                               offset => $offset,
+                                       };
+                               }
+                       }
+               }
+       }
+       $list;
+  }}
+
+  # setBlah(all-simple-blah-fields)
+  set eval {{
+       return 0 if $m->{len1};
+    my $setall_arg = eval($type->{accessor}->{'setall-arg'});
+       my $setall = eval($type->{accessor}->{setall});
+       <<END;
+       public void set{Name}($setall_arg) {
+               var self\$ = this;
+               $setall
+       }
+END
+  }}
+
+  # getBlah$field0(), getBlah$field1() etc.
+  get eval {{
+       die if ($m->{len1});
+
+       return 0 if $m->{len1};
+
+       my $list = eval($type->{accessor}->{'analyse-args'});   die "$@" if !defined $list;
+
+       join("\n", map {
+               my $tname = uc($_->{type});
+               my $fname = ucfirst($_->{name});
+               <<END;
+       public $_->{type} get$fname() {
+               return this.segment.get(Memory.$tname, $_->{offset});
+       }
+END
+       } @$list);
+  }}
+
+  # setBlahAtIndex(index, all-simple-blah-fields)
+  setat eval {{
+       return 0 if $m->{len1};
+    my $setall_arg = eval($type->{accessor}->{'setall-arg'});
+       my $setallat = eval($type->{accessor}->{setallat});
+       <<END;
+       public void set{Name}AtIndex(long index\$, $setall_arg) {
+               var self\$ = this;
+               $setallat
+       }
+END
+  }}
+
+  # create arg-list for setall template
+  setall-arg eval {{
+       return 0 if $m->{len1};
+
+       my $list = eval($type->{accessor}->{'analyse-args'});   die "$@" if !defined $list;
+
+       join(', ', map { "$_->{type} $_->{name}" } @$list);
+  }}
+
+  setall eval  {{
+       return 0 if $m->{len1};
+
+       my $list = eval($type->{accessor}->{'analyse-args'});   die "$@" if !defined $list;
+
+       join("\n\t", map { 'self$.segment.set(Memory.'.uc($_->{type}).", $_->{offset}, $_->{name});" } @$list);
+  }}
+
+  setallat eval        {{
+       use integer;
+
+       return 0 if $m->{len1};
+
+       my $list = eval($type->{accessor}->{'analyse-args'});   die "$@" if !defined $list;
+       my $stride = $s->{bitSize} / 8;
+
+       join("\n\t", map { 'self$.segment.set(Memory.'.uc($_->{type}).", index\$ * $stride + $_->{offset}, $_->{name});" } @$list);
+  }}
+}
+
+# ###################################################################### #
+
+# value with a SegmentAllocator passed to set()
+code value-alloc value {
+  set {{
+       /* {deref} */
+       public void set{Name}({type} {name}, SegmentAllocator alloc$) {
+               {java-set};
+       }
+  }}
+  setat {{
+       /* {deref} */
+       public void set{Name}AtIndex(long i$, {type} {name}, SegmentAllocator alloc$) {
+               {java-setat};
+       }
+  }}
+
+  setall               {{ self$.set{Name}({name}, alloc$); }}
+  setallat             {{ self$.set{Name}AtIndex(index$, {name}, alloc$); }}
+}
+
+# ###################################################################### #
+
+code Vulkan {
+  class {{
+       package {package};
+
+       import au.notzed.nativez.Memory;
+       import jdk.incubator.foreign.MemorySegment;
+       import java.util.Arrays;
+
+       public class Vulkan {
+
+               public static int VK_MAKE_API_VERSION(int variant, int major, int minor, int patch) {
+                       return (variant << 29) | (major << 22) | (minor << 12) | patch;
+               }
+
+               /** Init stype fields of an object or array */
+               static void initType(MemorySegment segment, long size, int stype) {
+                       for (long o = 0; o < segment.byteSize(); o += size)
+                               segment.set(Memory.INT, o, stype);
+               }
+
+               /** Init stype fields of an object or an array with an embedded typed struct */
+               static void initType(MemorySegment segment, long size, int stype, int off1, int stype1) {
+                       for (long o = 0; o < segment.byteSize(); o += size) {
+                               segment.set(Memory.INT, o, stype);
+                               segment.set(Memory.INT, o+off1, stype1);
+                       }
+               }
+
+               static record ErrorInfo(int id, String name) {}
+
+               public static String getErrorMessage(int errorid) {
+                       ErrorInfo q = new ErrorInfo(errorid, null);
+                       int i = Arrays.binarySearch(errors, q, (a, b) -> a.id - b.id);
+
+                       return i >= 0 ? errors[i].name : "Unknown Error";
+               }
+
+               {defines}
+
+               {constants}
+       }
+  }}
+  VK_API_VERSION_1_0 {{ public final static int VK_API_VERSION_1_0 = VK_MAKE_API_VERSION(0, 1, 0, 0); }}
+  VK_API_VERSION_1_1 {{ public final static int VK_API_VERSION_1_1 = VK_MAKE_API_VERSION(0, 1, 1, 0); }}
+  VK_API_VERSION_1_2 {{ public final static int VK_API_VERSION_1_2 = VK_MAKE_API_VERSION(0, 1, 2, 0); }}
+  VK_API_VERSION_1_3 {{ public final static int VK_API_VERSION_1_3 = VK_MAKE_API_VERSION(0, 1, 3, 0); }}
+}
+
+code dispatch {
+  class {{
+       // template: dispatch:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+       class {Name} {
+
+               {field-init}
+
+               {Name}(VkInstance instance$, ResourceScope scope$) {
+                       {init}
+               }
+       }
+  }}
+  field-init {{        final NativeSymbol {name}$NS; }}
+  init {{ {name}$NS = instance$.vkGetInstanceProcAddr("{name}", scope$); }}
+
+}
+
+# non-dispatchable handle
+code handle {
+  class {{
+       // template: handle:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {name} implements Pointer {
+               final NativeSymbol self;
+
+               private {name}(MemoryAddress address, ResourceScope scope) {
+                       this.self = NativeSymbol.ofAddress("{name}", address, scope);
+                       {init}
+               }
+
+               public static {name} create(MemoryAddress address, ResourceScope scope) {
+                       return address != MemoryAddress.NULL ? new {name}(address, scope) : null;
+               }
+
+               /** Allocate an array where the handle scope is independent of the array allocation */
+               public static HandleArray<{name}> createArray(long length, SegmentAllocator alloc, ResourceScope scope$) {
+                       return HandleArray.createArray(length, alloc, (a, s) -> create(a, scope$));
+               }
+
+               /** Allocate an array where the handle scope matches the array allocation */
+               public static HandleArray<{name}> createArray(long length, SegmentAllocator alloc) {
+                       return HandleArray.createArray(length, alloc, {name}::create);
+               }
+
+               public MemoryAddress address() {
+                       return self.address();
+               }
+
+               public ResourceScope scope() {
+                       return self.scope();
+               }
+
+               public String toString() {
+                       return String.format("[%s %016x]", getClass().getSimpleName(), self.address().toRawLongValue());
+               }
+       }
+  }}
+}
+
+# VkInstance
+code handle-instance {
+  class {{
+       // template: handle-instance:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {name} implements Pointer {
+               final NativeSymbol self;
+               final DispatchInstance dispatch;
+
+               private {name}(MemoryAddress address, ResourceScope scope) {
+                       this.self = NativeSymbol.ofAddress("{name}", address, scope);
+                       this.dispatch = new DispatchInstance(this, scope);
+                       {init}
+               }
+
+               public static {name} create(MemoryAddress address, ResourceScope scope) {
+                       return new {name}(address, scope);
+               }
+
+               public MemoryAddress address() {
+                       return self.address();
+               }
+
+               public ResourceScope scope() {
+                       return self.scope();
+               }
+
+               {commands}
+       }
+  }}
+}
+
+# dispatchable handle
+code handle-dispatch {
+  class {{
+       // template: handle-dispatch:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {name} implements Pointer {
+               final NativeSymbol self;
+               final DispatchInstance dispatch;
+
+               private {name}(MemoryAddress address, DispatchInstance dispatch, ResourceScope scope) {
+                       this.self = NativeSymbol.ofAddress("{name}", address, scope);
+                       this.dispatch = dispatch;
+                       {init}
+               }
+
+               public static {name} create(MemoryAddress address, DispatchInstance dispatch, ResourceScope scope) {
+                       return new {name}(address, dispatch, scope);
+               }
+
+               // TODO: evaluate how scope fits here
+               public static HandleArray<{name}> createArray(long length, SegmentAllocator alloc, DispatchInstance dispatch, ResourceScope scope) {
+                       return HandleArray.createArray(length, alloc, (a, s) -> create(a, dispatch, s), scope);
+               }
+
+               public static HandleArray<{name}> createArray(long length, SegmentAllocator alloc, VkInstance instance, ResourceScope scope) {
+                       return HandleArray.createArray(length, alloc, (a, s) -> create(a, instance.dispatch, s), scope);
+               }
+
+               public MemoryAddress address() {
+                       return self.address();
+               }
+
+               public ResourceScope scope() {
+                       return self.scope();
+               }
+
+               {commands}
+
+               {append}
+       }
+  }}
+}
+
+# ###################################################################### #
+
+# shared struct components
+code struct {
+  header {{
+               public MemorySegment segment;
+
+               public static final GroupLayout LAYOUT = {layout};
+
+               private {Name}(MemorySegment segment) {
+                       this.segment = segment;
+               }
+
+               public static {Name} create(MemorySegment segment) {
+                       return new {Name}(segment);
+               }
+
+               public static {Name} create(MemoryAddress address, ResourceScope scope) {
+                       return create(MemorySegment.ofAddress(address, LAYOUT.byteSize(), scope));
+               }
+
+               public static {Name} create(SegmentAllocator alloc) {
+                       var self$ = create(alloc.allocate(LAYOUT));
+                       init(self$);
+                       return self$;
+               }
+
+               private static void init({Name} self$) {
+                       {init}
+               }
+
+               public void reset() {
+                       segment.fill((byte)0);
+                       init(this);
+               }
+
+               // Pointer
+               @Override
+               public MemoryAddress address() {
+                       return segment.address();
+               }
+
+               // Pointer
+               @Override
+               public ResourceScope scope() {
+                       return segment.scope();
+               }
+
+               @Override
+               public String toString() {
+                       return Memory.toString(segment, LAYOUT);
+               }
+  }}
+  create-all {{
+               public static {Name} {create}({java-setall-arguments}, SegmentAllocator alloc$) {
+                       {Name} self$ = create(alloc$);
+
+                       {java-setall}
+
+                       return self$;
+               }
+  }}
+  set-all {{
+               public void {set}({java-setall-arguments}, SegmentAllocator alloc$) {
+                       {Name} self$ = this;
+
+                       {java-setall}
+               }
+  }}
+  setat-all {{
+               public void {set}AtIndex(long index$, {java-setall-arguments}, SegmentAllocator alloc$) {
+                       {Name} self$ = this;
+
+                       {java-setallat}
+               }
+  }}
+  array {{
+               static {Name} createArray(MemoryAddress addr, long length, ResourceScope scope) {
+                       return create(MemorySegment.ofAddress(addr, length * LAYOUT.byteSize(), scope));
+               }
+
+               public static {Name} createArray(long length, SegmentAllocator alloc) {
+                       var self$ = create(alloc.allocateArray(LAYOUT, length));
+                       init(self$);
+                       return self$;
+               }
+
+               public final static MethodHandle LAYOUT$SH = MemoryLayout.sequenceLayout(LAYOUT).sliceHandle(MemoryLayout.PathElement.sequenceElement());
+
+               // Array
+               @Override
+               public long length() {
+                       return segment.byteSize() / LAYOUT.byteSize();
+               }
+
+               @Override
+               public boolean isEmpty() {
+                       return segment.byteSize() == 0;
+               }
+
+               @Override
+               public {Name} getAtIndex(long index$) {
+                       try {
+                               return create((MemorySegment)LAYOUT$SH.invokeExact(this.segment, index$));
+                       } catch (Throwable t) {
+                               throw new RuntimeException(t);
+                       }
+               }
+  }}
+}
+
+# default - writeonly struct
+code struct-writeonly insert=struct:header,struct:create-all
+       fields=init,set,getorset,handle,java-setall {
+  class {{
+       // template: struct-writeonly:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {Name} implements Pointer {
+               {header}
+
+               {create-all}
+
+               {set}
+               {getorset}
+
+               {handle}
+       }
+  }}
+}
+
+code struct-writeonly-array insert=struct:header,struct:create-all,struct:array
+       fields=init,set,getorset,setat,getorsetat,handle,handleat,java-setall {
+  class {{
+       // template: struct-writeonly-array:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {Name} implements Pointer, Array<{Name}> {
+               {header}
+               {array}
+
+               {create-all}
+
+               {set}
+               {getorset}
+
+               {setat}
+               {getorsetat}
+
+               {handle}
+               {handleat}
+
+               {append}
+       }
+  }}
+}
+
+code struct-readonly insert=struct:header
+       fields=init,get,getorset,handle {
+  class {{
+       // template: struct-readonly:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {Name} implements Pointer {
+               {header}
+
+               {get}
+               {getorset}
+
+               {handle}
+
+               {append}
+       }
+  }}
+}
+
+code struct-readonly-array insert=struct:header,struct:array
+       fields=init,get,getorset,getat,getorsetat,handle,handleat {
+  class {{
+       // template: struct-readonly-array:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {Name} implements Pointer, Array<{Name}> {
+               {header}
+               {array}
+
+               {get}
+               {getorset}
+
+               {getat}
+               {getorsetat}
+
+               {handle}
+               {handleat}
+
+               {append}
+       }
+  }}
+}
+
+code struct-readwrite insert=struct:header,struct:create-all
+       fields=init,get,set,getorset,handle,java-setall {
+  class {{
+       // template: struct-readwrite:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {Name} implements Pointer {
+               {header}
+
+               {create-all}
+
+               {get}
+               {set}
+               {getorset}
+
+               {handle}
+
+               {append}
+       }
+  }}
+}
+
+code struct-readwrite-array insert=struct:header,struct:create-all,struct:array
+       fields=init,get,getat,set,setat,getorset,getorsetat,handle,handleat,java-setall {
+  class {{
+       // template: struct-readwrite-array:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {Name} implements Pointer,Array<{Name}> {
+               {header}
+               {array}
+
+               {create-all}
+
+               {get}
+               {set}
+               {getorset}
+
+               {getat}
+               {setat}
+               {getorsetat}
+
+               {handle}
+               {handleat}
+
+               {append}
+       }
+  }}
+}
+
+# read-write with a 'set' method for all members
+code struct-readwrite-all insert=struct:header,struct:create-all,struct:set-all
+       fields=init,get,set,getorset,handle,java-setall {
+  class {{
+       // template: struct-readwrite-all:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {Name} implements Pointer {
+               {header}
+
+               {create-all}
+
+               {get}
+               {set}
+               {getorset}
+
+               {handle}
+
+               {append}
+       }
+  }}
+}
+
+code struct-readwrite-array-all insert=struct:header,struct:create-all,struct:array,struct:set-all,struct:setat-all
+       fields=init,get,getat,set,setat,setallat,getorset,getorsetat,handle,handleat,java-setall,java-setallat {
+  class {{
+       // template: struct-readwrite-array-all:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {Name} implements Pointer,Array<{Name}> {
+               {header}
+               {array}
+
+               {create-all}
+
+               {get}
+               {set}
+               {getorset}
+
+               {getat}
+               {setat}
+               {getorsetat}
+
+               {handle}
+               {handleat}
+       }
+  }}
+}
+
+code struct-writeonly-array-all insert=struct:header,struct:create-all,struct:array,struct:set-all,struct:setat-all
+       fields=init,set,setat,setallat,getorset,getorsetat,handle,handleat,java-setall,java-setallat {
+  class {{
+       // template: struct-writeonly-array-all:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {Name} implements Pointer, Array<{Name}> {
+               {header}
+               {array}
+
+               {create-all}
+
+               {set}
+               {getorset}
+
+               {setat}
+               {getorsetat}
+
+               {handle}
+               {handleat}
+       }
+  }}
+}
+
+# ###################################################################### #
+
+# Basic value-based accessor
+type value accessor=value {
+       native-value    {{ {name} }}
+
+       native-get              {{ ({type}){name}$VH.get(this.segment) }}
+       handle                  {{ final static VarHandle {name}$VH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("{name}")); }}
+
+       java-get                {{ {native-get} }}
+       java-set                {{ {name}$VH.set(this.segment, {native-value}) }}
+
+       native-getat    {{ ({type}){name}$AH.get(this.segment, i$) }}
+       native-setat    {{ {name}$AH.set(this.segment, i$, {native-value}) }}
+       handleat                {{ final static VarHandle {name}$AH = MemoryLayout.sequenceLayout(LAYOUT).varHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("{name}")); }}
+
+       java-getat              {{ {native-getat} }}
+       java-setat              {{ {native-setat} }}
+}
+
+type value-array accessor=value-array {
+       native-get      {{ (MemorySegment){name}$SH.invokeExact(this.segment) }}
+       java-get        {{ {type}.create({native-get}) }}
+
+       native-geti     {{ {name}$EH.get(this.segment, i$) }}
+
+       java-geti       {{ ({typei}){native-geti} }}
+       java-seti       {{ {name}$EH.set(this.segment, i$, {name}) }}
+       handle          {{
+               final static MethodHandle {name}$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement("{name}"));
+               final static VarHandle {name}$EH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("{name}"), MemoryLayout.PathElement.sequenceElement());
+       }}
+
+       native-getat    {{ (MemorySegment){name}$AH.invokeExact(this.segment, i$) }}
+       handleat                {{
+               final static MethodHandle {name}$AH = MemoryLayout.sequenceLayout(LAYOUT).sliceHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("{name}"));
+       }}
+       java-getat              {{ {type}.create({native-getat}) }}
+}
+
+type value-array2d accessor=value-array2d {
+       native-get      {{ (MemorySegment){name}$SH.invokeExact(this.segment) }}
+       java-get        {{ {type}.create({native-get}) }}
+       java-geti       {{ ({typei}){name}$EH.get(this.segment, i$, j$) }}
+       java-seti       {{ {name}$EH.set(this.segment, i$, j$, {name}) }}
+       handle          {{
+               final static MethodHandle {name}$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement("{name}"));
+               final static VarHandle {name}$EH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("{name}"), MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement());
+       }}
+}
+
+type inline accessor=inline {
+       native-get      {{ (MemorySegment){name}$SH.invokeExact(this.segment) }}
+       java-get        {{ {type}.create({native-get}) }}
+       handle          {{ final static MethodHandle {name}$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement("{name}")); }}
+
+       native-getat {{ (MemorySegment){name}$SA.invokeExact(this.segment, i$) }}
+       java-getat      {{ {type}.create({native-getat}) }}
+       handleat        {{ final static MethodHandle {name}$SA = MemoryLayout.sequenceLayout(LAYOUT).sliceHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("{name}")); }}
+}
+
+type inline-array accessor=inline {
+       native-get      {{ (MemorySegment){name}$SH.invokeExact(this.segment) }}
+       java-get        {{ {type}.create({native-get}) }}
+       handle          {{ final static MethodHandle {name}$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement("{name}")); }}
+
+       native-getat {{ (MemorySegment){name}$SA.invokeExact(this.segment, i$) }}
+       java-getat      {{ {type}.create({native-getat}) }}
+       handleat        {{ final static MethodHandle {name}$SA = MemoryLayout.sequenceLayout(LAYOUT).sliceHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("{name}")); }}
+}
+
+type uint8_t,char value {
+       type    {{ byte }}
+       layout  {{ Memory.BYTE }}
+}
+
+type uint16_t value {
+       type    {{ short }}
+       layout  {{ Memory.SHORT }}
+}
+
+type uint32_t,int,int32_t value {
+       type    {{ int }}
+       layout  {{ Memory.INT }}
+}
+
+type uint64_t,int64_t,size_t value {
+       type    {{ long }}
+       layout  {{ Memory.LONG }}
+}
+
+type float value {
+       type    {{ float }}
+       layout  {{ Memory.FLOAT }}
+}
+
+type double value {
+       type    {{ double }}
+       layout  {{ Memory.DOUBLE }}
+}
+
+# ###################################################################### #
+
+type uint8_t[] value-array {
+       type    {{ ByteArray }}
+       layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.BYTE) }}
+       typei   {{ byte }}
+}
+
+# these are always embedded 0-terminated strings, so don't treat as array
+type char[] inline {
+       type    {{ String }}
+       layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.BYTE) }}
+
+       java-get        {{ ({native-get}).getUtf8String(0L) }}
+       java-getat      {{ ({native-getat}).getUtf8String(0L) }}
+}
+
+type uint32_t[],int32_t[] value-array {
+       type    {{ IntArray }}
+       layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.INT) }}
+       typei   {{ int }}
+}
+
+type uint64_t[] value-array {
+       type    {{ LongArray }}
+       layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.LONG) }}
+       typei   {{ long }}
+}
+
+type float[] value-array {
+       type    {{ FloatArray }}
+       layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.FLOAT) }}
+       typei   {{ float }}
+}
+
+type struct[] inline-array {
+       type    {{ {baseType} }}
+       layout  {{ MemoryLayout.sequenceLayout({len1}, {baseType}.LAYOUT) }}
+}
+
+type struct-expand[] struct[] accessor=inline-expand;
+
+type float[][] value-array2d {
+       type    {{ FloatArray }}
+       typei   {{ float }}
+       layout  {{ MemoryLayout.sequenceLayout({len1}, MemoryLayout.sequenceLayout({len2}, Memory.FLOAT)) }}
+}
+
+# select=len?  or what?
+
+type pointer value {
+       layout  {{ Memory.POINTER }}
+       type    {{ MemoryAddress }}
+
+       native-value    {{ Memory.address({name}) }}
+       native-get              {{ (MemoryAddress){name}$VH.get(this.segment) }}
+
+#      java-get        {{ {native-get} }}
+#      java-set        {{ {name}$VH.set(this.segment, Memory.address({name})) }}
+}
+
+type void* pointer;
+
+type value-pointer pointer {
+       java-get        {{ {type}.create((MemoryAddress){name}$VH.get(this.segment), segment.scope()) }}
+}
+
+type uint8_t* value-pointer {
+       type    ByteArray;
+}
+
+type uint32_t*,int32_t*,int* value-pointer {
+       type    IntArray;
+}
+
+type uint64_t* value-pointer {
+       type    LongArray;
+}
+
+type float* value-pointer {
+       type    FloatArray;
+}
+
+type size_t* value-pointer {
+       type    LongArray;
+}
+
+type pointer-length pointer {
+       java-get        {{ {type}.createArray({native-get}, {length}, this.segment.scope()) }}
+}
+
+type void*-length pointer-length {
+       type            MemorySegment;
+       java-get        {{ MemorySegment.ofAddress({native-get}, {length}, this.segment.scope()) }}
+}
+
+type uint8_t*-length pointer-length {
+       type    ByteArray;
+}
+
+type uint32_t*-length,int32_t*-length pointer-length {
+       type    IntArray;
+}
+
+type uint64_t*-length pointer-length {
+       type    LongArray;
+}
+
+type float*-length pointer-length {
+       type    FloatArray;
+}
+
+# special handling for strings, will fail if it isn't
+type char* pointer accessor=value-alloc need-alloc {
+       type            {{ String }}
+
+       java-get        {{ ({native-get}).getUtf8String(0) }}
+       native-value {{ Memory.copyString({name}, alloc$).address() }}
+
+       # this just verifies it's a string type
+       length eval     {{
+               if ($v->{len} =~ m/null-terminated/) {
+                       1;
+               } else {
+                       die Dumper($v, $s);
+               }
+       }}
+
+}
+
+type char**-length pointer-length accessor=value-alloc {
+       type    {{ String[] }}
+
+       java-set        {{ {name}$VH.set(this.segment, Memory.copyStringArray({name}, alloc$).address()); }}
+       java-get        {{ Memory.copyStringArray((MemoryAddress){name}$VH.get(this.segment), {length}) }}
+}
+
+type funcpointer pointer {
+       type            {{ FunctionPointer<{baseType}> }}
+       typei           {{ {baseType} }}
+       java-get        {{ {baseType}.downcall({native-get}, this.segment.scope()) }}
+}
+
+type void** pointer {
+}
+
+type void**-length pointer-length {
+       type    PointerArray;
+}
+
+type handle pointer {
+       type            {{ {baseType} }}
+       java-get        {{ {type}.create({native-get}, this.segment.scope()) }}
+}
+
+type handle[] value-array accessor=handle-array {
+       type            {{ HandleArray<{typei}> }}
+       typei           {{ {baseType} }}
+       layout          {{ MemoryLayout.sequenceLayout({len1}, Memory.POINTER) }}
+       java-get        {{ HandleArray.create({native-get}, (a, s) -> {typei}.create(a, instance$.dispatch, s), scope$) }}
+
+       java-geti       {{ {typei}.create((MemoryAddress){native-geti}, instance$.dispatch, scope$) }}
+}
+
+type handle* pointer {
+       type            {{ HandleArray<{typei}> }}
+       typei           {{ {baseType} }}
+       java-get        {{ HandleArray.create({native-get}, {typei}::create) }}
+}
+
+type handle*-length pointer-length {
+       type            {{ HandleArray<{baseType}> }}
+       typei           {{ {baseType} }}
+       java-get        {{ HandleArray.createArray({native-get}, {length}, {typei}::create, this.segment.scope()) }}
+       java-set        {{ {name}$VH.set(this.segment, Memory.address({name})); }}
+}
+
+type struct inline {
+       type    {{ {baseType} }}
+       layout  {{ {baseType}.LAYOUT }}
+}
+
+type struct-expand inline accessor=inline-expand {
+       type    {{ {baseType} }}
+       layout  {{ {baseType}.LAYOUT }}
+}
+
+type struct* pointer {
+       type            {{ {baseType} }}
+       java-get        {{ {baseType}.create({native-get}, this.segment.scope()) }}
+}
+
+type struct*-length pointer-length {
+       type    {{ {baseType} }}
+       typei   {{ {baseType} }}
+}
+
+type struct** pointer {
+}
+
+# xlib
+type XID,Window,VisualID uint64_t;
+
+type Display* handle {
+       type    xlib.XDisplay;
+}
+
+# xcb
+type xcb_window_t uint32_t;
+type xcb_visualid_t uint32_t;
+type xcb_connection_t* handle {
+       type    xcb.Connection;
+}
+
+override structs {
+       VkInstance                      template=handle-instance;
+       VkPhysicalDevice        template=handle-dispatch;
+       VkDevice                        template=handle-dispatch append {{
+               public HandleArray<VkPipeline> vkCreateGraphicsPipelines(
+                       VkPipelineCache pipelineCache,
+                       VkGraphicsPipelineCreateInfo pCreateInfos,
+                       SegmentAllocator alloc$,
+                       ResourceScope scope$) {
+                       HandleArray<VkPipeline> pPipelines = VkPipeline.createArray(pCreateInfos.length(), alloc$, scope$);
+                       vkCreateGraphicsPipelines(pipelineCache, (int)pCreateInfos.length(), pCreateInfos, pPipelines);
+                       // Vulkan.VK_PIPELINE_COMPILE_REQUIRED_EXT ??
+                       return pPipelines;
+               }
+
+               public VkPipeline vkCreateGraphicsPipeline(
+                       VkPipelineCache pipelineCache,
+                       VkGraphicsPipelineCreateInfo pCreateInfo,
+                       ResourceScope scope) {
+                       try (Frame alloc$ = Frame.frame()) {
+                               HandleArray<VkPipeline> pPipelines = VkPipeline.createArray(1, alloc$, scope);
+                               vkCreateGraphicsPipelines(pipelineCache, 1, pCreateInfo, pPipelines);
+                               // Vulkan.VK_PIPELINE_COMPILE_REQUIRED_EXT ??
+                               return pPipelines.get(0);
+                       }
+               }
+
+               public HandleArray<VkCommandBuffer> vkAllocateCommandBuffers(
+                               VkCommandBufferAllocateInfo pAllocateInfo,
+                               SegmentAllocator alloc$,
+                               ResourceScope scope$) {
+
+                               var buffers = VkCommandBuffer.createArray(
+                                       (int)VkCommandBufferAllocateInfo.commandBufferCount$VH.get(pAllocateInfo.segment),
+                                       alloc$, dispatch, scope$);
+
+                               vkAllocateCommandBuffers(pAllocateInfo, buffers);
+
+                               return buffers;
+               }
+
+               public HandleArray<VkDescriptorSet> vkAllocateDescriptorSets(
+                               VkDescriptorSetAllocateInfo pAllocateInfo,
+                               SegmentAllocator alloc$,
+                               ResourceScope scope) {
+
+                               var buffers = VkDescriptorSet.createArray(
+                                       (int)VkDescriptorSetAllocateInfo.descriptorSetCount$VH.get(pAllocateInfo.segment),
+                                       alloc$, scope);
+
+                               vkAllocateDescriptorSets(pAllocateInfo, buffers);
+
+                               return buffers;
+               }
+
+       }}
+       VkCommandBuffer         template=handle-dispatch;
+       VkQueue                         template=handle-dispatch;
+
+       VkAllocationCallbacks   ignore;
+
+       # We want 'set(all fields) for these types and to include them expanded if inline
+       VkOffset2D expand template=struct-readwrite-array-all;
+       VkOffset3D expand template=struct-readwrite-array-all;
+       VkExtent2D expand template=struct-readwrite-all;
+       VkExtent3D expand template=struct-readwrite-all;
+       VkRect2D expand template=struct-readwrite-array-all;
+
+       VkStencilOpState template=struct-readwrite-all;
+       VkComponentMapping expand template=struct-readwrite-all;
+       VkImageSubresourceRange expand template=struct-readwrite-all;
+
+       # probably just want writeonly? here
+       VkClearColorValue expand template=struct-readwrite-all;
+       VkClearDepthStencilValue expand template=struct-readwrite-all;
+
+       # Yikes, is all this really worth it?
+       VkClearValue template=struct-writeonly-array append {{
+               public static VkClearValue createColour(float r, float g, float b, float a, SegmentAllocator alloc) {
+                       var self = create(alloc);
+
+                       VkClearColorValue.float32$EH.set(self.segment, 0, r);
+                       VkClearColorValue.float32$EH.set(self.segment, 1, g);
+                       VkClearColorValue.float32$EH.set(self.segment, 2, b);
+                       VkClearColorValue.float32$EH.set(self.segment, 3, a);
+
+                       return self;
+               }
+               public static VkClearValue createColour(int r, int g, int b, int a, SegmentAllocator alloc) {
+                       var self = create(alloc);
+
+                       VkClearColorValue.int32$EH.set(self.segment, 0, r);
+                       VkClearColorValue.int32$EH.set(self.segment, 1, g);
+                       VkClearColorValue.int32$EH.set(self.segment, 2, b);
+                       VkClearColorValue.int32$EH.set(self.segment, 3, a);
+
+                       return self;
+               }
+               public static VkClearValue createDepthStencil(float depth, int stencil, SegmentAllocator alloc) {
+                       var self = create(alloc);
+
+                       VkClearDepthStencilValue.depth$VH.set(self.segment, depth);
+                       VkClearDepthStencilValue.stencil$VH.set(self.segment, stencil);
+
+                       return self;
+               }
+
+               final static VarHandle colour$float32$EH = MemoryLayout.sequenceLayout(LAYOUT).varHandle(
+                               MemoryLayout.PathElement.sequenceElement(),
+                               MemoryLayout.PathElement.groupElement("color"),
+                               MemoryLayout.PathElement.groupElement("float32"),
+                               MemoryLayout.PathElement.sequenceElement());
+
+               final static VarHandle colour$int32$EH = MemoryLayout.sequenceLayout(LAYOUT).varHandle(
+                               MemoryLayout.PathElement.sequenceElement(),
+                               MemoryLayout.PathElement.groupElement("color"),
+                               MemoryLayout.PathElement.groupElement("int32"),
+                               MemoryLayout.PathElement.sequenceElement());
+
+               public void setColourAtIndex(int index, float r, float g, float b, float a) {
+                       colour$float32$EH.set(segment, index, 0, r);
+                       colour$float32$EH.set(segment, index, 1, g);
+                       colour$float32$EH.set(segment, index, 2, b);
+                       colour$float32$EH.set(segment, index, 3, a);
+               }
+               public void setColourAtIndex(int index, int r, int g, int b, int a) {
+                       colour$int32$EH.set(segment, index, 0, r);
+                       colour$int32$EH.set(segment, index, 1, g);
+                       colour$int32$EH.set(segment, index, 2, b);
+                       colour$int32$EH.set(segment, index, 3, a);
+               }
+
+               final static VarHandle depthStencil$depth$EH = MemoryLayout.sequenceLayout(LAYOUT).varHandle(
+                               MemoryLayout.PathElement.sequenceElement(),
+                               MemoryLayout.PathElement.groupElement("depthStencil"),
+                               MemoryLayout.PathElement.groupElement("depth"));
+
+               final static VarHandle depthStencil$stencil$EH = MemoryLayout.sequenceLayout(LAYOUT).varHandle(
+                               MemoryLayout.PathElement.sequenceElement(),
+                               MemoryLayout.PathElement.groupElement("depthStencil"),
+                               MemoryLayout.PathElement.groupElement("stencil"));
+
+               public void setDepthStencilAtIndex(int index, float depth, int stencil) {
+                       depthStencil$depth$EH.set(segment, index, depth);
+                       depthStencil$stencil$EH.set(segment, index, stencil);
+               }
+
+       }}
+
+       VkMemoryType template=struct-readonly-array;
+       VkMemoryHeap template=struct-readonly-array;
+       VkPhysicalDeviceMemoryProperties template=struct-readonly append {{
+
+               final static VarHandle memoryTypes$propertyFlags$EH = LAYOUT.varHandle(
+                               MemoryLayout.PathElement.groupElement("memoryTypes"),
+                               MemoryLayout.PathElement.sequenceElement(),
+                               MemoryLayout.PathElement.groupElement("propertyFlags"));
+
+               final static VarHandle memoryTypes$heapIndex$EH = LAYOUT.varHandle(
+                               MemoryLayout.PathElement.groupElement("memoryTypes"),
+                               MemoryLayout.PathElement.sequenceElement(),
+                               MemoryLayout.PathElement.groupElement("heapIndex"));
+
+               public int getMemoryTypes$propertyFlagsAtIndex(int index) {
+                       return (int)memoryTypes$propertyFlags$EH.get(segment, index);
+               }
+
+               public int getMemoryTypes$heapIndexAtIndex(int index) {
+                       return (int)memoryTypes$heapIndex$EH.get(segment, index);
+               }
+
+               final static VarHandle memoryHeaps$size$EH = LAYOUT.varHandle(
+                               MemoryLayout.PathElement.groupElement("memoryHeaps"),
+                               MemoryLayout.PathElement.sequenceElement(),
+                               MemoryLayout.PathElement.groupElement("size"));
+
+               final static VarHandle memoryHeaps$flags$EH = LAYOUT.varHandle(
+                               MemoryLayout.PathElement.groupElement("memoryHeaps"),
+                               MemoryLayout.PathElement.sequenceElement(),
+                               MemoryLayout.PathElement.groupElement("flags"));
+
+               public int getMemoryHeaps$sizeAtIndex(int index) {
+                       return (int)memoryHeaps$size$EH.get(segment, index);
+               }
+
+               public int getMemoryHeaps$flagsAtIndex(int index) {
+                       return (int)memoryHeaps$flags$EH.get(segment, index);
+               }
+       }}
+
+       # possibly way to auto-detect this - all simple types
+       VkAttachmentDescription template=struct-writeonly-array-all;
+       VkPipelineShaderStageCreateInfo template=struct-writeonly-array-all;
+       VkVertexInputAttributeDescription template=struct-writeonly-array-all;
+       VkVertexInputBindingDescription template=struct-writeonly-array-all;
+
+       # Override default read/write
+       VkDebugUtilsMessengerCallbackDataEXT template=struct-readonly;
+       VkDebugUtilsLabelEXT template=struct-readonly-array;
+       VkDebugUtilsObjectNameInfoEXT template=struct-readonly-array;
+
+       VkPhysicalDeviceFeatures template=struct-readwrite append {{
+               /**
+               * return true if every feature requested in query is present in this.
+               */
+               public boolean hasFeatures(VkPhysicalDeviceFeatures query) {
+                       // s q
+                       // 0 0  1
+                       // 0 1  0
+                       // 1 0  1
+                       // 1 1  1
+                       //      = ((s & q) | ~q) & 1
+
+                       // We know this is all just a list of VkBool32, so peek as an array
+                       int ok = 1;
+                       for (int i=0;ok != 0 && i<segment.byteSize() / 4; i++) {
+                               int s = segment.getAtIndex(Memory.INT, i);
+                               int q = query.segment.getAtIndex(Memory.INT, i);
+
+                               ok &= (1 & ((s & q) | ~q));
+                       }
+                       return ok != 0;
+               }
+       }}
+
+# can't really work out what this is it's a void ** but it stays it's a pointer to uint8_t * in the spec
+       VkCuLaunchInfoNVX       pParams=type:pointer pExtras=type:pointer;
+}
diff --git a/src/notzed.vulkan/gen/vulkan.pm b/src/notzed.vulkan/gen/vulkan.pm
new file mode 100644 (file)
index 0000000..68e3077
--- /dev/null
@@ -0,0 +1,814 @@
+
+# Routines for working with vulkan registry
+
+package vulkan;
+
+use strict;
+
+use Data::Dumper;
+use XML::Parser;
+
+sub new {
+       my $class = shift;
+       my $sys = shift;
+       my $self = {
+               sys => $sys,
+               data => {},
+               extensions => [],
+               handles => {},
+               types => {},
+               commands => {},
+               features => [],
+               funcpointers => {},
+       };
+
+       bless $self, $class;
+
+       loadRegistry($self);
+
+       # build various indices
+       my $data = $self->{data};
+       my $handles = $self->{handles};
+       my $types = $self->{types};
+       my $commands = $self->{commands};
+       my $extensions = $self->{extensions};
+       my $funcpointers = $self->{funcpointers};
+
+       foreach my $t (keys %{$data}) {
+               my $v = $data->{$t};
+               $handles->{$v->{name}} = $v if $v->{category} eq 'handle';
+               $types->{$v->{name}} = $v if $v->{category} =~ m/struct|union|platform/;
+               $commands->{$v->{name}} = $v if $v->{category} eq 'command';
+               $funcpointers->{$v->{name}} = $v if $v->{category} eq 'funcpointer';
+       }
+
+       # mark extension functions?
+       foreach my $e (@{$extensions}) {
+               foreach my $name (map { @{$_->{commands}} } @{$e->{require}}) {
+                       my $r = $data->{$name};
+
+                       die if !defined($r);
+
+                       push @{$r->{extensions}}, $e->{name};
+               }
+       }
+
+       # Link up bitmask base types
+       foreach my $s (grep { $_->{category} eq 'enum' } values %{$data}) {
+               if ($s->{requires}) {
+                       my $t = $data->{$s->{requires}};
+                       die Dumper($s) if !defined $t;
+                       die Dumper($s) if !defined $s->{fullType};
+                       $t->{uses} = $s;
+                       $t->{fullType} = $s->{fullType};
+               } elsif ($s->{bitvalues}) {
+                       my $t = $data->{$s->{bitvalues}};
+                       die Dumper($s) if !defined $t;
+                       die Dumper($s) if !defined $s->{fullType};
+                       $t->{uses} = $s;
+                       $t->{fullType} = $s->{fullType};
+               } elsif (!defined $s->{fullType}) {
+                       $s->{fullType} = 'VkFlags';
+               }
+       }
+
+       $self;
+}
+
+sub buildRequirement {
+       my $vk = shift;
+       my $data = shift;
+       my $req = shift;
+       my $ext = shift;
+       my $outconst = $data->{'API Constants'};
+       my $allconst = $vk->{data}->{'API Constants'};
+
+       # Find included types in this requirement
+       foreach my $c (@{$req->{commands}}, @{$req->{types}}) {
+               my $d = $vk->{data}->{$c};
+
+               if (defined $d) {
+                       # for format change?
+                       if ($d->{category} eq 'enum') {
+                               # Copy all aliases across to data
+                               while ($d->{alias}) {
+                                       $data->{$d->{name}} = $d;
+                                       $d = $vk->{data}->{$d->{alias}};
+                               }
+                               $d = { %$d };
+                               $d->{items} = [ @{$d->{items}} ] if defined($d->{items});
+                               $data->{$d->{name}} = $d;
+                       } else {
+                               $data->{$d->{name}} = $d;
+                       }
+               } else {
+                       $data->{$c} = {
+                               name => $c,
+                               category => 'define',
+                       };
+               }
+       }
+
+       foreach my $c (@{$req->{enums}}) {
+               if ($c->{extends}) {
+                       my $d = $data->{$c->{extends}};
+
+                       if (defined($c->{value})) {
+                       } elsif (defined($c->{bitpos})) {
+                               $c->{value} =  "".(1<<$c->{bitpos});
+                       } elsif (defined($c->{extnumber})) {
+                               $c->{value} = "".(1000000000
+                                                                 + 1000 * ($c->{extnumber} - 1)
+                                                                 + $c->{offset});
+                       } elsif (defined($c->{offset})) {
+                               $c->{value} = $c->{dir}."".(1000000000
+                                                                                       + 1000 * ($ext->{number} - 1)
+                                                                                       + $c->{offset});
+                       } elsif (defined($c->{alias})) {
+                       } else {
+                               print Dumper($c);
+                               die;
+                       }
+
+                       $c->{extension} = $ext->{name} if defined($ext);
+
+                       push @{$d->{items}}, $c;
+               } elsif ($c->{value}) {
+                       if ($c->{value} =~ m/^"/) {
+                               if (!defined $outconst->{index}->{$c->{name}}) {
+                                       my $v = { %$c, type=>'const char *' };
+                                       push @{$outconst->{items}}, $v;
+                                       $outconst->{index}->{$v->{name}} = $v;
+                               }
+                       } else {
+                               if (!defined $outconst->{index}->{$c->{name}}) {
+                                       my $v = { %$c, type=>'uint32_t' };
+                                       push @{$outconst->{items}}, $v;
+                                       $outconst->{index}->{$v->{name}} = $v;
+                               }
+                       }
+               } elsif (!$c->{alias}) {
+                       if (!defined $outconst->{index}->{$c->{name}}) {
+                               my $v = $allconst->{index}->{$c->{name}};
+
+                               die Dumper($c) if !defined $v;
+
+                               push @{$outconst->{items}}, $v;
+                               $outconst->{index}->{$c->{name}} = $v;
+                       }
+               }
+       }
+}
+
+# Ideally this builds a 'view' of the features
+# But it doesn't work properly if something is promoted and uses new names
+
+sub buildFeatures {
+       my $vk = shift;
+       my $vers = shift;
+       my $plat = shift;
+       my $data = {};
+       my $versions = {};
+       my $platform = {};
+
+       map { $versions->{$_} = 1 } @$vers;
+       map { $platform->{$_} = 1 } @$plat;
+
+       #print Dumper($vk->{features});
+
+       $data->{'API Constants'} = {
+               name => 'API Constants',
+               category => 'define',
+               items => [],
+               index => {},
+       };
+
+       # add constants that the api's dont reference (only 1 so far)
+       my $outconst = $data->{'API Constants'};
+       my $allconst = $vk->{data}->{'API Constants'};
+       foreach my $v (map {$allconst->{index}->{$_}} qw(VK_UUID_SIZE)) {
+               push @{$outconst->{items}}, $v;
+               $outconst->{index}->{$v->{name}} = $v;
+       }
+
+       foreach my $feature (grep { $versions->{$_->{name}} } @{$vk->{features}}) {
+               print "Feature $feature->{name}\n" if ($vk->{sys}->{verbose});
+               foreach my $req (@{$feature->{require}}) {
+                       buildRequirement($vk, $data, $req);
+               }
+       }
+
+       foreach my $extension (grep { $_->{supported} eq 'vulkan' && (!defined($_->{platform}) || $platform->{$_->{platform}})
+                                                  } @{$vk->{extensions}}) {
+               foreach my $req (grep { (!defined($_->{feature})) || $versions->{$_->{feature}} }
+                                                @{$extension->{require}}) {
+                       print "Extension $extension->{name} $req->{feature}\n" if ($vk->{sys}->{verbose});
+                       buildRequirement($vk, $data, $req, $extension);
+               }
+       }
+
+       #print "rest\n";
+       #print Dumper($data);
+
+       # TODO: need to remove aliases here?
+       my $handles = {};
+       my $types = {};
+       my $commands = {};
+       my $enums = {};
+       my $funcpointers = {};
+       my $defines = {};
+
+       foreach my $t (keys %{$data}) {
+               my $v = $data->{$t};
+               $handles->{$v->{name}} = $v if $v->{category} eq 'handle';
+               $types->{$v->{name}} = $v if $v->{category} =~ m/struct|union/on;
+               $commands->{$v->{name}} = $v if $v->{category} eq 'command';
+               $enums->{$v->{name}} = $v if $v->{category} eq 'enum';
+               $funcpointers->{$v->{name}} = $v if $v->{category} eq 'funcpointer';
+               $defines->{$v->{name}} = $v if $v->{category} eq 'define';
+       }
+
+       if (0) {
+               open(my $f, '>', 'features.pm');
+               print $f Dumper($data);
+               close $f;
+
+               open(my $f, '>', 'vk.pm');
+               print $f Dumper($vk);
+               close $f;
+       }
+
+       my $api = {
+               data => $data,
+               handles => $handles,
+               types => $types,
+               commands => $commands,
+               funcpointers => $funcpointers,
+               enums => $enums,
+               defines => $defines,
+       };
+
+       # create sizes for every struct of interest
+       foreach my $s (values %$types) {
+               next if $s->{alias};
+
+               if ($s->{category} eq 'struct') {
+                       structSize($vk, $api, $s);
+               } elsif ($s->{category} eq 'union') {
+                       unionSize($vk, $api, $s);
+               } else {
+                       die;
+               }
+       }
+
+       return $api;
+}
+
+my $typeInfo = {
+       'void *' => { bitSize => 64, bitAlign => 64 },
+       'int' => { bitSize => 32, bitAlign => 32 },
+       'char' => { bitSize => 8, bitAlign => 8 },
+       'uint8_t' => { bitSize => 8, bitAlign => 8 },
+       'uint16_t' => { bitSize => 16, bitAlign => 16 },
+       'int32_t' => { bitSize => 32, bitAlign => 32 },
+       'uint32_t' => { bitSize => 32, bitAlign => 32 },
+       'int64_t' => { bitSize => 64, bitAlign => 64 },
+       'uint64_t' => { bitSize => 64, bitAlign => 64 },
+       'size_t' => { bitSize => 64, bitAlign => 64 },
+       'float' => { bitSize => 32, bitAlign => 32 },
+       'double' => { bitSize => 64, bitAlign => 64 },
+       'size_t' => { bitSize => 64, bitAlign => 64 },
+       'Window' => { bitSize => 64, bitAlign => 64 },
+       'Display' => { bitSize => 64, bitAlign => 64 },
+       'xcb_window_t' => { bitSize => 32, bitAlign => 32 },
+       'xcb_connection_t' => { bitSize => 64, bitAlign => 64 },
+#      'VkFlags' =>  { bitSize => 32, bitAlign => 32 },
+#      'VkFlags64' =>  { bitSize => 64, bitAlign => 64 },
+};
+
+sub memberSize {
+       my $vk = shift;
+       my $api = shift;
+       my $m = shift;
+       my $t = $api->{data}->{$m->{baseType}};
+       my $nstar = $m->{fullType} =~ tr/*/*/;
+       my ($nbits) = $m->{fullType} =~ m/:(\d+)$/o;
+       my $array = 1;
+       my $info = $typeInfo->{'void *'};
+
+       # arrays and bitfields
+       if ($m->{fullType} =~ m/\[(.*)\]\[(.*)\]$/) {
+               $array = $1 * $2;
+       } elsif ($m->{fullType} =~ m/\[(\d+)\]$/o) {
+               $array = $1;
+       } elsif ($m->{fullType} =~ m/\[(.+)\]$/o) {
+               $array = $vk->{data}->{'API Constants'}->{index}->{$1}->{value};
+       }
+
+       if (!defined($t)) {
+               if ($nbits) {
+                       die Dumper($m) if $nstar > 0;
+                       $info = { bitSize => $nbits, bitAlign => 1 };
+               } else {
+                       $info = $typeInfo->{$m->{baseType}} if ($nstar == 0);
+               }
+       } else {
+               while ($t->{alias}) {
+                       $t = $api->{data}->{$t->{alias}};
+               }
+
+               die Dumper($m) if !defined $t;
+
+               if ($t->{category} =~ m/enum|bitmask/on) {
+                       if ($nbits) {
+                               die Dumper($m) if $nstar > 0;
+                               $info = { bitSize => $nbits, bitAlign => 1 };
+                       } else {
+                               $t = $vk->{data}->{$t->{fullType}};
+                               $info = $typeInfo->{$t->{type}} if ($nstar == 0);
+                       }
+               } elsif ($t->{category} eq 'struct') {
+                       $info = structSize($vk, $api, $t) if ($nstar == 0);
+               } elsif ($t->{category} eq 'union') {
+                       $info = unionSize($vk, $api, $t) if ($nstar == 0);
+               } elsif ($t->{category} eq 'handle') {
+                       # already set
+               } elsif ($t->{category} eq 'basetype') {
+                       $info = $typeInfo->{$t->{type}} if ($nstar == 0);
+               } elsif ($t->{category} eq 'funcpointer') {
+                       # already set
+               } else {
+                       die Dumper($m, $t);
+               }
+       }
+
+       die Dumper($m, $t) if !defined($info);
+
+       #print Dumper($m, $t, $info);
+       #print "size $m->{name} $m->{fullType} = $info->{bitSize}\n";
+
+
+       return { bitSize => $info->{bitSize} * $array, bitAlign => $info->{bitAlign} };
+}
+
+sub align {
+       my $v = shift;
+       my $a = shift;
+
+       return ($v + $a - 1) & ~($a - 1);
+}
+
+sub structSize {
+       my $vk = shift;
+       my $api = shift;
+       my $s = shift;
+       my $bitSize = 0;
+       my $bitAlign = 8;
+
+       if (!defined($s->{bitSize})) {
+               foreach my $m (@{$s->{items}}) {
+                       use integer;
+                       my $info = memberSize($vk, $api, $m);
+
+                       $bitSize = align($bitSize, $info->{bitAlign});
+
+                       $m->{bitOffset} = $bitSize;
+                       $m->{bitSize} = $info->{bitSize};
+
+                       $bitSize = $bitSize + $info->{bitSize};
+                       $bitAlign = $info->{bitAlign} if $info->{bitAlign} > $bitAlign;
+               }
+
+               $bitSize = align($bitSize, $bitAlign);
+
+               $s->{bitSize} = $bitSize;
+               $s->{bitAlign} = $bitAlign;
+       } else {
+               $bitSize = $s->{bitSize};
+               $bitAlign = $s->{bitAlign};
+       }
+
+       return { bitSize => $bitSize, bitAlign => $bitAlign };
+}
+
+sub unionSize {
+       my $vk = shift;
+       my $api = shift;
+       my $s = shift;
+       my $bitSize = 0;
+       my $bitAlign = 8;
+
+       if (!defined($s->{bitSize})) {
+               foreach my $m (@{$s->{items}}) {
+                       use integer;
+                       my $info = memberSize($vk, $api, $m);
+
+                       $m->{bitOffset} = 0;
+                       $m->{bitSize} = $info->{bitSize};
+
+                       $bitSize = $info->{bitSize} if $info->{bitSize} > $bitSize;
+                       $bitAlign = $info->{bitAlign} if $info->{bitAlign} > $bitAlign;
+               }
+
+               $bitSize = align($bitSize, $bitAlign);
+
+               $s->{bitSize} = $bitSize;
+               $s->{bitAlign} = $bitAlign;
+       } else {
+               $bitSize = $s->{bitSize};
+               $bitAlign = $s->{bitAlign};
+       }
+
+       return { bitSize => $bitSize, bitAlign => $bitAlign };
+}
+
+sub loadRegistry {
+       my $vk = shift;
+
+       my $xml = XML::Parser->new(Style => 'Tree');
+       my $doc = $xml->parsefile('/usr/share/vulkan/registry/vk.xml') || die "unable to parse vulkan registry";
+
+       #print Dumper($doc);
+
+       my $root = $doc->[1];
+       my $roota = shift @{$root};
+
+       my $data = $vk->{data};
+       my $alias = $vk->{alias};
+       my $extensions = $vk->{extensions};
+       my $features = $vk->{features};
+
+       # This destructively consumes the whole tree so must be one pass
+       while ($#{$root} >= 0) {
+               my $xt = shift @{$root};
+               my $xn = shift @{$root};
+
+               next if $xt eq '0';
+
+               my $xa = shift @{$xn};
+
+               if ($xt eq 'types') {
+                       while ($#{$xn} >= 0) {
+                               my $yt = shift @{$xn};
+                               my $yn = shift @{$xn};
+
+                               next if $yt ne 'type';
+
+                               my $ya = $yn->[0];
+
+                               if ($ya->{category} =~ m/struct|union/) {
+                                       if (!defined($ya->{alias})) {
+                                               my $s = $ya;
+
+                                               $s->{items} = [];
+
+                                               shift @{$yn};
+                                               while ($#{$yn} >= 0) {
+                                                       my $mt = shift @{$yn};
+                                                       my $mm = shift @{$yn};
+
+                                                       push @{$s->{items}}, loadMember($mm) if $mt eq 'member';
+                                               }
+
+                                               $data->{$s->{name}} = $s;
+                                       } else {
+                                               $alias->{$ya->{name}} = $ya->{alias};
+                                               $data->{$ya->{name}} = $ya;
+                                       }
+                               } elsif ($ya->{category} =~ m/^(handle|basetype|funcpointer|bitmask)$/n) {
+                                       if (!defined($ya->{alias})) {
+                                               my $info = loadMember($yn);
+                                               my $s = $ya;
+
+                                               $s->{name} = $info->{name};
+                                               $s->{type} = $info->{baseType} if defined $info->{baseType};
+
+                                               $s->{category} = 'enum' if $s->{category} eq 'bitmask';
+                                               analyseFunctionPointer($s) if ($s->{category} eq 'funcpointer');
+
+                                               $data->{$s->{name}} = $s;
+                                       } else {
+                                               $ya->{category} = 'enum' if $ya->{category} eq 'bitmask';
+                                               $alias->{$ya->{name}} = $ya->{alias};
+                                               $data->{$ya->{name}} = $ya;
+                                       }
+                               } elsif ($ya->{category} eq 'enum') {
+                                       $data->{$ya->{name}} = $ya;
+                               } elsif ($ya->{requires} eq 'vk_platform' || $ya->{name} eq 'int') {
+                                       # These are just primitive types, not sure what to do with them, could auto-map them to java i suppose
+                                       $ya->{category} = 'platform';
+                                       $data->{$ya->{name}} = $ya;
+                               } else {
+                                       #noisy print "Unhandled: $ya->{name}\n";
+                               }
+                       }
+               } elsif ($xt eq 'enums') {
+                       if ($xa->{type} =~ m/enum|bitmask/o) {
+                               #print "enum: $xa->{name}\n";
+                               # these are forward referenced from <types> block so re-use, or just overwrite?
+                               my $e = $data->{$xa->{name}};
+
+                               $e = { %{$xa}, category => "enum" } if (!defined($e));
+                               $e->{items} = [];
+
+                               while ($#{$xn} >= 0) {
+                                       my $yt = shift @{$xn};
+                                       my $yn = shift @{$xn};
+
+                                       next if $yt ne 'enum';
+
+                                       my $ya = shift @{$yn};
+
+                                       #next if $ya->{alias};
+
+                                       push @{$e->{items}}, $ya;
+                               }
+
+                               $data->{$e->{name}} = $e;
+                       } elsif ($xa->{name} eq 'API Constants') {
+                               my $d = { category => "define", name => $xa->{name}, items =>[], index=>{} };
+
+                               $data->{$xa->{name}} = $d;
+
+                               while ($#{$xn} >= 0) {
+                                       my $yt = shift @{$xn};
+                                       my $yn = shift @{$xn};
+
+                                       next if $yt ne 'enum';
+
+                                       my $ya = shift @{$yn};
+
+                                       #next if $ya->{alias};
+
+                                       push @{$d->{items}}, $ya;
+                                       $d->{index}->{$ya->{name}} = $ya;
+                               }
+                       }
+               } elsif ($xt eq 'commands') {
+                       while ($#{$xn} >= 0) {
+                               my $yt = shift @{$xn};
+                               my $yn = shift @{$xn};
+
+                               next if $yt ne 'command';
+
+                               my $ya = shift @{$yn};
+
+                               if (!defined($ya->{alias})) {
+                                       my $cmd = $ya;
+
+                                       $cmd->{category} = 'command';
+                                       $cmd->{items} = [];
+                                       $cmd->{proto} = {};
+
+                                       while ($#{$yn} >= 0) {
+                                               my $zt = shift @{$yn};
+                                               my $zn = shift @{$yn};
+
+                                               if ($zt eq 'proto') {
+                                                       $cmd->{proto} = loadMember($zn);
+                                               } elsif ($zt eq 'param') {
+                                                       push @{$cmd->{items}}, loadMember($zn);
+                                               }
+                                       }
+
+                                       my $name = $cmd->{proto}->{name};
+
+                                       # check we parsed it properly
+                                       if ($cmd->{proto}->{fullType} eq "") {
+                                               print Dumper([$ya, $yn]);
+                                               die();
+                                       }
+                                       $cmd->{name} = $name;
+
+                                       $data->{$name} = $cmd;
+                               } else {
+                                       # want forward ref or not?
+                                       $alias->{$ya->{name}} = $ya->{alias};
+                                       $data->{$ya->{name}} = $ya;
+                               }
+                       }
+               } elsif ($xt eq 'feature') {
+                       my $feature = $xa;
+
+                       $feature->{require} = [];
+
+                       while ($#{$xn} >= 0) {
+                               my $yt = shift @{$xn};
+                               my $yn = shift @{$xn};
+
+                               next if $yt ne 'require';
+
+                               push @{$feature->{require}}, loadRequire($data, $alias, $yn);
+                       }
+
+                       push @{$features}, $feature;
+               } elsif ($xt eq 'extensions') {
+                       while ($#{$xn} >= 0) {
+                               my $yt = shift @{$xn};
+                               my $yn = shift @{$xn};
+
+                               next if $yt ne 'extension';
+
+                               my $ext = shift @{$yn};
+
+                               $ext->{require} = [];
+
+                               while ($#{$yn} >= 0) {
+                                       my $zt = shift @{$yn};
+                                       my $zn = shift @{$yn};
+
+                                       next if $zt ne 'require';
+
+                                       push @{$ext->{require}}, loadRequire($data, $alias, $zn);
+                               }
+
+                               push @{$extensions}, $ext;
+                       }
+               } else {
+                       print "vulkan.pm: Ignore node: $xt\n";
+               }
+       }
+}
+
+# find an object including via alias
+sub findData {
+       my $data = shift;
+       my $alias = shift;
+       my $name = shift;
+
+       do {
+               my $s = $data->{$name};
+               return $s if defined $s;
+               #print "alias $name => $alias->{$name}\n";
+               $name = $alias->{$name};
+       } while ($name);
+
+       die "No match for type '$name'";
+}
+
+sub makeParameter {
+       my $name = shift;
+       my $fullType = shift;
+       my $type = $fullType;
+
+       $type =~ s/const|\*|\s//gon;
+
+       $fullType =~ s/\s{2,}/ /go; # collapse all whitespace to ' '
+
+       # canonicalise spaces in c type
+       #$fullType =~ s/(?<!const)\s+//go; # strip all spaces except those following const
+       $fullType =~ s/(?<! )\*/ */go;   # insert a space before * if there isn't one
+       $fullType =~ s/(?<=\*)(\S)/ \1/go;# insert a space after * if there isn't one
+
+       # fix brackets and trailing spaces
+       #$fullType =~ s/\( /(/go;
+       #$fullType =~ s/ \)/)/go;
+       #$fullType =~ s/ \[/[/go;
+       $fullType =~ s/^\s+|\s+$//go;
+
+       return {
+               name => $name,
+               Name => ucfirst($name),
+               fullType => $fullType,
+               baseType => $type,
+               type => $type,
+       };
+}
+
+# Convert function typedef into function info
+sub analyseFunctionPointer {
+       my $s = shift;
+
+       if ($s->{fullType} =~ m/^(.+)\s+\(VKAPI_PTR \*\)\((.*)\)$/o) {
+               my $rt = $1;
+               my @args = split /,/,$2;
+
+               $s->{proto} = makeParameter('result$', $rt);
+               $s->{items} = [];
+
+               if ($#args != 0 || $args[0] ne 'void') {
+                       foreach my $a (@args) {
+                               if (my ($fullType, $name) = $a =~ m/^(.*)\s+(\S+)$/o) {
+                                       push @{$s->{items}}, makeParameter($name, $fullType);
+                               } else {
+                                       die "Unable to parse function pointer argument '$a'\n";
+                               }
+                       }
+               }
+       } else {
+               die "Unable to parse function pointer prototype '$s->{fullType}'\n";
+       }
+       $s->{Name} = $s->{name};
+
+       delete $s->{type};
+       delete $s->{baseType};
+       delete $s->{fullType};
+}
+
+sub loadMember {
+       my $nn = shift;
+       #my $x = (join '',split('\n',Dumper($nn)));     $x =~ s/ +/ /g; print "load: $x\n";
+       my $m = shift @{$nn};
+       my $baseType = "";
+       my $fullType = "";
+       my $name = "";
+
+       while ($#{$nn} >= 0) {
+               my $pt = shift @{$nn};
+               my $pn = shift @{$nn};
+
+               if ($pt eq '0') {
+                       $fullType .= $pn;
+               } elsif ($pt eq 'type') {
+                       die if $pn->[1] != 0;
+                       $baseType = $pn->[2];
+                       $fullType .= $baseType;
+               } elsif ($pt eq 'name') {
+                       die if $pn->[1] != 0;
+                       $name = $pn->[2];
+               } elsif ($pt eq 'enum') {
+                       die if $pn->[1] != 0;
+                       $fullType .= $pn->[2];
+               }
+       }
+
+       $fullType =~ s/^typedef (.*);$/\1/os; # strip out 'typedef' part
+       $fullType =~ s/\s{2,}/ /go; # collapse all whitespace to ' '
+
+       # canonicalise spaces in c type
+       #$fullType =~ s/(?<!const)\s+//go; # strip all spaces except those following const
+       $fullType =~ s/(?<! )\*/ */go;   # insert a space before * if there isn't one
+       $fullType =~ s/(?<=\*)(\S)/ \1/go;# insert a space after * if there isn't one
+
+       # fix brackets and trailing spaces
+       $fullType =~ s/\( /(/go;
+       $fullType =~ s/ \)/)/go;
+       $fullType =~ s/ \[/[/go;
+    $fullType =~ s/^\s+|\s+$//go;
+    $fullType =~ s/ :/:/go;
+
+       $m->{name} = $name;
+       $m->{baseType} = $baseType;
+       $m->{fullType} = $fullType;
+
+    $m;
+}
+
+sub loadRequire {
+       my $data = shift;
+       my $alias = shift;
+       my $nn = shift;
+       my $r = shift @{$nn};
+
+       $r->{enums} = [];
+       $r->{types} = [];
+       $r->{commands} = [];
+
+       while ($#{$nn} >= 0) {
+               my $mt = shift @{$nn};
+               my $mn = shift @{$nn};
+
+               if ($mt eq 'type') {
+                       my $ma = shift @{$mn};
+                       push @{$r->{types}}, $ma->{name};
+               } elsif ($mt eq 'command') {
+                       my $ma = shift @{$mn};
+                       push @{$r->{commands}}, $ma->{name};
+               } elsif ($mt eq 'enum') {
+                       my $ma = shift @{$mn};
+                       push @{$r->{enums}}, $ma;
+               }
+       }
+
+       $r;
+}
+
+sub findElements {
+       my $n = shift;
+       my $name = shift;
+       my @list;
+
+       while ($#{$n} >= 0) {
+               my $tag = shift @{$n};
+               my $con = shift @{$n};
+
+               if ($tag eq $name) {
+                       push @list, [$tag, $con];
+               }
+       }
+       @list;
+}
+
+sub scanElements {
+       my $n = shift;
+
+       while ($#{$n} >= 0) {
+               my $tag = shift @{$n};
+               my $con = shift @{$n};
+
+               print "$#{$n} ";
+               print "tag $tag\n";
+       }
+}
+
+1;
diff --git a/src/notzed.xcb/classes/module-info.java b/src/notzed.xcb/classes/module-info.java
new file mode 100644 (file)
index 0000000..4b1e747
--- /dev/null
@@ -0,0 +1,6 @@
+
+module notzed.xcb {
+       requires transitive notzed.nativez;
+
+       exports xcb;
+}
diff --git a/src/notzed.xcb/classes/xcb/Connection.java b/src/notzed.xcb/classes/xcb/Connection.java
new file mode 100644 (file)
index 0000000..27ff8fc
--- /dev/null
@@ -0,0 +1,28 @@
+
+package xcb;
+
+import jdk.incubator.foreign.*;
+import au.notzed.nativez.Pointer;
+
+/* small hack to get it to compile, unimplemented functions so far */
+
+public class Connection implements Pointer {
+       NativeSymbol symbol;
+
+       public Connection(MemoryAddress addr, ResourceScope scope) {
+               symbol = NativeSymbol.ofAddress("Connection", addr, scope);
+       }
+
+       public static Connection create(MemoryAddress addr, ResourceScope scope) {
+               return new Connection(addr, scope);
+       }
+
+       public MemoryAddress address() {
+               return symbol.address();
+       }
+
+       public ResourceScope scope() {
+               return symbol.scope();
+       }
+
+}
diff --git a/src/notzed.xlib/classes/module-info.java b/src/notzed.xlib/classes/module-info.java
new file mode 100644 (file)
index 0000000..d424a8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+module notzed.xlib {
+       requires notzed.nativez;
+
+       exports xlib;
+}
diff --git a/src/notzed.xlib/gen/gen.make b/src/notzed.xlib/gen/gen.make
new file mode 100644 (file)
index 0000000..922b827
--- /dev/null
@@ -0,0 +1,3 @@
+
+notzed.xlib_API = xlib
+notzed.xlib_APIFLAGS = -t xlib -Isrc/notzed.xlib/gen
diff --git a/src/notzed.xlib/gen/xlib.api b/src/notzed.xlib/gen/xlib.api
new file mode 100644 (file)
index 0000000..56f994b
--- /dev/null
@@ -0,0 +1,79 @@
+# -*- Mode:text; tab-width:4; electric-indent-mode: nil; indent-line-function:insert-tab; -*-
+
+%include types.api;
+%include code.api;
+
+struct <default> default=all access=rw rename=s/^_// field:rename=studly-caps {
+       class rename=XClass;
+       visualid rename=VisualID;
+       xid rename=XID;
+}
+
+union <default> default=all access=rw rename=s/^_// field:rename=studly-caps {
+}
+
+struct _XPrivDisplay {
+       screens array array-size=nscreens access=r;
+}
+
+struct Screen access=rw
+ template=code:class=struct-array
+{
+}
+
+#struct Visual {
+#       Class field:rename=XClass;
+#}
+
+#struct XVisualInfo {
+#       Class rename=XClass;
+#}
+
+library XLib {
+       define:xlib;
+       func:XCreateColormap;
+       func:XCreateWindow;
+       func:XDestroyWindow;
+       func:XFlush;
+       func:XGetVisualInfo;
+       func:XInitThreads;
+       func:XInternAtom;
+       func:XMapWindow;
+       func:XOpenDisplay;
+       func:XCloseDisplay;
+       func:XSelectInput;
+
+       func:XSetWMProtocols;
+
+       func:XPending;
+       func:XNextEvent;
+       func:XWindowEvent;
+       func:XCheckWindowEvent;
+
+       func:XGetWindowAttributes;
+
+#      #define DefaultScreen(dpy)      (((_XPrivDisplay)(dpy))->default_screen)
+#      #define RootWindow(dpy, scr)    (ScreenOfDisplay(dpy,scr)->root)
+#   #define ScreenOfDisplay(dpy, scr)(&((_XPrivDisplay)(dpy))->screens[scr])
+
+       code:<inline> {{
+       public static int DefaultScreen(XDisplay dpy) {
+               XPrivDisplay pdpy = XPrivDisplay.create(dpy.address, dpy.scope);
+               return pdpy.getDefaultScreen();
+       }
+
+       public static long RootWindow(XDisplay dpy, int scr) {
+               XPrivDisplay pdpy = XPrivDisplay.create(dpy.address, dpy.scope);
+               Screen screen = pdpy.getScreens().getAtIndex(scr);
+               return screen.getRoot();
+       }
+
+       }}
+
+}
+
+define xlib xlib.h {
+       /Pixel$|^Input|^CW|Mask$|^Alloc/        include;
+       /KeyPress|KeyRelease|ButtonPress|ButtonRelease|MotionNotify|EnterNotify|LeaveNotify|FocusIn|FocusOut|KeymapNotify|Expose|GraphicsExpose|NoExpose|VisibilityNotify|CreateNotify|DestroyNotify|UnmapNotify|MapNotify|MapRequest|ReparentNotify|ConfigureNotify|ConfigureRequest|GravityNotify|ResizeRequest|CirculateNotify|CirculateRequest|PropertyNotify|SelectionClear|SelectionRequest|SelectionNotify|ColormapNotify|ClientMessage|MappingNotify|GenericEvent/ include;
+
+}
diff --git a/src/notzed.xlib/gen/xlib.h b/src/notzed.xlib/gen/xlib.h
new file mode 100644 (file)
index 0000000..18f0d17
--- /dev/null
@@ -0,0 +1,2 @@
+
+#include <X11/Xutil.h>