Full Code of linebender/piet-metal for AI

master 71afb99a68f8 cached
32 files
214.4 KB
48.9k tokens
206 symbols
1 requests
Download .txt
Showing preview only (225K chars total). Download the full file or copy to clipboard to get everything.
Repository: linebender/piet-metal
Branch: master
Commit: 71afb99a68f8
Files: 32
Total size: 214.4 KB

Directory structure:
gitextract_b08e_b5r/

├── .gitignore
├── Cargo.toml
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── TestApp/
│   ├── AppDelegate.h
│   ├── AppDelegate.m
│   ├── Assets.xcassets/
│   │   ├── AppIcon.appiconset/
│   │   │   └── Contents.json
│   │   └── Contents.json
│   ├── Base.lproj/
│   │   └── Main.storyboard
│   ├── GenTypes.h
│   ├── Info.plist
│   ├── PietRender.metal
│   ├── PietRenderer.h
│   ├── PietRenderer.m
│   ├── PietShaderTypes.h
│   ├── SceneEncoder.h
│   ├── SceneEncoder.m
│   ├── ViewController.h
│   ├── ViewController.m
│   ├── main.m
│   └── piet_metal.entitlements
├── include/
│   └── piet_metal.h
├── piet-gpu-derive/
│   ├── Cargo.toml
│   └── src/
│       └── lib.rs
├── piet-metal.xcodeproj/
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   ├── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       └── IDEWorkspaceChecks.plist
│   └── xcuserdata/
│       └── raph.xcuserdatad/
│           └── xcschemes/
│               └── xcschememanagement.plist
└── src/
    ├── flatten.rs
    ├── lib.rs
    └── main.rs

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
/target
/build
.idea

================================================
FILE: Cargo.toml
================================================
[package]
name = "piet-metal"
version = "0.0.0"
authors = ["Raph Levien <raph.levien@gmail.com>"]
description = "A Metal-based renderer for the piet 2D graphics abstraction."
license = "MIT/Apache-2.0"
edition = "2018"
keywords = ["graphics", "2d"]
categories = ["rendering::graphics-api"]

[dependencies]
kurbo = "0.5.6"
piet-gpu-derive = { path = "./piet-gpu-derive" }

# this is for reading the tiger, will be factored out
roxmltree = "0.6.0"

[lib]
name = "piet_metal"
crate-type = ["staticlib"]


================================================
FILE: LICENSE-APACHE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: LICENSE-MIT
================================================
Copyright (c) 2019 Raph Levien

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.


================================================
FILE: README.md
================================================
# piet-metal

This repository is currently an experiment in using GPU compute to implement the piet 2D graphics API. In its initial stages it is an Objective-C macOS application, to make it easier to use Xcode tools. When it becomes more functional, Rust bindings will be added, with the Objective-C code for the Metal bindings built from the Rust library's build.rs.

## Following along

I'm discussing the design and my findings in the [#druid] stream on xi Zulip. I've also creates a [notes document].

## License

Licensed under either of these:

 * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
   https://www.apache.org/licenses/LICENSE-2.0)
 * MIT license ([LICENSE-MIT](LICENSE-MIT) or
   https://opensource.org/licenses/MIT)

[notes document]: https://docs.google.com/document/d/1LILagXyJgYtlm6y83x1Mc2VoNfOcvW_ZiCldZbs4yO8/edit?usp=sharing
[#druid]: https://xi.zulipchat.com/#narrow/stream/147926-druid


================================================
FILE: TestApp/AppDelegate.h
================================================
//  Copyright 2019 The xi-editor authors.

#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate>


@end



================================================
FILE: TestApp/AppDelegate.m
================================================
//  Copyright 2019 The xi-editor authors.

#import "AppDelegate.h"

@interface AppDelegate ()

@end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application
}


- (void)applicationWillTerminate:(NSNotification *)aNotification {
    // Insert code here to tear down your application
}


@end


================================================
FILE: TestApp/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "mac",
      "size" : "16x16",
      "scale" : "1x"
    },
    {
      "idiom" : "mac",
      "size" : "16x16",
      "scale" : "2x"
    },
    {
      "idiom" : "mac",
      "size" : "32x32",
      "scale" : "1x"
    },
    {
      "idiom" : "mac",
      "size" : "32x32",
      "scale" : "2x"
    },
    {
      "idiom" : "mac",
      "size" : "128x128",
      "scale" : "1x"
    },
    {
      "idiom" : "mac",
      "size" : "128x128",
      "scale" : "2x"
    },
    {
      "idiom" : "mac",
      "size" : "256x256",
      "scale" : "1x"
    },
    {
      "idiom" : "mac",
      "size" : "256x256",
      "scale" : "2x"
    },
    {
      "idiom" : "mac",
      "size" : "512x512",
      "scale" : "1x"
    },
    {
      "idiom" : "mac",
      "size" : "512x512",
      "scale" : "2x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: TestApp/Assets.xcassets/Contents.json
================================================
{
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: TestApp/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--Application-->
        <scene sceneID="JPo-4y-FX3">
            <objects>
                <application id="hnw-xV-0zn" sceneMemberID="viewController">
                    <menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
                        <items>
                            <menuItem title="piet-metal" id="1Xt-HY-uBw">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="piet-metal" systemMenu="apple" id="uQy-DD-JDr">
                                    <items>
                                        <menuItem title="About piet-metal" id="5kV-Vb-QxS">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
                                        <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
                                        <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
                                        <menuItem title="Services" id="NMo-om-nkz">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
                                        <menuItem title="Hide piet-metal" keyEquivalent="h" id="Olw-nP-bQN">
                                            <connections>
                                                <action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                            <connections>
                                                <action selector="hideOtherApplications:" target="Ady-hI-5gd" id="VT4-aY-XCT"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Show All" id="Kd2-mp-pUS">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="unhideAllApplications:" target="Ady-hI-5gd" id="Dhg-Le-xox"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
                                        <menuItem title="Quit piet-metal" keyEquivalent="q" id="4sb-4s-VLi">
                                            <connections>
                                                <action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="File" id="dMs-cI-mzQ">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="File" id="bib-Uj-vzu">
                                    <items>
                                        <menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
                                            <connections>
                                                <action selector="newDocument:" target="Ady-hI-5gd" id="4Si-XN-c54"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
                                            <connections>
                                                <action selector="openDocument:" target="Ady-hI-5gd" id="bVn-NM-KNZ"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Open Recent" id="tXI-mr-wws">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
                                                <items>
                                                    <menuItem title="Clear Menu" id="vNY-rz-j42">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="clearRecentDocuments:" target="Ady-hI-5gd" id="Daa-9d-B3U"/>
                                                        </connections>
                                                    </menuItem>
                                                </items>
                                            </menu>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
                                        <menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
                                            <connections>
                                                <action selector="performClose:" target="Ady-hI-5gd" id="HmO-Ls-i7Q"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
                                            <connections>
                                                <action selector="saveDocument:" target="Ady-hI-5gd" id="teZ-XB-qJY"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
                                            <connections>
                                                <action selector="saveDocumentAs:" target="Ady-hI-5gd" id="mDf-zr-I0C"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H">
                                            <connections>
                                                <action selector="revertDocumentToSaved:" target="Ady-hI-5gd" id="iJ3-Pv-kwq"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
                                        <menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
                                            <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
                                            <connections>
                                                <action selector="runPageLayout:" target="Ady-hI-5gd" id="Din-rz-gC5"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
                                            <connections>
                                                <action selector="print:" target="Ady-hI-5gd" id="qaZ-4w-aoO"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="Edit" id="5QF-Oa-p0T">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Edit" id="W48-6f-4Dl">
                                    <items>
                                        <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
                                            <connections>
                                                <action selector="undo:" target="Ady-hI-5gd" id="M6e-cu-g7V"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
                                            <connections>
                                                <action selector="redo:" target="Ady-hI-5gd" id="oIA-Rs-6OD"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
                                        <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
                                            <connections>
                                                <action selector="cut:" target="Ady-hI-5gd" id="YJe-68-I9s"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
                                            <connections>
                                                <action selector="copy:" target="Ady-hI-5gd" id="G1f-GL-Joy"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
                                            <connections>
                                                <action selector="paste:" target="Ady-hI-5gd" id="UvS-8e-Qdg"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                            <connections>
                                                <action selector="pasteAsPlainText:" target="Ady-hI-5gd" id="cEh-KX-wJQ"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Delete" id="pa3-QI-u2k">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="delete:" target="Ady-hI-5gd" id="0Mk-Ml-PaM"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
                                            <connections>
                                                <action selector="selectAll:" target="Ady-hI-5gd" id="VNm-Mi-diN"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
                                        <menuItem title="Find" id="4EN-yA-p0u">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <menu key="submenu" title="Find" id="1b7-l0-nxx">
                                                <items>
                                                    <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
                                                        <connections>
                                                            <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="cD7-Qs-BN4"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
                                                        <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                                        <connections>
                                                            <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="WD3-Gg-5AJ"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
                                                        <connections>
                                                            <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="NDo-RZ-v9R"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
                                                        <connections>
                                                            <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="HOh-sY-3ay"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
                                                        <connections>
                                                            <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="U76-nv-p5D"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
                                                        <connections>
                                                            <action selector="centerSelectionInVisibleArea:" target="Ady-hI-5gd" id="IOG-6D-g5B"/>
                                                        </connections>
                                                    </menuItem>
                                                </items>
                                            </menu>
                                        </menuItem>
                                        <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
                                                <items>
                                                    <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
                                                        <connections>
                                                            <action selector="showGuessPanel:" target="Ady-hI-5gd" id="vFj-Ks-hy3"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
                                                        <connections>
                                                            <action selector="checkSpelling:" target="Ady-hI-5gd" id="fz7-VC-reM"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
                                                    <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="toggleContinuousSpellChecking:" target="Ady-hI-5gd" id="7w6-Qz-0kB"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="toggleGrammarChecking:" target="Ady-hI-5gd" id="muD-Qn-j4w"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="toggleAutomaticSpellingCorrection:" target="Ady-hI-5gd" id="2lM-Qi-WAP"/>
                                                        </connections>
                                                    </menuItem>
                                                </items>
                                            </menu>
                                        </menuItem>
                                        <menuItem title="Substitutions" id="9ic-FL-obx">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
                                                <items>
                                                    <menuItem title="Show Substitutions" id="z6F-FW-3nz">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="orderFrontSubstitutionsPanel:" target="Ady-hI-5gd" id="oku-mr-iSq"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
                                                    <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="toggleSmartInsertDelete:" target="Ady-hI-5gd" id="3IJ-Se-DZD"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Smart Quotes" id="hQb-2v-fYv">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="toggleAutomaticQuoteSubstitution:" target="Ady-hI-5gd" id="ptq-xd-QOA"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Smart Dashes" id="rgM-f4-ycn">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="toggleAutomaticDashSubstitution:" target="Ady-hI-5gd" id="oCt-pO-9gS"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Smart Links" id="cwL-P1-jid">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="toggleAutomaticLinkDetection:" target="Ady-hI-5gd" id="Gip-E3-Fov"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Data Detectors" id="tRr-pd-1PS">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="toggleAutomaticDataDetection:" target="Ady-hI-5gd" id="R1I-Nq-Kbl"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Text Replacement" id="HFQ-gK-NFA">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="toggleAutomaticTextReplacement:" target="Ady-hI-5gd" id="DvP-Fe-Py6"/>
                                                        </connections>
                                                    </menuItem>
                                                </items>
                                            </menu>
                                        </menuItem>
                                        <menuItem title="Transformations" id="2oI-Rn-ZJC">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
                                                <items>
                                                    <menuItem title="Make Upper Case" id="vmV-6d-7jI">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="uppercaseWord:" target="Ady-hI-5gd" id="sPh-Tk-edu"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Make Lower Case" id="d9M-CD-aMd">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="lowercaseWord:" target="Ady-hI-5gd" id="iUZ-b5-hil"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Capitalize" id="UEZ-Bs-lqG">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="capitalizeWord:" target="Ady-hI-5gd" id="26H-TL-nsh"/>
                                                        </connections>
                                                    </menuItem>
                                                </items>
                                            </menu>
                                        </menuItem>
                                        <menuItem title="Speech" id="xrE-MZ-jX0">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
                                                <items>
                                                    <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="startSpeaking:" target="Ady-hI-5gd" id="654-Ng-kyl"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="stopSpeaking:" target="Ady-hI-5gd" id="dX8-6p-jy9"/>
                                                        </connections>
                                                    </menuItem>
                                                </items>
                                            </menu>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="Format" id="jxT-CU-nIS">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Format" id="GEO-Iw-cKr">
                                    <items>
                                        <menuItem title="Font" id="Gi5-1S-RQB">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
                                                <items>
                                                    <menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
                                                        <connections>
                                                            <action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
                                                        <connections>
                                                            <action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
                                                        <connections>
                                                            <action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
                                                        <connections>
                                                            <action selector="underline:" target="Ady-hI-5gd" id="FYS-2b-JAY"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
                                                    <menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
                                                        <connections>
                                                            <action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
                                                        <connections>
                                                            <action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
                                                    <menuItem title="Kern" id="jBQ-r6-VK2">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <menu key="submenu" title="Kern" id="tlD-Oa-oAM">
                                                            <items>
                                                                <menuItem title="Use Default" id="GUa-eO-cwY">
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="useStandardKerning:" target="Ady-hI-5gd" id="6dk-9l-Ckg"/>
                                                                    </connections>
                                                                </menuItem>
                                                                <menuItem title="Use None" id="cDB-IK-hbR">
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="turnOffKerning:" target="Ady-hI-5gd" id="U8a-gz-Maa"/>
                                                                    </connections>
                                                                </menuItem>
                                                                <menuItem title="Tighten" id="46P-cB-AYj">
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="tightenKerning:" target="Ady-hI-5gd" id="hr7-Nz-8ro"/>
                                                                    </connections>
                                                                </menuItem>
                                                                <menuItem title="Loosen" id="ogc-rX-tC1">
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="loosenKerning:" target="Ady-hI-5gd" id="8i4-f9-FKE"/>
                                                                    </connections>
                                                                </menuItem>
                                                            </items>
                                                        </menu>
                                                    </menuItem>
                                                    <menuItem title="Ligatures" id="o6e-r0-MWq">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
                                                            <items>
                                                                <menuItem title="Use Default" id="agt-UL-0e3">
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="useStandardLigatures:" target="Ady-hI-5gd" id="7uR-wd-Dx6"/>
                                                                    </connections>
                                                                </menuItem>
                                                                <menuItem title="Use None" id="J7y-lM-qPV">
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="turnOffLigatures:" target="Ady-hI-5gd" id="iX2-gA-Ilz"/>
                                                                    </connections>
                                                                </menuItem>
                                                                <menuItem title="Use All" id="xQD-1f-W4t">
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="useAllLigatures:" target="Ady-hI-5gd" id="KcB-kA-TuK"/>
                                                                    </connections>
                                                                </menuItem>
                                                            </items>
                                                        </menu>
                                                    </menuItem>
                                                    <menuItem title="Baseline" id="OaQ-X3-Vso">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <menu key="submenu" title="Baseline" id="ijk-EB-dga">
                                                            <items>
                                                                <menuItem title="Use Default" id="3Om-Ey-2VK">
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="unscript:" target="Ady-hI-5gd" id="0vZ-95-Ywn"/>
                                                                    </connections>
                                                                </menuItem>
                                                                <menuItem title="Superscript" id="Rqc-34-cIF">
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="superscript:" target="Ady-hI-5gd" id="3qV-fo-wpU"/>
                                                                    </connections>
                                                                </menuItem>
                                                                <menuItem title="Subscript" id="I0S-gh-46l">
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="subscript:" target="Ady-hI-5gd" id="Q6W-4W-IGz"/>
                                                                    </connections>
                                                                </menuItem>
                                                                <menuItem title="Raise" id="2h7-ER-AoG">
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="raiseBaseline:" target="Ady-hI-5gd" id="4sk-31-7Q9"/>
                                                                    </connections>
                                                                </menuItem>
                                                                <menuItem title="Lower" id="1tx-W0-xDw">
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="lowerBaseline:" target="Ady-hI-5gd" id="OF1-bc-KW4"/>
                                                                    </connections>
                                                                </menuItem>
                                                            </items>
                                                        </menu>
                                                    </menuItem>
                                                    <menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
                                                    <menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
                                                        <connections>
                                                            <action selector="orderFrontColorPanel:" target="Ady-hI-5gd" id="mSX-Xz-DV3"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
                                                    <menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
                                                        <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                                        <connections>
                                                            <action selector="copyFont:" target="Ady-hI-5gd" id="GJO-xA-L4q"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
                                                        <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                                        <connections>
                                                            <action selector="pasteFont:" target="Ady-hI-5gd" id="JfD-CL-leO"/>
                                                        </connections>
                                                    </menuItem>
                                                </items>
                                            </menu>
                                        </menuItem>
                                        <menuItem title="Text" id="Fal-I4-PZk">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <menu key="submenu" title="Text" id="d9c-me-L2H">
                                                <items>
                                                    <menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
                                                        <connections>
                                                            <action selector="alignLeft:" target="Ady-hI-5gd" id="zUv-R1-uAa"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
                                                        <connections>
                                                            <action selector="alignCenter:" target="Ady-hI-5gd" id="spX-mk-kcS"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Justify" id="J5U-5w-g23">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="alignJustified:" target="Ady-hI-5gd" id="ljL-7U-jND"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
                                                        <connections>
                                                            <action selector="alignRight:" target="Ady-hI-5gd" id="r48-bG-YeY"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
                                                    <menuItem title="Writing Direction" id="H1b-Si-o9J">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
                                                            <items>
                                                                <menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                </menuItem>
                                                                <menuItem id="YGs-j5-SAR">
                                                                    <string key="title">	Default</string>
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="makeBaseWritingDirectionNatural:" target="Ady-hI-5gd" id="qtV-5e-UBP"/>
                                                                    </connections>
                                                                </menuItem>
                                                                <menuItem id="Lbh-J2-qVU">
                                                                    <string key="title">	Left to Right</string>
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="makeBaseWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="S0X-9S-QSf"/>
                                                                    </connections>
                                                                </menuItem>
                                                                <menuItem id="jFq-tB-4Kx">
                                                                    <string key="title">	Right to Left</string>
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="makeBaseWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="5fk-qB-AqJ"/>
                                                                    </connections>
                                                                </menuItem>
                                                                <menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
                                                                <menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                </menuItem>
                                                                <menuItem id="Nop-cj-93Q">
                                                                    <string key="title">	Default</string>
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="makeTextWritingDirectionNatural:" target="Ady-hI-5gd" id="lPI-Se-ZHp"/>
                                                                    </connections>
                                                                </menuItem>
                                                                <menuItem id="BgM-ve-c93">
                                                                    <string key="title">	Left to Right</string>
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="makeTextWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="caW-Bv-w94"/>
                                                                    </connections>
                                                                </menuItem>
                                                                <menuItem id="RB4-Sm-HuC">
                                                                    <string key="title">	Right to Left</string>
                                                                    <modifierMask key="keyEquivalentModifierMask"/>
                                                                    <connections>
                                                                        <action selector="makeTextWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="EXD-6r-ZUu"/>
                                                                    </connections>
                                                                </menuItem>
                                                            </items>
                                                        </menu>
                                                    </menuItem>
                                                    <menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
                                                    <menuItem title="Show Ruler" id="vLm-3I-IUL">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="toggleRuler:" target="Ady-hI-5gd" id="FOx-HJ-KwY"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
                                                        <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
                                                        <connections>
                                                            <action selector="copyRuler:" target="Ady-hI-5gd" id="71i-fW-3W2"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
                                                        <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
                                                        <connections>
                                                            <action selector="pasteRuler:" target="Ady-hI-5gd" id="cSh-wd-qM2"/>
                                                        </connections>
                                                    </menuItem>
                                                </items>
                                            </menu>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="View" id="H8h-7b-M4v">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="View" id="HyV-fh-RgO">
                                    <items>
                                        <menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                            <connections>
                                                <action selector="toggleToolbarShown:" target="Ady-hI-5gd" id="BXY-wc-z0C"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="runToolbarCustomizationPalette:" target="Ady-hI-5gd" id="pQI-g3-MTW"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
                                        <menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
                                            <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
                                            <connections>
                                                <action selector="toggleSidebar:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
                                            <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
                                            <connections>
                                                <action selector="toggleFullScreen:" target="Ady-hI-5gd" id="dU3-MA-1Rq"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="Window" id="aUF-d1-5bR">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
                                    <items>
                                        <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
                                            <connections>
                                                <action selector="performMiniaturize:" target="Ady-hI-5gd" id="VwT-WD-YPe"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Zoom" id="R4o-n2-Eq4">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="performZoom:" target="Ady-hI-5gd" id="DIl-cC-cCs"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
                                        <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="arrangeInFront:" target="Ady-hI-5gd" id="DRN-fu-gQh"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="Help" id="wpr-3q-Mcd">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
                                    <items>
                                        <menuItem title="piet-metal Help" keyEquivalent="?" id="FKE-Sm-Kum">
                                            <connections>
                                                <action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                        </items>
                    </menu>
                    <connections>
                        <outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
                    </connections>
                </application>
                <customObject id="Voe-Tx-rLC" customClass="AppDelegate"/>
                <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
                <customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="75" y="0.0"/>
        </scene>
        <!--Window Controller-->
        <scene sceneID="R2V-B0-nI4">
            <objects>
                <windowController id="B8D-0N-5wS" sceneMemberID="viewController">
                    <window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA">
                        <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
                        <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
                        <rect key="contentRect" x="196" y="240" width="1024" height="768"/>
                        <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
                        <connections>
                            <outlet property="delegate" destination="B8D-0N-5wS" id="98r-iN-zZc"/>
                        </connections>
                    </window>
                    <connections>
                        <segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/>
                    </connections>
                </windowController>
                <customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="75" y="250"/>
        </scene>
        <!--View Controller-->
        <scene sceneID="hIz-AP-VOD">
            <objects>
                <viewController id="XfG-lQ-9wD" customClass="ViewController" sceneMemberID="viewController">
                    <view key="view" id="m2S-Jp-Qdl" customClass="MTKView">
                        <rect key="frame" x="0.0" y="0.0" width="1024" height="768"/>
                        <autoresizingMask key="autoresizingMask"/>
                    </view>
                </viewController>
                <customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="75" y="655"/>
        </scene>
    </scenes>
</document>


================================================
FILE: TestApp/GenTypes.h
================================================
//  Copyright 2019 The xi-editor authors.

// The contents of this file are autogenerated.

// scene from new (merged) generator

inline uint extract_8bit_value(uint bit_shift, uint package) {
    uint mask = 255;
    uint result = (package >> bit_shift) & mask;

    return result;
}

inline uint extract_16bit_value(uint bit_shift, uint package) {
    uint mask = 65535;
    uint result = (package >> bit_shift) & mask;

    return result;
}

typedef uint SimpleGroupRef;
typedef uint PietCircleRef;
typedef uint PietStrokeLineRef;
typedef uint PietFillRef;
typedef uint PietStrokePolyLineRef;
typedef uint PietItemRef;

struct SimpleGroupPacked {
    uint n_items;
    PietItemRef items_ix;
    uint2 bbox;
};

inline SimpleGroupPacked SimpleGroup_read(const device char *buf, SimpleGroupRef ref) {
    SimpleGroupPacked result;

    uint n_items = *(device const uint*)(buf + ref);
    result.n_items = n_items;

    PietItemRef items_ix = *(device const uint*)(buf + ref + 4);
    result.items_ix = items_ix;

    uint2 bbox = *(device const packed_uint2*)(buf + ref + 8);
    result.bbox = bbox;

    return result;
}

inline uint SimpleGroup_n_items(const device char *buf, SimpleGroupRef ref) {
    uint n_items = *(device const uint*)(buf + ref);
    return n_items;
}

inline PietItemRef SimpleGroup_items_ix(const device char *buf, SimpleGroupRef ref) {
    PietItemRef items_ix = *(device const uint*)(buf + ref + 4);
    return items_ix;
}

inline uint2 SimpleGroup_bbox(const device char *buf, SimpleGroupRef ref) {
    uint2 bbox = *(device const packed_uint2*)(buf + ref + 8);
    return bbox;
}

inline uint4 SimpleGroup_unpack_bbox(uint2 bbox) {
    uint4 result;

    result[0] = extract_16bit_value(0, bbox[0]);
    result[1] = extract_16bit_value(16, bbox[0]);
    result[2] = extract_16bit_value(0, bbox[1]);
    result[3] = extract_16bit_value(16, bbox[1]);
    return result;
}

struct SimpleGroup {
    uint n_items;
    PietItemRef items_ix;
    uint4 bbox;
};

inline SimpleGroup SimpleGroup_unpack(SimpleGroupPacked packed_form) {
    SimpleGroup result;

    result.n_items = packed_form.n_items;
    result.items_ix = packed_form.items_ix;
    result.bbox = SimpleGroup_unpack_bbox(packed_form.bbox);

    return result;
}

struct PietCirclePacked {
    uint tag;
};

inline PietCirclePacked PietCircle_read(const device char *buf, PietCircleRef ref) {
    PietCirclePacked result;

    return result;
}

struct PietCircle {
};

inline PietCircle PietCircle_unpack(PietCirclePacked packed_form) {
    PietCircle result;


    return result;
}

struct PietStrokeLinePacked {
    uint tag;
    uint flags;
    uint rgba_color;
    float width;
    float2 start;
    float2 end;
};

inline PietStrokeLinePacked PietStrokeLine_read(const device char *buf, PietStrokeLineRef ref) {
    PietStrokeLinePacked result;

    uint flags = *(device const uint*)(buf + ref + 4);
    result.flags = flags;

    uint rgba_color = *(device const uint*)(buf + ref + 8);
    result.rgba_color = rgba_color;

    float width = as_type<float>(*(device const uint*)(buf + ref + 12));
    result.width = width;

    float2 start = as_type<float2>(*(device const packed_uint2*)(buf + ref + 16));
    result.start = start;

    float2 end = as_type<float2>(*(device const packed_uint2*)(buf + ref + 24));
    result.end = end;

    return result;
}

inline uint PietStrokeLine_flags(const device char *buf, PietStrokeLineRef ref) {
    uint flags = *(device const uint*)(buf + ref + 4);
    return flags;
}

inline uint PietStrokeLine_rgba_color(const device char *buf, PietStrokeLineRef ref) {
    uint rgba_color = *(device const uint*)(buf + ref + 8);
    return rgba_color;
}

inline float PietStrokeLine_width(const device char *buf, PietStrokeLineRef ref) {
    float width = as_type<float>(*(device const uint*)(buf + ref + 12));
    return width;
}

inline float2 PietStrokeLine_start(const device char *buf, PietStrokeLineRef ref) {
    float2 start = as_type<float2>(*(device const packed_uint2*)(buf + ref + 16));
    return start;
}

inline float2 PietStrokeLine_end(const device char *buf, PietStrokeLineRef ref) {
    float2 end = as_type<float2>(*(device const packed_uint2*)(buf + ref + 24));
    return end;
}

struct PietStrokeLine {
    uint flags;
    uint rgba_color;
    float width;
    float2 start;
    float2 end;
};

inline PietStrokeLine PietStrokeLine_unpack(PietStrokeLinePacked packed_form) {
    PietStrokeLine result;

    result.flags = packed_form.flags;
    result.rgba_color = packed_form.rgba_color;
    result.width = packed_form.width;
    result.start = packed_form.start;
    result.end = packed_form.end;

    return result;
}

struct PietFillPacked {
    uint tag;
    uint flags;
    uint rgba_color;
    uint n_points;
    uint points_ix;
};

inline PietFillPacked PietFill_read(const device char *buf, PietFillRef ref) {
    PietFillPacked result;

    uint flags = *(device const uint*)(buf + ref + 4);
    result.flags = flags;

    uint rgba_color = *(device const uint*)(buf + ref + 8);
    result.rgba_color = rgba_color;

    uint n_points = *(device const uint*)(buf + ref + 12);
    result.n_points = n_points;

    uint points_ix = *(device const uint*)(buf + ref + 16);
    result.points_ix = points_ix;

    return result;
}

inline uint PietFill_flags(const device char *buf, PietFillRef ref) {
    uint flags = *(device const uint*)(buf + ref + 4);
    return flags;
}

inline uint PietFill_rgba_color(const device char *buf, PietFillRef ref) {
    uint rgba_color = *(device const uint*)(buf + ref + 8);
    return rgba_color;
}

inline uint PietFill_n_points(const device char *buf, PietFillRef ref) {
    uint n_points = *(device const uint*)(buf + ref + 12);
    return n_points;
}

inline uint PietFill_points_ix(const device char *buf, PietFillRef ref) {
    uint points_ix = *(device const uint*)(buf + ref + 16);
    return points_ix;
}

struct PietFill {
    uint flags;
    uint rgba_color;
    uint n_points;
    uint points_ix;
};

inline PietFill PietFill_unpack(PietFillPacked packed_form) {
    PietFill result;

    result.flags = packed_form.flags;
    result.rgba_color = packed_form.rgba_color;
    result.n_points = packed_form.n_points;
    result.points_ix = packed_form.points_ix;

    return result;
}

struct PietStrokePolyLinePacked {
    uint tag;
    uint rgba_color;
    float width;
    uint n_points;
    uint points_ix;
};

inline PietStrokePolyLinePacked PietStrokePolyLine_read(const device char *buf, PietStrokePolyLineRef ref) {
    PietStrokePolyLinePacked result;

    uint rgba_color = *(device const uint*)(buf + ref + 4);
    result.rgba_color = rgba_color;

    float width = as_type<float>(*(device const uint*)(buf + ref + 8));
    result.width = width;

    uint n_points = *(device const uint*)(buf + ref + 12);
    result.n_points = n_points;

    uint points_ix = *(device const uint*)(buf + ref + 16);
    result.points_ix = points_ix;

    return result;
}

inline uint PietStrokePolyLine_rgba_color(const device char *buf, PietStrokePolyLineRef ref) {
    uint rgba_color = *(device const uint*)(buf + ref + 4);
    return rgba_color;
}

inline float PietStrokePolyLine_width(const device char *buf, PietStrokePolyLineRef ref) {
    float width = as_type<float>(*(device const uint*)(buf + ref + 8));
    return width;
}

inline uint PietStrokePolyLine_n_points(const device char *buf, PietStrokePolyLineRef ref) {
    uint n_points = *(device const uint*)(buf + ref + 12);
    return n_points;
}

inline uint PietStrokePolyLine_points_ix(const device char *buf, PietStrokePolyLineRef ref) {
    uint points_ix = *(device const uint*)(buf + ref + 16);
    return points_ix;
}

struct PietStrokePolyLine {
    uint rgba_color;
    float width;
    uint n_points;
    uint points_ix;
};

inline PietStrokePolyLine PietStrokePolyLine_unpack(PietStrokePolyLinePacked packed_form) {
    PietStrokePolyLine result;

    result.rgba_color = packed_form.rgba_color;
    result.width = packed_form.width;
    result.n_points = packed_form.n_points;
    result.points_ix = packed_form.points_ix;

    return result;
}

struct PietItem {
    uint tag;
    uint body[7];
};
inline uint PietItem_tag(const device char *buf, PietItemRef ref) {
    uint result = *(device const uint*)(buf + ref);
    return result;
}

#define SIMPLE_GROUP_SIZE 16
#define PIET_ITEM_SIZE 32
// TODO: these are manually fixed up. Make encoders consistent
#define PietItem_Circle 1
#define PietItem_Line 2
#define PietItem_Fill 3
#define PietItem_Poly 4

// Following are older-style accessors (haven't converted ptcl yet)

typedef uint CmdCircleRef;
typedef uint CmdLineRef;
typedef uint CmdStrokeRef;
typedef uint CmdFillRef;
typedef uint CmdFillEdgeRef;
typedef uint CmdDrawFillRef;
typedef uint CmdSolidRef;
typedef uint CmdRef;
struct CmdCirclePacked {
    uint tag;
    ushort4 bbox;
};
CmdCirclePacked CmdCircle_read(const device char *buf, CmdCircleRef ref) {
    return *((const device CmdCirclePacked *)(buf + ref));
}
ushort4 CmdCircle_bbox(const device char *buf, CmdCircleRef ref) {
    return ((const device CmdCirclePacked *)(buf + ref))->bbox;
}
struct CmdLinePacked {
    uint tag;
    float2 start;
    float2 end;
};
CmdLinePacked CmdLine_read(const device char *buf, CmdLineRef ref) {
    return *((const device CmdLinePacked *)(buf + ref));
}
float2 CmdLine_start(const device char *buf, CmdLineRef ref) {
    return ((const device CmdLinePacked *)(buf + ref))->start;
}
float2 CmdLine_end(const device char *buf, CmdLineRef ref) {
    return ((const device CmdLinePacked *)(buf + ref))->end;
}
struct CmdStrokePacked {
    uint tag;
    float halfWidth;
    uint rgba_color;
};
CmdStrokePacked CmdStroke_read(const device char *buf, CmdStrokeRef ref) {
    return *((const device CmdStrokePacked *)(buf + ref));
}
float CmdStroke_halfWidth(const device char *buf, CmdStrokeRef ref) {
    return ((const device CmdStrokePacked *)(buf + ref))->halfWidth;
}
uint CmdStroke_rgba_color(const device char *buf, CmdStrokeRef ref) {
    return ((const device CmdStrokePacked *)(buf + ref))->rgba_color;
}
struct CmdFillPacked {
    uint tag;
    float2 start;
    float2 end;
};
CmdFillPacked CmdFill_read(const device char *buf, CmdFillRef ref) {
    return *((const device CmdFillPacked *)(buf + ref));
}
float2 CmdFill_start(const device char *buf, CmdFillRef ref) {
    return ((const device CmdFillPacked *)(buf + ref))->start;
}
float2 CmdFill_end(const device char *buf, CmdFillRef ref) {
    return ((const device CmdFillPacked *)(buf + ref))->end;
}
struct CmdFillEdgePacked {
    uint tag;
    int sign;
    float y;
};
CmdFillEdgePacked CmdFillEdge_read(const device char *buf, CmdFillEdgeRef ref) {
    return *((const device CmdFillEdgePacked *)(buf + ref));
}
int CmdFillEdge_sign(const device char *buf, CmdFillEdgeRef ref) {
    return ((const device CmdFillEdgePacked *)(buf + ref))->sign;
}
float CmdFillEdge_y(const device char *buf, CmdFillEdgeRef ref) {
    return ((const device CmdFillEdgePacked *)(buf + ref))->y;
}
struct CmdDrawFillPacked {
    uint tag;
    int backdrop;
    uint rgba_color;
};
CmdDrawFillPacked CmdDrawFill_read(const device char *buf, CmdDrawFillRef ref) {
    return *((const device CmdDrawFillPacked *)(buf + ref));
}
int CmdDrawFill_backdrop(const device char *buf, CmdDrawFillRef ref) {
    return ((const device CmdDrawFillPacked *)(buf + ref))->backdrop;
}
uint CmdDrawFill_rgba_color(const device char *buf, CmdDrawFillRef ref) {
    return ((const device CmdDrawFillPacked *)(buf + ref))->rgba_color;
}
struct CmdSolidPacked {
    uint tag;
    uint rgba_color;
};
CmdSolidPacked CmdSolid_read(const device char *buf, CmdSolidRef ref) {
    return *((const device CmdSolidPacked *)(buf + ref));
}
uint CmdSolid_rgba_color(const device char *buf, CmdSolidRef ref) {
    return ((const device CmdSolidPacked *)(buf + ref))->rgba_color;
}
struct Cmd {
    uint tag;
    uint body[5];
};
Cmd Cmd_read(const device char *buf, CmdRef ref) {
    return *((const device Cmd *)(buf + ref));
}
uint Cmd_tag(const device char *buf, CmdRef ref) {
    return ((const device Cmd *)(buf + ref))->tag;
}
#define Cmd_End 1
#define Cmd_Circle 2
CmdCirclePacked CmdCircle_load(const thread Cmd &s) {
    CmdCirclePacked r;
    r.tag = s.tag;
    r.bbox = *((const thread ushort4 *)((const thread char *)&s + 8));
    return r;
}
#define Cmd_Line 3
CmdLinePacked CmdLine_load(const thread Cmd &s) {
    CmdLinePacked r;
    r.tag = s.tag;
    r.start = *((const thread float2 *)((const thread char *)&s + 8));
    r.end = *((const thread float2 *)((const thread char *)&s + 16));
    return r;
}
#define Cmd_Fill 4
CmdFillPacked CmdFill_load(const thread Cmd &s) {
    CmdFillPacked r;
    r.tag = s.tag;
    r.start = *((const thread float2 *)((const thread char *)&s + 8));
    r.end = *((const thread float2 *)((const thread char *)&s + 16));
    return r;
}
#define Cmd_Stroke 5
CmdStrokePacked CmdStroke_load(const thread Cmd &s) {
    CmdStrokePacked r;
    r.tag = s.tag;
    r.halfWidth = *((const thread float *)((const thread char *)&s + 4));
    r.rgba_color = *((const thread uint *)((const thread char *)&s + 8));
    return r;
}
#define Cmd_FillEdge 6
CmdFillEdgePacked CmdFillEdge_load(const thread Cmd &s) {
    CmdFillEdgePacked r;
    r.tag = s.tag;
    r.sign = *((const thread int *)((const thread char *)&s + 4));
    r.y = *((const thread float *)((const thread char *)&s + 8));
    return r;
}
#define Cmd_DrawFill 7
CmdDrawFillPacked CmdDrawFill_load(const thread Cmd &s) {
    CmdDrawFillPacked r;
    r.tag = s.tag;
    r.backdrop = *((const thread int *)((const thread char *)&s + 4));
    r.rgba_color = *((const thread uint *)((const thread char *)&s + 8));
    return r;
}
#define Cmd_Solid 8
CmdSolidPacked CmdSolid_load(const thread Cmd &s) {
    CmdSolidPacked r;
    r.tag = s.tag;
    r.rgba_color = *((const thread uint *)((const thread char *)&s + 4));
    return r;
}
#define Cmd_Bail 9

void CmdCircle_write(device char *buf, CmdCircleRef ref, CmdCirclePacked s) {
    *((device CmdCirclePacked *)(buf + ref)) = s;
}
void CmdLine_write(device char *buf, CmdLineRef ref, CmdLinePacked s) {
    *((device CmdLinePacked *)(buf + ref)) = s;
}
void CmdStroke_write(device char *buf, CmdStrokeRef ref, CmdStrokePacked s) {
    *((device CmdStrokePacked *)(buf + ref)) = s;
}
void CmdFill_write(device char *buf, CmdFillRef ref, CmdFillPacked s) {
    *((device CmdFillPacked *)(buf + ref)) = s;
}
void CmdFillEdge_write(device char *buf, CmdFillEdgeRef ref, CmdFillEdgePacked s) {
    *((device CmdFillEdgePacked *)(buf + ref)) = s;
}
void CmdDrawFill_write(device char *buf, CmdDrawFillRef ref, CmdDrawFillPacked s) {
    *((device CmdDrawFillPacked *)(buf + ref)) = s;
}
void CmdSolid_write(device char *buf, CmdSolidRef ref, CmdSolidPacked s) {
    *((device CmdSolidPacked *)(buf + ref)) = s;
}
void Cmd_write_tag(device char *buf, CmdRef ref, uint tag) {
    ((device Cmd *)(buf + ref))->tag = tag;
}



================================================
FILE: TestApp/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>$(DEVELOPMENT_LANGUAGE)</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIconFile</key>
	<string></string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleVersion</key>
	<string>1</string>
	<key>LSMinimumSystemVersion</key>
	<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
	<key>NSHumanReadableCopyright</key>
	<string>Copyright 2019 The xi-editor authors.</string>
	<key>NSMainStoryboardFile</key>
	<string>Main</string>
	<key>NSPrincipalClass</key>
	<string>NSApplication</string>
</dict>
</plist>


================================================
FILE: TestApp/PietRender.metal
================================================
//  Copyright 2019 The xi-editor authors.

#include <metal_stdlib>
using namespace metal;

#include "PietShaderTypes.h"
#include "GenTypes.h"

struct RenderData {
    float4 clipSpacePosition [[position]];
    float2 textureCoordinate;
    float pointSize [[point_size]];
    half4 solidColor;
};

vertex RenderData
vertexShader(uint vertexID [[ vertex_id ]],
             constant RenderVertex *vertexArray [[ buffer(RenderVertexInputIndexVertices) ]],
             texture2d<half> loTexture [[texture(0)]])
{
    RenderData out;
    float2 clipSpacePosition = vertexArray[vertexID].position;
    out.clipSpacePosition.xy = clipSpacePosition;
    out.clipSpacePosition.z = 0.0;
    out.clipSpacePosition.w = 1.0;
    float2 xy = vertexArray[vertexID].textureCoordinate;
    out.textureCoordinate = xy;
    out.pointSize = 16;
    uint2 tileXY = uint2(xy.x / tileWidth, xy.y / tileHeight);
    out.solidColor = loTexture.read(tileXY);
    return out;
}

fragment half4 fragmentShader(RenderData in [[stage_in]],
                               texture2d<half> texture [[texture(0)]]) {
    const half4 loSample = in.solidColor;
    if (loSample.a == 0.0) {
        uint2 coords = uint2(in.clipSpacePosition.xy);
        const half4 sample = texture.read(coords);
        return sample;
    } else {
        return loSample;
    }
}

// Distance field rendering of strokes

// Accumulate distance field.
void stroke(thread float &df, float2 pos, float2 start, float2 end) {
    float2 lineVec = end - start;
    float2 dPos = pos - start;
    float t = saturate(dot(lineVec, dPos) / dot(lineVec, lineVec));
    float field = length(lineVec * t - dPos);
    df = min(df, field);
}

// TODO: figure out precision so we can move more stuff to half
half renderDf(float df, float halfWidth) {
    return saturate(halfWidth + 0.5 - df);
}

/*
// TODO: this should be in the autogenerated output
void Cmd_write_tag(device char *buf, CmdRef ref, uint tag) {
    ((device Cmd *)(buf + ref))->tag = tag;
}
 */

struct TileEncoder {
public:
    TileEncoder(device char *dst) {
        this->dst = dst;
        this->tileBegin = dst;
        this->solidColor = 0xffffffff;
    }
    void encodeCircle(ushort4 bbox) {
        CmdCirclePacked cmd;
        cmd.tag = Cmd_Circle;
        cmd.bbox = bbox;
        CmdCircle_write(dst, 0, cmd);
        solidColor = 0;
        dst += sizeof(Cmd);
    }
    void encodeLine(float2 start, float2 end) {
        CmdLinePacked cmd;
        cmd.tag = Cmd_Line;
        cmd.start = start;
        cmd.end = end;
        CmdLine_write(dst, 0, cmd);
        solidColor = 0;
        dst += sizeof(Cmd);
    }
    void encodeStroke(uint rgbaColor, float width) {
        CmdStrokePacked cmd;
        cmd.tag = Cmd_Stroke;
        cmd.rgba_color = rgbaColor;
        cmd.halfWidth = 0.5 * width;
        CmdStroke_write(dst, 0, cmd);
        solidColor = 0;
        dst += sizeof(Cmd);
    }
    void encodeFill(float2 start, float2 end) {
        CmdFillPacked cmd;
        cmd.tag = Cmd_Fill;
        cmd.start = start;
        cmd.end = end;
        CmdFill_write(dst, 0, cmd);
        dst += sizeof(Cmd);
    }
    void encodeFillEdge(float sign, float y) {
        CmdFillEdgePacked cmd;
        cmd.tag = Cmd_FillEdge;
        cmd.sign = sign;
        cmd.y = y;
        CmdFillEdge_write(dst, 0, cmd);
        dst += sizeof(Cmd);
    }
    void encodeDrawFill(const thread PietFillPacked &fill, int backdrop) {
        CmdDrawFillPacked cmd;
        cmd.tag = Cmd_DrawFill;
        cmd.backdrop = backdrop;
        cmd.rgba_color = fill.rgba_color;
        CmdDrawFill_write(dst, 0, cmd);
        solidColor = 0;
        dst += sizeof(Cmd);
    }
    void encodeSolid(uint rgba) {
        // A fun optimization would be to alpha-composite semi-opaque
        // solid blocks.
        
        // Another optimization is to skip encoding the default bg color.
        if ((rgba & 0xff000000) == 0xff000000) {
            solidColor = rgba;
            dst = tileBegin;
        }
        CmdSolidPacked cmd;
        // Note: could defer writing, not sure how much of a win that is
        cmd.tag = Cmd_Solid;
        cmd.rgba_color = rgba;
        CmdSolid_write(dst, 0, cmd);
        dst += sizeof(Cmd);
    }
    // return solid color
    uint end() {
        if (solidColor) {
            Cmd_write_tag(tileBegin, 0, Cmd_Bail);
        } else {
            Cmd_write_tag(dst, 0, Cmd_End);
        }
        return solidColor;
    }
private:
    // Pointer to command buffer for tile.
    device char *dst;
    device char *tileBegin;
    uint solidColor;
};

// Traverse the scene graph and produce a command list for a tile.
kernel void
tileKernel(device const char *scene [[buffer(0)]],
           device char *tiles [[buffer(1)]],
           texture2d<half, access::write> outTexture [[texture(0)]],
           uint2 gid [[thread_position_in_grid]],
           uint tix [[thread_index_in_threadgroup]])
{
    uint tileIx = gid.y * maxTilesWidth + gid.x;
    ushort x0 = gid.x * tileWidth;
    ushort y0 = gid.y * tileHeight;
    device char *dst = tiles + tileIx * tileBufSize;
    TileEncoder encoder(dst);
    // TODO: correct calculation of size
    const ushort tgs = tilerGroupWidth * tilerGroupHeight;
    const ushort nBitmap = tgs / 32;
    threadgroup atomic_uint bitmap;
    threadgroup uint rd;
    const memory_order relaxed = memory_order::memory_order_relaxed;

    // Size of the region covered by one SIMD group. TODO, don't hardcode.
    const ushort stw = tilerGroupWidth * tileWidth;
    const ushort sth = tilerGroupHeight * tileHeight;
    ushort sx0 = x0 & ~(stw - 1);
    ushort sy0 = y0 & ~(sth - 1);
    
    SimpleGroupRef group_ref = 0;
    // TODO: write accessor functions for variable-sized array here
    device const SimpleGroupPacked *group = (device const SimpleGroupPacked *)scene;
    device const ushort4 *bboxes = (device const ushort4 *)&group->bbox;
    uint n = SimpleGroup_n_items(scene, group_ref);
    PietItemRef items_ref = SimpleGroup_items_ix(scene, group_ref);
    for (uint i = 0; i < n; i += tgs) {
        if (tix < nBitmap) {
            atomic_store_explicit(&bitmap, 0, relaxed);
        }
        threadgroup_barrier(mem_flags::mem_threadgroup);
        if (i + tix < n) {
            ushort4 bbox = bboxes[i + tix];
            if (bbox.z >= sx0 && bbox.x < sx0 + stw && bbox.w >= sy0 && bbox.y < sy0 + sth) {
                uint mask = 1 << (tix & 31);
                atomic_fetch_or_explicit(&bitmap, mask, relaxed);
            }
        }
        threadgroup_barrier(mem_flags::mem_threadgroup);
        if (tix == 0) {
            rd = atomic_load_explicit(&bitmap, relaxed);
            atomic_store_explicit(&bitmap, 0, relaxed);
        }
        threadgroup_barrier(mem_flags::mem_threadgroup);
        //for (ushort bitmapIx = 0; bitmapIx <= bitmapCount; bitmapIx++) {
            uint v = rd;
            while (v) {
                uint ix = i + ctz(v);
                ushort4 bbox = bboxes[ix];
                bool hit = bbox.z >= x0 && bbox.x < x0 + tileWidth && bbox.w >= y0 && bbox.y < y0 + tileHeight;
                PietItemRef item_ref = items_ref + ix * sizeof(PietItem);
                ushort itemType = PietItem_tag(scene, item_ref);
                switch (itemType) {
                    case PietItem_Circle:
                        if (hit) {
                            encoder.encodeCircle(bbox);
                        }
                        break;
                    case PietItem_Line: {
                        // set up line equation, ax + by + c = 0
                        if (hit) {
                            PietStrokeLinePacked line = PietStrokeLine_read(scene, item_ref);
                            float a = line.end.y - line.start.y;
                            float b = line.start.x - line.end.x;
                            float c = -(a * line.start.x + b * line.start.y);
                            // TODO: is this bound as tight as it can be?
                            float hw = 0.5 * line.width + 0.5;
                            float left = a * (x0 - hw);
                            float right = a * (x0 + tileWidth + hw);
                            float top = b * (y0 - hw);
                            float bot = b * (y0 + tileHeight + hw);
                            // If all four corners are on same side of line, cull
                            float s00 = sign(top + left + c);
                            float s01 = sign(top + right + c);
                            float s10 = sign(bot + left + c);
                            float s11 = sign(bot + right + c);
                            if (s00 * s01 + s00 * s10 + s00 * s11 < 3.0) {
                                encoder.encodeLine(line.start, line.end);
                                encoder.encodeStroke(line.rgba_color, line.width);
                            }
                        }
                        break;
                    }
                    case PietItem_Fill: {
                        PietFillPacked fill = PietFill_read(scene, item_ref);
                        device const float2 *pts = (device const float2 *)(scene + fill.points_ix);
                        uint nPoints = fill.n_points;
                        float backdrop = 0;
                        bool anyFill = false;
                        // use simd ballot to quick-reject segments with no contribution
                        // Note: we just do 16 at a time for now, there's the option of doing
                        // a 16x2 strip of tiles, with more complexity in the left-ray test.
                        for (uint j = 0; j < nPoints; j += 16) {
                            bool fillHit = false;
                            uint fillIx = j + (tix & 15);
                            if (fillIx < nPoints) {
                                float2 start = pts[fillIx];
                                float2 end = pts[fillIx + 1 == nPoints ? 0 : fillIx + 1];
                                float2 xymin = min(start, end);
                                float2 xymax = max(start, end);
                                if (xymax.y >= y0 && xymin.y < y0 + tileHeight && xymin.x < sx0 + stw) {
                                    // set up line equation, ax + by + c = 0
                                    float a = end.y - start.y;
                                    float b = start.x - end.x;
                                    float c = -(a * start.x + b * start.y);
                                    float left = a * sx0;
                                    float right = a * (sx0 + stw);
                                    float ytop = max(float(y0), xymin.y);
                                    float ybot = min(float(y0 + tileHeight), xymax.y);
                                    float top = b * ytop;
                                    float bot = b * ybot;
                                    // top left of rightmost tile in strip
                                    float sTopLeft = sign(right - a * (tileWidth) + float(y0) * b + c);
                                    float s00 = sign(top + left + c);
                                    float s01 = sign(top + right + c);
                                    float s10 = sign(bot + left + c);
                                    float s11 = sign(bot + right + c);
                                    if (sTopLeft == sign(a) && xymin.y <= y0) {
                                        // left ray intersects, need backdrop
                                        fillHit = true;
                                    }
                                    if (s00 * s01 + s00 * s10 + s00 * s11 < 3.0 && xymax.x > sx0) {
                                        // intersects strip
                                        fillHit = true;
                                    }
                                    // TODO: maybe avoid boolean - does it cost a register?
                                    if (fillHit) {
                                        atomic_fetch_or_explicit(&bitmap, 1 << tix, relaxed);
                                    }
                                }
                            }
                            threadgroup_barrier(mem_flags::mem_threadgroup);
                            if (tix == 0) {
                                rd = atomic_load_explicit(&bitmap, relaxed);
                                atomic_store_explicit(&bitmap, 0, relaxed);
                            }
                            threadgroup_barrier(mem_flags::mem_threadgroup);
                            uint fillVote = (rd >> (tix & 16)) & 0xffff;
                            while (fillVote) {
                                uint fillSubIx = ctz(fillVote);
                                fillIx = j + fillSubIx;

                                if (hit) {
                                    float2 start = pts[fillIx];
                                    float2 end = pts[fillIx + 1 == nPoints ? 0 : fillIx + 1];
                                    float2 xymin = min(start, end);
                                    float2 xymax = max(start, end);
                                    // Note: no y-based cull here because it's been done in the earlier pass.
                                    // If we change that to do a strip taller than 1 tile, re-introduce here.

                                    // set up line equation, ax + by + c = 0
                                    float a = end.y - start.y;
                                    float b = start.x - end.x;
                                    float c = -(a * start.x + b * start.y);
                                    float left = a * x0;
                                    float right = a * (x0 + tileWidth);
                                    float ytop = max(float(y0), xymin.y);
                                    float ybot = min(float(y0 + tileHeight), xymax.y);
                                    float top = b * ytop;
                                    float bot = b * ybot;
                                    // top left of tile
                                    float sTopLeft = sign(left + float(y0) * b + c);
                                    float s00 = sign(top + left + c);
                                    float s01 = sign(top + right + c);
                                    float s10 = sign(bot + left + c);
                                    float s11 = sign(bot + right + c);
                                    if (sTopLeft == sign(a) && xymin.y <= y0) {
                                        backdrop -= s00;
                                    }
                                    if (xymin.x < x0 && xymax.x > x0) {
                                        float yEdge = mix(start.y, end.y, (start.x - x0) / b);
                                        if (yEdge >= y0 && yEdge < y0 + tileHeight) {
                                            // line intersects left edge of this tile
                                            encoder.encodeFillEdge(s00, yEdge);
                                            if (b > 0.0) {
                                                encoder.encodeFill(start, float2(x0, yEdge));
                                            } else {
                                                encoder.encodeFill(float2(x0, yEdge), end);
                                            }
                                            anyFill = true;
                                        } else if (s00 * s01 + s00 * s10 + s00 * s11 < 3.0) {
                                            encoder.encodeFill(start, end);
                                            anyFill = true;
                                        }
                                    } else if (s00 * s01 + s00 * s10 + s00 * s11 < 3.0
                                               && xymin.x < x0 + tileWidth && xymax.x > x0) {
                                        encoder.encodeFill(start, end);
                                        anyFill = true;
                                    }
                                } // end if (hit)

                                fillVote &= ~(1 << fillSubIx);
                            }
                        }
                        if (anyFill) {
                            encoder.encodeDrawFill(fill, backdrop);
                        } else if (backdrop != 0.0) {
                            encoder.encodeSolid(fill.rgba_color);
                        }
                        break;
                    }
                    case PietItem_Poly: {
                        PietStrokePolyLinePacked poly = PietStrokePolyLine_read(scene, item_ref);
                        device const float2 *pts = (device const float2 *)(scene + poly.points_ix);
                        uint nPoints = poly.n_points - 1;
                        bool anyStroke = false;
                        float hw = 0.5 * poly.width + 0.5;
                        // use simd ballot to quick-reject segments with no contribution
                        for (uint j = 0; j < nPoints; j += 32) {
                            uint polyIx = j + tix;
                            if (polyIx < nPoints) {
                                float2 start = pts[polyIx];
                                float2 end = pts[polyIx + 1];
                                float2 xymin = min(start, end);
                                float2 xymax = max(start, end);
                                if (xymax.y > sy0 - hw && xymin.y < sy0 + sth + hw &&
                                    xymax.x > sx0 - hw && xymin.x < sx0 + stw + hw) {
                                    // set up line equation, ax + by + c = 0
                                    float a = end.y - start.y;
                                    float b = start.x - end.x;
                                    float c = -(a * start.x + b * start.y);
                                    float left = a * (sx0 - hw);
                                    float right = a * (sx0 + stw + hw);
                                    float top = b * (y0 - hw);
                                    float bot = b * (y0 + tileHeight + hw);
                                    float s00 = sign(top + left + c);
                                    float s01 = sign(top + right + c);
                                    float s10 = sign(bot + left + c);
                                    float s11 = sign(bot + right + c);
                                    if (s00 * s01 + s00 * s10 + s00 * s11 < 3.0) {
                                        // intersects strip
                                        atomic_fetch_or_explicit(&bitmap, 1 << tix, relaxed);
                                    }
                                }
                            }
                            threadgroup_barrier(mem_flags::mem_threadgroup);
                            if (tix == 0) {
                                rd = atomic_load_explicit(&bitmap, relaxed);
                                atomic_store_explicit(&bitmap, 0, relaxed);
                            }
                            threadgroup_barrier(mem_flags::mem_threadgroup);
                            uint polyVote = rd;
                            while (polyVote) {
                                uint polySubIx = ctz(polyVote);
                                polyIx = j + polySubIx;
                                
                                if (hit) {
                                    float2 start = pts[polyIx];
                                    float2 end = pts[polyIx + 1];
                                    float2 xymin = min(start, end);
                                    float2 xymax = max(start, end);
                                    if (xymax.y > y0 - hw && xymin.y < y0 + tileHeight + hw &&
                                        xymax.x > x0 - hw && xymin.x < x0 + tileWidth + hw) {
                                        float a = end.y - start.y;
                                        float b = start.x - end.x;
                                        float c = -(a * start.x + b * start.y);
                                        float hw = 0.5 * poly.width + 0.5;
                                        float left = a * (x0 - hw);
                                        float right = a * (x0 + tileWidth + hw);
                                        float top = b * (y0 - hw);
                                        float bot = b * (y0 + tileHeight + hw);
                                        // If all four corners are on same side of line, cull
                                        float s00 = sign(top + left + c);
                                        float s01 = sign(top + right + c);
                                        float s10 = sign(bot + left + c);
                                        float s11 = sign(bot + right + c);
                                        if (s00 * s01 + s00 * s10 + s00 * s11 < 3.0) {
                                            encoder.encodeLine(start, end);
                                            anyStroke = true;
                                        }
                                    }
                                } // end if (hit)
                                
                                polyVote &= ~(1 << polySubIx);
                            }
                        }
                        if (anyStroke) {
                            encoder.encodeStroke(poly.rgba_color, poly.width);
                        }
                        break;
                    }
                } // end switch(itemType);
                v &= v - 1;
            } // end while (v)
            threadgroup_barrier(mem_flags::mem_threadgroup);
        //} // end for (bitmapIx)
    }
    uint solidColor = encoder.end();
    outTexture.write(unpack_unorm4x8_to_half(solidColor), gid);
}

// Interpret the commands in the command list to produce a pixel.
kernel void
renderKernel(texture2d<half, access::write> outTexture [[texture(0)]],
             const device char *tiles [[buffer(0)]],
             uint2 gid [[thread_position_in_grid]],
             uint2 tgid [[threadgroup_position_in_grid]])
{
    uint tileIx = tgid.y * maxTilesWidth + tgid.x;
    const device char *src = tiles + tileIx * tileBufSize;
    uint x = gid.x;
    uint y = gid.y;
    float2 xy = float2(x, y);

    // Render state (maybe factor out?)
    half3 rgb = half3(1.0);
    float df = 1e9;
    half signedArea = 0.0;

    while (1) {
        Cmd cmd = Cmd_read(src, 0);
        uint tag = cmd.tag;
        if (tag == Cmd_End) {
            break;
        }
        switch (tag) {
            case Cmd_Circle: {
                CmdCirclePacked circle = CmdCircle_load(cmd);
                ushort4 bbox = circle.bbox;
                float2 xy0 = float2(bbox.x, bbox.y);
                float2 xy1 = float2(bbox.z, bbox.w);
                float2 center = mix(xy0, xy1, 0.5);
                float r = length(xy - center);
                // I should make this shade an ellipse properly but am too lazy.
                // But see WebRender ellipse.glsl (linked in notes)
                float circleR = min(center.x - xy0.x, center.y - xy0.y);
                float alpha = saturate(circleR - r);
                rgb = mix(rgb, half3(0.0), alpha);
                break;
            }
            case Cmd_Line: {
                CmdLinePacked line = CmdLine_load(cmd);
                stroke(df, xy, line.start, line.end);
                break;
            }
            case Cmd_Stroke: {
                CmdStrokePacked stroke = CmdStroke_load(cmd);
                half alpha = renderDf(df, stroke.halfWidth);
                half4 fg = unpack_unorm4x8_srgb_to_half(stroke.rgba_color);
                rgb = mix(rgb, fg.rgb, fg.a * alpha);
                df = 1e9;
                break;
            }
            case Cmd_Fill: {
                CmdFillPacked fill = CmdFill_load(cmd);
                float2 start = fill.start - xy;
                float2 end = fill.end - xy;
                float2 window = saturate(float2(start.y, end.y));
                // maybe should be an epsilon test for better numerical stability
                if (window.x != window.y) {
                    float2 t = (window - start.y) / (end.y - start.y);
                    float2 xs = mix(float2(start.x), float2(end.x), t);
                    // This fudge factor might be inadequate when xmax is large, could
                    // happen with small slopes.
                    float xmin = min(min(xs.x, xs.y), 1.0) - 1e-6;
                    float xmax = max(xs.x, xs.y);
                    float b = min(xmax, 1.0);
                    float c = max(b, 0.0);
                    float d = max(xmin, 0.0);
                    float area = (b + 0.5 * (d * d - c * c) - xmin) / (xmax - xmin);
                    // TODO: evaluate accuracy loss from more use of half
                    signedArea += half(area * (window.x - window.y));
                }
                break;
            }
            case Cmd_FillEdge: {
                CmdFillEdgePacked fill = CmdFillEdge_load(cmd);
                signedArea += fill.sign * saturate(y - fill.y + 1);
                break;
            }
            case Cmd_DrawFill: {
                CmdDrawFillPacked draw = CmdDrawFill_load(cmd);
                half alpha = signedArea + half(draw.backdrop);
                alpha = min(abs(alpha), 1.0h); // nonzero winding rule
                // even-odd is: alpha = abs(alpha - 2.0 * round(0.5 * alpha))
                // also: abs(2 * fract(0.5 * (x - 1.0)) - 1.0)
                half4 fg = unpack_unorm4x8_srgb_to_half(draw.rgba_color);
                rgb = mix(rgb, fg.rgb, fg.a * alpha);
                signedArea = 0.0;
                break;
            }
            case Cmd_Solid: {
                CmdSolidPacked solid = CmdSolid_load(cmd);
                half4 fg = unpack_unorm4x8_srgb_to_half(solid.rgba_color);
                rgb = mix(rgb, fg.rgb, fg.a);
                break;
            }
            case Cmd_Bail:
                return;
            // This case shouldn't happen, but we'll keep it for debugging.
            default:
                outTexture.write(half4(1.0, 0.0, 1.0, 1.0), gid);
                return;
        }
        src += sizeof(Cmd);
    }
    // Linear to sRGB conversion. Note that if we had writable sRGB textures
    // we could let this be done in the write call.
    rgb = select(1.055 * pow(rgb, 1/2.4) - 0.055, 12.92 * rgb, rgb < 0.0031308);
    half4 rgba = half4(rgb, 1.0);
    outTexture.write(rgba, gid);
}


================================================
FILE: TestApp/PietRenderer.h
================================================
//  Copyright 2019 The xi-editor authors.

@import MetalKit;

@interface PietRenderer : NSObject<MTKViewDelegate>

- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView;

@end


================================================
FILE: TestApp/PietRenderer.m
================================================
//  Copyright 2019 The xi-editor authors.

@import MetalKit;

#import "PietRenderer.h"
#import "PietShaderTypes.h"
#include "piet_metal.h"

@implementation PietRenderer {
    id<MTLDevice> _device;
    id<MTLComputePipelineState> _tilePipelineState;
    id<MTLComputePipelineState> _computePipelineState;
    id<MTLRenderPipelineState> _renderPipelineState;
    id<MTLCommandQueue> _commandQueue;
    id<MTLTexture> _texture;
    id<MTLTexture> _loTexture;
    id<MTLBuffer> _sceneBuf;
    id<MTLBuffer> _tileBuf;
    id<MTLBuffer> _vertexBuf;
    vector_uint2 _viewportSize;
}

- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView {
    self = [super init];
    if (self) {
        NSError *error = NULL;
        _device = mtkView.device;
        // Note: this is consciously not sRGB, we do the conversion before writing the texture.
        mtkView.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
        id<MTLLibrary> defaultLibrary = [_device newDefaultLibrary];
        MTLRenderPipelineDescriptor *pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
        id<MTLFunction> tileFunction = [defaultLibrary newFunctionWithName:@"tileKernel"];
        id<MTLFunction> kernelFunction = [defaultLibrary newFunctionWithName:@"renderKernel"];
        id<MTLFunction> vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"];
        id<MTLFunction> fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"];
        pipelineDescriptor.vertexFunction = vertexFunction;
        pipelineDescriptor.fragmentFunction = fragmentFunction;
        pipelineDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat;

        _tilePipelineState = [_device newComputePipelineStateWithFunction:tileFunction error:&error];
        _computePipelineState = [_device newComputePipelineStateWithFunction:kernelFunction error:&error];
        if (!_tilePipelineState || !_computePipelineState) {
            NSLog(@"Failed to create compute pipeline state, error %@", error);
            return nil;
        }
        _renderPipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineDescriptor error: &error];

        _commandQueue = [_device newCommandQueue];
        
        NSUInteger tileBufSizeBytes = maxTilesWidth * maxTilesHeight * tileBufSize;
        // Note: consider using managed here, worth experimenting with.
        MTLResourceOptions sceneOptions = MTLResourceStorageModeShared | MTLResourceCPUCacheModeWriteCombined;
        _sceneBuf = [_device newBufferWithLength:16*1024*1024 options:sceneOptions];
        _tileBuf = [_device newBufferWithLength:tileBufSizeBytes options:MTLResourceStorageModePrivate];
    }
    return self;
}

- (void)drawInMTKView:(nonnull MTKView *)view {
    id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
    commandBuffer.label = @"RenderCommand";

    uint nTilesX = (_viewportSize.x + tileWidth - 1) / tileWidth;
    uint nTilesY = (_viewportSize.y + tileHeight - 1) / tileHeight;

    uint nTilerGroupsX = (nTilesX + tilerGroupWidth - 1) / tilerGroupWidth;
    uint nTilerGroupsY = (nTilesY + tilerGroupHeight - 1) / tilerGroupHeight;

    // Run tile compute shader.
    id<MTLComputeCommandEncoder> computeEncoder = [commandBuffer computeCommandEncoder];
    [computeEncoder setComputePipelineState:_tilePipelineState];
    [computeEncoder setTexture:_loTexture atIndex:0];
    [computeEncoder setBuffer:_sceneBuf offset:0 atIndex:0];
    [computeEncoder setBuffer:_tileBuf offset:0 atIndex:1];
    MTLSize tilegroupSize = MTLSizeMake(tilerGroupWidth, tilerGroupHeight, 1);
    MTLSize tilegroupCount = MTLSizeMake(nTilerGroupsX, nTilerGroupsY, 1);
    [computeEncoder dispatchThreadgroups:tilegroupCount threadsPerThreadgroup:tilegroupSize];
    [computeEncoder endEncoding];

    // Run compute shader for rendering.
    computeEncoder = [commandBuffer computeCommandEncoder];
    [computeEncoder setComputePipelineState:_computePipelineState];
    [computeEncoder setTexture:_texture atIndex:0];
    [computeEncoder setBuffer:_tileBuf offset:0 atIndex:0];
    MTLSize threadgroupSize = MTLSizeMake(tileWidth, tileHeight, 1);
    MTLSize threadgroupCount = MTLSizeMake(nTilesX, nTilesY, 1);
    [computeEncoder dispatchThreadgroups:threadgroupCount threadsPerThreadgroup:threadgroupSize];
    [computeEncoder endEncoding];
    
    MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
    if (renderPassDescriptor != nil) {
        id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
        [renderEncoder setViewport:(MTLViewport){0.0, 0.0, _viewportSize.x, _viewportSize.y, -1.0, 1.0}];
        [renderEncoder setRenderPipelineState:_renderPipelineState];
        [renderEncoder setVertexBuffer:_vertexBuf offset:0 atIndex:RenderVertexInputIndexVertices];
        [renderEncoder setVertexTexture:_loTexture atIndex:0];
        [renderEncoder setFragmentTexture:_texture atIndex:0];
        [renderEncoder drawPrimitives:MTLPrimitiveTypePoint vertexStart:0 vertexCount:nTilesX * nTilesY];
        [renderEncoder endEncoding];
        [commandBuffer presentDrawable:view.currentDrawable];
    }
    [commandBuffer commit];
}

- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size {
    _viewportSize.x = size.width;
    _viewportSize.y = size.height;
    // TODO: try not to allocate as wildly on smooth resize (maybe round up
    // the size).
    MTLTextureDescriptor *descriptor = [[MTLTextureDescriptor alloc] init];
    descriptor.textureType = MTLTextureType2D;
    descriptor.pixelFormat = MTLPixelFormatBGRA8Unorm;
    descriptor.width = _viewportSize.x;
    descriptor.height = _viewportSize.y;
    descriptor.usage = MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead;
    _texture = [_device newTextureWithDescriptor:descriptor];

    uint nTilesX = (_viewportSize.x + tileWidth - 1) / tileWidth;
    uint nTilesY = (_viewportSize.y + tileHeight - 1) / tileHeight;

    descriptor.width = nTilesX;
    descriptor.height = nTilesY;
    _loTexture = [_device newTextureWithDescriptor:descriptor];
    
    uint vertexLen = nTilesX * nTilesY * sizeof(RenderVertex);
    MTLResourceOptions vertexOptions = MTLResourceStorageModeShared | MTLResourceCPUCacheModeWriteCombined;
    _vertexBuf = [_device newBufferWithLength:vertexLen options:vertexOptions];
    RenderVertex *vertices = (RenderVertex *)_vertexBuf.contents;
    uint ix = 0;
    float scaleX = 2.0 / _viewportSize.x;
    float scaleY = 2.0 / _viewportSize.y;
    for (uint y = 0; y < nTilesY; y++) {
        for (uint x = 0; x < nTilesX; x++) {
            RenderVertex rv;
            uint x0 = x * tileWidth + (tileWidth / 2);
            uint y0 = y * tileHeight + (tileHeight / 2);
            rv.position.x = x0 * scaleX - 1.0;
            rv.position.y = y0 * -scaleY + 1.0;
            rv.textureCoordinate.x = x0;
            rv.textureCoordinate.y = y0;
            vertices[ix++] = rv;
        }
    }

    [self initScene];
}

/*
- (void)initCardioid {
    float cx = 1024;
    float cy = 768;
    float r = 750;
    int n = 97;
    SceneEncoder *encoder = [[SceneEncoder alloc] initWithBuffer:_sceneBuf];
    [encoder beginGroup: 2 * (n - 1)];
    for (int i = 1; i < n; i++) {
        float th0 = 2 * M_PI * i / n;
        float th1 = 2 * M_PI * ((i * 2) % n) / n;
        vector_float2 start = simd_make_float2(cx + r * cos(th0), cy - r * sin(th0));
        vector_float2 end = simd_make_float2(cx + r * cos(th1), cy - r * sin(th1));
        [encoder circle:start radius: 8];
        [encoder line:start to:end width:2 color:0xff800000];
    }
    [encoder endGroup];
}

- (void)fillTest {
    const int n = 256;
    SceneEncoder *encoder = [[SceneEncoder alloc] initWithBuffer:_sceneBuf];
    [encoder beginGroup: n];
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < 3; j++) {
            uint32_t x = arc4random() % _viewportSize.x;
            uint32_t y = arc4random() % _viewportSize.y;
            [encoder addPt:simd_make_float2(x, y)];
        }
        [encoder fill:arc4random() | 0xff000000];
    }
    [encoder endGroup];
}

- (void)initCircles {
    const int radius = 8;
    const int n = 256;
    SceneEncoder *encoder = [[SceneEncoder alloc] initWithBuffer:_sceneBuf];
    [encoder beginGroup:n + 1];
    for (int i = 0; i < n; i++) {
        uint32_t x = arc4random() % _viewportSize.x;
        uint32_t y = arc4random() % _viewportSize.y;
        [encoder circle:simd_make_float2(x, y) radius:radius];
    }
    [encoder line:simd_make_float2(100, 500) to:simd_make_float2(700, 600) width:100 color:0xff800000];
    [encoder endGroup];
}

 - (void)initScene {
    //[self initCircles];
    //[self initCardioid];
    [self fillTest];
}
*/

- (void)initScene {
    init_test_scene(_sceneBuf.contents, _sceneBuf.allocatedSize);
}

@end


================================================
FILE: TestApp/PietShaderTypes.h
================================================
//  Copyright 2019 The xi-editor authors.

typedef struct
{
    // This is a clip space coordinate (-1 to 1).
    vector_float2 position;
    // This is now an integer coordinate for reading the texture.
    vector_float2 textureCoordinate;
} RenderVertex;

typedef enum RenderVertexInputIndex
{
    RenderVertexInputIndexVertices = 0,
} RenderVertexInputIndex;

// Size in pixels of an individual tile
#define tileWidth 16
#define tileHeight 16

// Size (in tiles) of a threadgroup for tiling
#define tilerGroupWidth 16
#define tilerGroupHeight 2

// The number of bytes in a buffer for a single tile.
// For prototyping, this is a hard maximum, but for production we'd want
// a mechanism to overflow.
#define tileBufSize 4096

// For simplicity, we're going to hardcode these dimensions. For production,
// they need to be dynamic.
#define maxTilesWidth 256
#define maxTilesHeight 256


================================================
FILE: TestApp/SceneEncoder.h
================================================
@interface SceneEncoder: NSObject

- (nonnull instancetype)initWithBuffer:(nonnull id<MTLBuffer>)buffer;

- (void)beginGroup:(uint)nItems;

- (void)endGroup;

- (void)circle:(vector_float2)center radius:(float)radius;

- (void)line:(vector_float2)start to:(vector_float2)end width:(float) width color:(uint) rgba;

- (void)addPt:(vector_float2)xy;

- (void)fill:(uint)rgba;

@end

// The following are legacy definitions - encoding in ObjC will not be supported
// going forward.
typedef struct SimpleGroup {
    uint nItems;
    // Offset in bytes to items
    uint itemsIx;
    vector_ushort4 bbox[1];
} SimpleGroup;

typedef struct PietCircle {
    uint itemType;
} PietCircle;

// A single line to be stroked, with default parameters
typedef struct PietStrokeLine {
    uint itemType;
    uint flags; // reserved, partially for alignment
    uint rgbaColor;
    float width;
    vector_float2 start;
    vector_float2 end;
} PietStrokeLine;

typedef struct PietFill {
    uint itemType;
    uint flags; // will be used for winding number rule
    uint rgbaColor;
    uint nPoints;
    uint pointsIx;
} PietFill;

typedef struct PietStrokePolyLine {
    uint itemType;
    uint rgbaColor;
    float width;
    uint nPoints;
    uint pointsIx;
} PietStrokePolyLine;

typedef union PietItem {
    uint itemType;
    PietCircle circle;
    PietStrokeLine line;
    PietFill fill;
    PietStrokePolyLine poly;
} PietItem;

// This should be an enum but the storage needs to be of fixed size
#define PIET_ITEM_CIRCLE 1
#define PIET_ITEM_LINE 2
#define PIET_ITEM_FILL 3
#define PIET_ITEM_STROKE_POLYLINE 4


================================================
FILE: TestApp/SceneEncoder.m
================================================
//  Copyright 2019 The xi-editor authors.

@import MetalKit;

#import "PietShaderTypes.h"
#import "SceneEncoder.h"

@implementation SceneEncoder {
    char *_buf;
    uint _bboxIx;
    uint _ix;
    uint _count;
    // Index of beginning of free space (currently allocation is just a bump).
    uint _freeSpace;
    uint _pointCount;
    vector_float4 _bbox;
}

- (nonnull instancetype)initWithBuffer:(nonnull id<MTLBuffer>)buffer {
    _buf = buffer.contents;
    _ix = 0;
    _freeSpace = 0;
    _pointCount = 0;
    _bbox = simd_make_float4(0.0);
    return self;
}

- (uint)alloc:(uint)size {
    uint ix = _freeSpace;
    _freeSpace += size;
    return ix;
}

- (void)beginGroup:(uint)nItems {
    uint size = sizeof(SimpleGroup) - sizeof(vector_ushort4) + nItems * (sizeof(vector_ushort4) + sizeof(PietItem));
    uint ix = [self alloc:size];
    SimpleGroup *group = (SimpleGroup *)(_buf + ix);
    // Does zero-size array work in obj-C?
    _bboxIx = ix + sizeof(SimpleGroup) - sizeof(vector_ushort4);
    _ix = _bboxIx + sizeof(vector_ushort4) * nItems;
    group->nItems = nItems;
    group->itemsIx = _ix;
    _count = nItems;
}

- (void)endGroup {
    if (_count != 0) {
        NSLog(@"Not enough items encoded in group.");
    }
    /*
    for (int i = 0; i < _freeSpace / 4; i++) {
        NSLog(@"%04x: %08x", i * 4, ((uint *)_buf)[i]);
    }
     */
}

- (void)circle:(vector_float2)center radius:(float)radius {
    vector_float4 bbox = simd_make_float4(center.x - radius,
                                          center.y - radius,
                                          center.x + radius,
                                          center.y + radius);
    PietCircle *circle = &[self allocItem:bbox]->circle;
    circle->itemType = PIET_ITEM_CIRCLE;
}

// The color argument is actually ABGR, which is the native format.
// Maybe rename?
- (void)line:(vector_float2)start to:(vector_float2)end width:(float) width color:(uint) rgba {
    float half = 0.5 * width;
    vector_float4 bbox = simd_make_float4(MIN(start.x, end.x) - half,
                                          MIN(start.y, end.y) - half,
                                          MAX(start.x, end.x) + half,
                                          MAX(start.y, end.y) + half);
    PietStrokeLine *line = &[self allocItem:bbox]->line;
    line->itemType = PIET_ITEM_LINE;
    line->rgbaColor = rgba;
    line->width = width; // should this be half?
    line->start = start;
    line->end = end;
}

// This isn't dealing with subpaths and has a crude allocation strategy.
- (void)addPt:(vector_float2)xy {
    uint ix = [self alloc:sizeof(vector_float2)];
    vector_float2 *dst = (vector_float2 *)(_buf + ix);
    *dst = xy;
    if (_pointCount == 0) {
        _bbox = simd_make_float4(xy, xy);
    } else {
        _bbox = simd_make_float4(
                                 MIN(_bbox.x, xy.x),
                                 MIN(_bbox.y, xy.y),
                                 MAX(_bbox.z, xy.x),
                                 MAX(_bbox.w, xy.y)
        );
    }
    _pointCount++;
}

- (void)fill:(uint)rgba {
    PietFill *fill = &[self allocItem:_bbox]->fill;
    fill->itemType = PIET_ITEM_FILL;
    fill->rgbaColor = rgba;
    fill->nPoints = _pointCount;
    // This is a hack, needs to be fixed if we have real allocation.
    fill->pointsIx = _freeSpace - _pointCount * sizeof(vector_float2);
    _pointCount = 0;
}

- (PietItem *)allocItem:(vector_float4)bbox {
    if (_count == 0) {
        NSLog(@"encoder group count overflow");
        return nil;
    }
    _count -= 1;
    vector_short4 *bboxPtr = (vector_short4 *)(_buf + _bboxIx);
    bboxPtr->x = MAX(bbox.x, 0.0);
    bboxPtr->y = MAX(bbox.y, 0.0);
    bboxPtr->z = bbox.z;
    bboxPtr->w = bbox.w;
    _bboxIx += sizeof(vector_short4);
    PietItem *item = (PietItem *)(_buf + _ix);
    _ix += sizeof(PietItem);
    return item;
}

@end



================================================
FILE: TestApp/ViewController.h
================================================
//  Copyright 2019 The xi-editor authors.

#import <Cocoa/Cocoa.h>

@import MetalKit;

@interface ViewController : NSViewController

@end



================================================
FILE: TestApp/ViewController.m
================================================
//  Copyright 2019 The xi-editor authors.

#import "ViewController.h"
#import "PietRenderer.h"

@implementation ViewController
{
    MTKView *_view;
    PietRenderer *_renderer;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    _view = (MTKView *)self.view;
    _view.device = MTLCreateSystemDefaultDevice();
    
    if(!_view.device)
    {
        NSLog(@"Metal is not supported on this device");
        return;
    }
    
    _renderer = [[PietRenderer alloc] initWithMetalKitView:_view];

    [_renderer mtkView:_view drawableSizeWillChange:_view.drawableSize];

    _view.delegate = _renderer;
}


- (void)setRepresentedObject:(id)representedObject {
    [super setRepresentedObject:representedObject];

    // Update the view, if already loaded.
}


@end


================================================
FILE: TestApp/main.m
================================================
//  Copyright 2019 The xi-editor authors.

#import <Cocoa/Cocoa.h>

#import "piet_metal.h"

int main(int argc, const char * argv[]) {
    return NSApplicationMain(argc, argv);
}


================================================
FILE: TestApp/piet_metal.entitlements
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.app-sandbox</key>
    <true/>
    <key>com.apple.security.files.user-selected.read-only</key>
    <true/>
</dict>
</plist>


================================================
FILE: include/piet_metal.h
================================================
#include <stdint.h>

void init_test_scene(uint8_t *buf, ssize_t buf_size);


================================================
FILE: piet-gpu-derive/Cargo.toml
================================================
[package]
name = "piet-gpu-derive"
version = "0.0.0"
authors = ["Raph Levien <raph.levien@gmail.com>"]
description = "Proc macro derives for piet-gpu."
license = "MIT/Apache-2.0"
edition = "2018"
keywords = ["graphics", "2d"]
categories = ["rendering::graphics-api"]

[lib]
proc-macro = true

[dependencies]
syn = {version = "1.0.5", features = ["extra-traits", "full"]}
quote = "1.0.2"
proc-macro2 = "1.0.4"


================================================
FILE: piet-gpu-derive/src/lib.rs
================================================
//! TODO: explain in detail how this works.
//!
//! A few notes that will be helpful. Structs are encoded differently depending
//! on whether they appear as a variant in an enum; if so, the tag is included.
//! This allows the alignment of the struct to take the tag into account.

extern crate proc_macro;
#[macro_use]
extern crate quote;

use std::collections::HashSet;
use std::fmt::Write;
use std::ops::Deref;

use proc_macro::TokenStream;
use syn::parse_macro_input;
use syn::{
    Expr, ExprLit, Fields, FieldsNamed, FieldsUnnamed, GenericArgument, ItemEnum, ItemStruct,
    Lit, PathArguments, TypeArray, TypePath,
};

/// The target shader language. We can't make this a public type because of Rust rules.
#[derive(Copy, Clone, PartialEq)]
enum TargetLang {
    Hlsl,
    Msl,
}

/// A scalar that can be represented in a packed data structure.
#[derive(Clone, Copy, PartialEq)]
enum GpuScalar {
    I8,
    I16,
    I32,
    F32,
    U8,
    U16,
    U32,
    // TODO: Add F16
}

/// An algebraic datatype.
#[derive(Clone)]
enum GpuType {
    Scalar(GpuScalar),
    Vector(GpuScalar, usize),
    /// Used mostly for the body of enum variants.
    InlineStruct(String),
    Ref(Box<GpuType>),
}

struct GpuEnum {
    name: String,
    variants: Vec<(String, Vec<GpuType>)>,
}

enum GpuTypeDef {
    Struct(String, Vec<(String, GpuType)>),
    Enum(GpuEnum),
}

struct GpuModule {
    #[allow(unused)]
    name: String,
    /// Set of item names that are used as enum variants.
    enum_variants: HashSet<String>,
    defs: Vec<GpuTypeDef>,
}

impl TargetLang {
    /// The typed function argument for "buf"
    fn buf_arg(self) -> &'static str {
        match self {
            TargetLang::Hlsl => "ByteAddressBuffer buf",
            TargetLang::Msl => "const device char *buf",
        }
    }

    /// An expression for loading a number of uints.
    fn load_expr(self, offset: usize, size: usize) -> String {
        let tail = if offset == 0 {
            "".into()
        } else {
            format!(" + {}", offset)
        };
        let size_str = vector_size_str(size);
        match self {
            TargetLang::Hlsl => format!("buf.Load{}(ref{})", size_str, tail),
            TargetLang::Msl => {
                let packed = if size == 1 { "" } else { "packed_" };
                format!(
                    "*(device const {}uint{}*)(buf + ref{})",
                    packed, size_str, tail
                )
            }
        }
    }
}

impl GpuScalar {
    /// The unpacked type of the scalar value.
    fn unpacked_type(self, target: TargetLang) -> GpuScalar {
        match target {
            TargetLang::Hlsl => match self {
                GpuScalar::I8 | GpuScalar::I16 => GpuScalar::I32,
                GpuScalar::U8 | GpuScalar::U16 => GpuScalar::U32,
                _ => self,
            },
            _ => self,
        }
    }

    fn typename(self, target: TargetLang) -> &'static str {
        if target == TargetLang::Hlsl && self.size() < 4 {
            panic!(
                "Internal logic error: trying to determine HLSL typename for {} byte value",
                self.size()
            );
        }
        match self {
            GpuScalar::F32 => "float",
            GpuScalar::I8 => "char",
            GpuScalar::I16 => "short",
            GpuScalar::I32 => "int",
            GpuScalar::U8 => "uchar",
            GpuScalar::U16 => "ushort",
            GpuScalar::U32 => "uint",
        }
    }

    fn size(self) -> usize {
        match self {
            GpuScalar::F32 | GpuScalar::I32 | GpuScalar::U32 => 4,
            GpuScalar::I8 | GpuScalar::U8 => 1,
            GpuScalar::I16 | GpuScalar::U16 => 2,
        }
    }

    /// Convert an expression with type "uint" into the given scalar.
    fn cvt(self, inner: &str, target: TargetLang) -> String {
        self.cvt_vec(inner, 1, target)
    }

    /// Convert a uint vector into the given vector
    fn cvt_vec(self, inner: &str, size: usize, target: TargetLang) -> String {
        let size = vector_size_str(size);
        match (target, self) {
            (TargetLang::Hlsl, GpuScalar::F32) => format!("asfloat({})", inner),
            (TargetLang::Hlsl, GpuScalar::I32) => format!("asint({})", inner),
            (TargetLang::Msl, GpuScalar::F32) => format!("as_type<float{}>({})", size, inner),
            (TargetLang::Msl, GpuScalar::I32) => format!("as_type<int{}>({})", size, inner),
            // TODO: need to be smarter about signed int conversion
            _ => inner.into(),
        }
    }

    fn from_syn(ty: &syn::Type) -> Option<Self> {
        ty_as_single_ident(ty).and_then(|ident| match ident.as_str() {
            "f32" => Some(GpuScalar::F32),
            "i8" => Some(GpuScalar::I8),
            "i16" => Some(GpuScalar::I16),
            "i32" => Some(GpuScalar::I32),
            "u8" => Some(GpuScalar::U8),
            "u16" => Some(GpuScalar::U16),
            "u32" => Some(GpuScalar::U32),
            _ => None,
        })
    }
}

impl std::fmt::Display for GpuScalar {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            GpuScalar::F32 => write!(f, "F32"),
            GpuScalar::I8 => write!(f, "I8"),
            GpuScalar::I16 => write!(f, "I16"),
            GpuScalar::I32 => write!(f, "I32"),
            GpuScalar::U8 => write!(f, "U8"),
            GpuScalar::U16 => write!(f, "U16"),
            GpuScalar::U32 => write!(f, "U32"),
        }
    }
}

/// If `c = 0`, return `"var_name`, else `"var_name + c"`
fn simplified_add(var_name: &str, c: usize) -> String {
    if c == 0 {
        String::from(var_name)
    } else {
        format!("{} + {}", var_name, c)
    }
}

/// Suffix to add to scalar type to make it into a vector.
///
/// For size of 1, returns empty string, though "usize1" is usually valid.
/// This is so we have one name for the same type, and also so the suffix
/// can be used for `ByteAddressBuf.Load` method names.
fn vector_size_str(size: usize) -> &'static str {
    match size {
        1 => "",
        2 => "2",
        3 => "3",
        4 => "4",
        _ => panic!("illegal vector size {}", size)
    }
}

/// Return number of `uints` required to store `num_bytes` bytes.
fn size_in_uints(num_bytes: usize) -> usize {
    // a `uint` has a size of 4 bytes, (size_in_bytes + 4 - 1) / 4
    (num_bytes + 3) / 4
}

// TODO: this only generates unsigned extractors, we will need signed as well.
fn generate_value_extractor(size_in_bits: u32) -> String {
    if size_in_bits > 31 {
        panic!("nonsensical to generate an extractor for a value with bit size greater than 31");
    }
    let mut extractor: String = String::new();

    let mask_width: usize = 2_usize.pow(size_in_bits) - 1;

    write!(
        extractor,
        "inline uint extract_{}bit_value(uint bit_shift, uint package) {{\n",
        size_in_bits
    )
    .unwrap();
    write!(extractor, "    uint mask = {};\n", mask_width).unwrap();
    write!(
        extractor,
        "{}",
        "    uint result = (package >> bit_shift) & mask;\n\n    return result;\n}\n\n"
    )
    .unwrap();

    extractor
}

/// A `PackedField` stores `StoredField`s
#[derive(Clone)]
struct StoredField {
    name: String,
    ty: GpuType,
    /// The offset of the field within the packed field, in bits.
    offset: usize,
}

/// A `PackedStruct` has `PackedField`s
#[derive(Clone)]
struct PackedField {
    name: String,
    /// The type of the package, as stored packed.
    ty: Option<GpuType>,
    stored_fields: Vec<StoredField>,
    size: usize,
}

/// Possible results of the `pack` method on a `PackedField`.
#[derive(PartialEq)]
enum PackResult {
    SuccessAndOpen,
    SuccessAndClosed,
    FailAndClosed,
}

#[derive(Clone)]
struct PackedStruct {
    name: String,
    packed_fields: Vec<PackedField>,
    is_enum_variant: bool,
}

struct SpecifiedStruct {
    name: String,
    fields: Vec<(String, GpuType)>,
    packed_form: PackedStruct,
}

impl StoredField {
    fn generate_unpacker(
        &self,
        packed_struct_name: &str,
        packed_field_name: &str,
        target: TargetLang,
    ) -> String {
        let mut unpacker = String::new();

        // A hack to get the base struct name
        let stripped_name = &packed_struct_name[0..packed_struct_name.len() - 6];
        if self.ty.is_small() {
            match self.ty {
                GpuType::Scalar(scalar) => {
                    let size_in_bits = 8 * scalar.size();
                    let hlsl_typename: String = match scalar {
                        GpuScalar::F32 | GpuScalar::I32 | GpuScalar::U32 => {
                            panic!("unexpected unpacking of 32 bit value!")
                        }
                        _ => String::from(scalar.unpacked_type(target).typename(target)),
                    };

                    write!(
                        unpacker,
                        "inline uint {}_unpack_{}(uint {}) {{\n    {} result;\n\n",
                        stripped_name, self.name, packed_field_name, hlsl_typename,
                    )
                    .unwrap();

                    write!(
                        unpacker,
                        "    result = extract_{}bit_value({}, {});\n",
                        size_in_bits, self.offset, packed_field_name
                    )
                    .unwrap();
                }
                GpuType::Vector(scalar, unpacked_size) => {
                    let scalar_size_in_bits = 8 * scalar.size();
                    let unpacked_typename = self.ty.unpacked_typename(target);

                    let size_in_uints = size_in_uints(&scalar.size() * unpacked_size);
                    write!(
                        unpacker,
                        "inline {} {}_unpack_{}(uint{} {}) {{\n    {} result;\n\n",
                        unpacked_typename,
                        stripped_name,
                        self.name,
                        size_in_uints,
                        packed_field_name,
                        unpacked_typename,
                    )
                    .unwrap();

                    for i in 0..unpacked_size {
                        let subscript = if size_in_uints == 1 {
                            "".into()
                        } else {
                            format!("[{}]", (i * scalar_size_in_bits) / 32)
                        };
                        let extracted = scalar.cvt(
                            &format!(
                                "extract_{}bit_value({}, {}{})",
                                scalar_size_in_bits,
                                self.offset + (i * scalar_size_in_bits) % 32,
                                packed_field_name,
                                subscript
                            ),
                            target,
                        );
                        write!(unpacker, "    result[{}] = {};\n", i, extracted).unwrap();
                    }
                }
                _ => panic!(
                    "only expected small types, got: {}",
                    self.ty.unpacked_typename(target)
                ),
            }

            write!(unpacker, "{}", "    return result;\n").unwrap();
            write!(unpacker, "{}", "}\n\n").unwrap();
        }

        unpacker
    }
}

impl PackedField {
    fn new() -> PackedField {
        PackedField {
            name: String::new(),
            ty: None,
            size: 0,
            stored_fields: vec![],
        }
    }

    fn pack(
        &mut self,
        module: &GpuModule,
        field_type: &GpuType,
        field_name: &str,
    ) -> Result<PackResult, String> {
        if !self.is_closed() {
            let field_size = field_type.size(module);

            if field_size + self.size > 4 {
                if self.is_empty() {
                    self.stored_fields.push(StoredField {
                        name: field_name.into(),
                        ty: field_type.clone(),
                        offset: 0,
                    });
                    self.close(module).unwrap();
                    Ok(PackResult::SuccessAndClosed)
                } else {
                    self.close(module).unwrap();
                    Ok(PackResult::FailAndClosed)
                }
            } else {
                self.stored_fields.push(StoredField {
                    name: String::from(field_name),
                    ty: field_type.clone(),
                    offset: self.size * 8,
                });
                self.size += field_size;
                Ok(PackResult::SuccessAndOpen)
            }
        } else {
            Err("cannot extend closed package".into())
        }
    }

    fn is_empty(&self) -> bool {
        self.stored_fields.is_empty()
    }

    fn is_closed(&self) -> bool {
        self.ty.is_some()
    }

    /// True when the packed and unpacked types differ.
    fn is_packed(&self, struct_result: bool) -> bool {
        if self.stored_fields.len() != 1 {
            return true;
        }
        match self.stored_fields[0].ty {
            GpuType::Scalar(scalar) => scalar.size() < 4,
            GpuType::Vector(scalar, _) => scalar.size() < 4,
            GpuType::InlineStruct(_) => struct_result,
            _ => false,
        }
    }

    fn close(&mut self, module: &GpuModule) -> Result<(), String> {
        if !self.is_closed() {
            if self.is_empty() {
                Err("cannot close empty package".into())
            } else {
                let stored_field_names = self
                    .stored_fields
                    .iter()
                    .map(|pf| pf.name.clone())
                    .collect::<Vec<String>>();
                self.name = stored_field_names.join("_");

                if self.is_packed(false) {
                    let summed_size = self.stored_fields.iter().map(|pf| pf.ty.size(module)).sum();
                    let size_in_uints = size_in_uints(summed_size);
                    if size_in_uints == 1 {
                        self.ty = Some(GpuType::Scalar(GpuScalar::U32));
                    } else {
                        self.ty = Some(GpuType::Vector(GpuScalar::U32, size_in_uints));
                    }
                } else {
                    self.ty = Some(self.stored_fields[0].ty.clone());
                }
                Ok(())
            }
        } else {
            Err("cannot close closed package".into())
        }
    }

    fn generate_reader(
        &self,
        current_offset: usize,
        target: TargetLang,
    ) -> Result<String, String> {
        if let Some(ty) = &self.ty {
            let type_name = ty.unpacked_typename(target);
            let packed_field_name = &self.name;

            match ty {
                GpuType::Scalar(scalar) => {
                    let load_expr = target.load_expr(current_offset, 1);
                    let cvt_exp = scalar.cvt(&load_expr, target);
                    Ok(format!(
                        "    {} {} = {};\n",
                        type_name, packed_field_name, cvt_exp,
                    ))
                }
                GpuType::Vector(scalar, size) => {
                    let size_in_uints = size_in_uints(scalar.size() * size);
                    let load_expr = target.load_expr(current_offset, size_in_uints);
                    let cvt_exp = scalar.cvt_vec(&load_expr, *size, target);
                    Ok(format!(
                        "    {}{} {} = {};\n",
                        scalar.typename(target),
                        size,
                        packed_field_name,
                        cvt_exp,
                    ))
                }
                GpuType::InlineStruct(isn) => Ok(format!(
                    "    {}Packed {} = {}_read(buf, {});\n",
                    isn,
                    packed_field_name,
                    isn,
                    simplified_add("ref", current_offset)
                )),
                GpuType::Ref(inner) => {
                    if let GpuType::InlineStruct(isn) = inner.deref() {
                        Ok(format!(
                            "    {}Ref {} = {};\n",
                            isn,
                            packed_field_name,
                            target.load_expr(current_offset, 1),
                        ))
                    } else {
                        Ok(format!(
                            "    uint {} = {};\n",
                            packed_field_name,
                            target.load_expr(current_offset, 1),
                        ))
                    }
                }
            }
        } else {
            Err("cannot generate field reader from an open packed field".into())
        }
    }

    fn generate_accessor(
        &self,
        packed_struct_name: &str,
        ref_type: &str,
        reader: &str,
        target: TargetLang,
    ) -> Result<String, String> {
        if let Some(ty) = &self.ty {
            let mut field_accessor = String::new();

            match ty {
                GpuType::InlineStruct(name) => {
                    write!(
                        field_accessor,
                        "inline {}Packed {}_{}({}, {} ref) {{\n",
                        name,
                        packed_struct_name,
                        self.name,
                        target.buf_arg(),
                        ref_type,
                    )
                    .unwrap();
                }
                _ => {
                    write!(
                        field_accessor,
                        "inline {} {}_{}({}, {} ref) {{\n",
                        ty.unpacked_typename(target),
                        packed_struct_name,
                        self.name,
                        target.buf_arg(),
                        ref_type,
                    )
                    .unwrap();
                }
            }
            write!(field_accessor, "{}", reader).unwrap();
            write!(field_accessor, "    return {};\n}}\n\n", self.name).unwrap();

            Ok(field_accessor)
        } else {
            Err("cannot generate field accessor from open packed field".into())
        }
    }

    fn generate_unpackers(&self, packed_struct_name: &str, target: TargetLang) -> String {
        let mut unpackers = String::new();

        for sf in &self.stored_fields {
            write!(
                unpackers,
                "{}",
                sf.generate_unpacker(packed_struct_name, &self.name, target)
            )
            .unwrap();
        }

        unpackers
    }

    fn size(&self, module: &GpuModule) -> Result<usize, String> {
        if let Some(ty) = &self.ty {
            Ok(ty.size(module))
        } else {
            Err("cannot calculate size of open packed field".into())
        }
    }
}

impl PackedStruct {
    fn new(module: &GpuModule, name: &str, fields: &Vec<(String, GpuType)>) -> PackedStruct {
        let mut packed_fields: Vec<PackedField> = Vec::new();

        let mut current_packed_field = PackedField::new();
        for (field_name, ty) in fields {
            match current_packed_field.pack(module, &ty, &field_name).unwrap() {
                PackResult::SuccessAndClosed => {
                    packed_fields.push(current_packed_field);
                    current_packed_field = PackedField::new();
                }
                PackResult::FailAndClosed => {
                    packed_fields.push(current_packed_field);
                    current_packed_field = PackedField::new();
                    let res = current_packed_field.pack(module, &ty, &field_name).unwrap();
                    if res == PackResult::SuccessAndClosed {
                        packed_fields.push(current_packed_field);
                        current_packed_field = PackedField::new();
                    }
                }
                _ => {}
            }
        }

        if !current_packed_field.is_closed() {
            if !current_packed_field.is_empty() {
                current_packed_field.close(module).unwrap();
                packed_fields.push(current_packed_field);
            }
        }

        PackedStruct {
            name: format!("{}Packed", name),
            packed_fields,
            is_enum_variant: module.enum_variants.contains(name),
        }
    }

    fn generate_functions(&self, module: &GpuModule, target: TargetLang) -> String {
        let mut r = String::new();
        let mut field_accessors: Vec<String> = Vec::new();
        let mut unpackers: Vec<String> = Vec::new();

        // This is something of a hack to strip the "Packed" off the struct name
        let stripped_name = &self.name[0..self.name.len() - 6];
        let ref_type = format!("{}Ref", stripped_name);

        write!(
            r,
            "inline {} {}_read({}, {} ref) {{\n",
            self.name,
            stripped_name,
            target.buf_arg(),
            ref_type,
        )
        .unwrap();
        write!(r, "    {} result;\n\n", self.name).unwrap();

        let mut current_offset: usize = 0;
        if self.is_enum_variant {
            // account for tag
            current_offset = 4;
        }

        for packed_field in &self.packed_fields {
            let reader: String = packed_field
                .generate_reader(current_offset, target)
                .unwrap();
            let field_accessor: String = packed_field
                .generate_accessor(stripped_name, &ref_type, &reader, target)
                .unwrap();

            field_accessors.push(field_accessor);
            if packed_field.is_packed(false) {
                unpackers.push(packed_field.generate_unpackers(&self.name, target));
            }

            write!(r, "{}", reader).unwrap();
            write!(
                r,
                "    result.{} = {};\n\n",
                packed_field.name, packed_field.name
            )
            .unwrap();

            current_offset += packed_field.size(module).unwrap();
        }

        write!(r, "    return result;\n}}\n\n",).unwrap();

        for field_accessor in field_accessors {
            write!(r, "{}", field_accessor).unwrap();
        }

        for unpacker in unpackers {
            write!(r, "{}", unpacker).unwrap();
        }

        r
    }

    fn generate_structure_def(&self, target: TargetLang) -> String {
        let mut r = String::new();

        // The packed struct definition (is missing variable sized arrays)
        write!(r, "struct {} {{\n", self.name).unwrap();
        if self.is_enum_variant {
            write!(r, "    uint tag;\n").unwrap();
        }

        for packed_field in self.packed_fields.iter() {
            match packed_field.ty.as_ref().unwrap() {
                GpuType::InlineStruct(name) => {
                    // a packed struct will only store the packed version of any structs
                    write!(r, "    {}Packed {};\n", name, packed_field.name)
                }
                _ => write!(
                    r,
                    "    {} {};\n",
                    packed_field
                        .ty
                        .as_ref()
                        .expect(&format!("packed field {} has no type", packed_field.name))
                        .unpacked_typename(target),
                    packed_field.name
                ),
            }
            .unwrap()
        }
        write!(r, "{}", "};\n\n").unwrap();

        r
    }

    fn to_shader(&self, module: &GpuModule, target: TargetLang) -> String {
        let mut r = String::new();

        write!(r, "{}", self.generate_structure_def(target)).unwrap();
        write!(r, "{}", self.generate_functions(module, target)).unwrap();

        r
    }
}

impl SpecifiedStruct {
    fn new(module: &GpuModule, name: &str, fields: Vec<(String, GpuType)>) -> SpecifiedStruct {
        let packed_form = PackedStruct::new(module, name, &fields);

        SpecifiedStruct {
            name: name.to_string(),
            fields,
            packed_form,
        }
    }

    fn generate_structure_def(&self, target: TargetLang) -> String {
        let mut r = String::new();

        // The unpacked struct definition (is missing variable sized arrays)
        write!(r, "struct {} {{\n", self.name).unwrap();

        for (field_name, field_type) in self.fields.iter() {
            write!(
                r,
                "    {} {};\n",
                field_type.unpacked_typename(target),
                field_name
            )
            .unwrap()
        }
        write!(r, "{}", "};\n\n").unwrap();

        r
    }

    fn generate_unpacker(&self) -> String {
        let mut r = String::new();

        write!(
            r,
            "inline {} {}_unpack({} packed_form) {{\n",
            self.name, self.name, self.packed_form.name,
        )
        .unwrap();

        write!(r, "    {} result;\n\n", self.name).unwrap();
        for (field_name, field_type) in self.fields.iter() {
            let packed_field = self
                .packed_form
                .packed_fields
                .iter()
                .find(|&pf| {
                    pf.stored_fields
                        .iter()
                        .find(|&sf| sf.name == field_name.as_str())
                        .is_some()
                })
                .expect(&format!(
                    "no packed field stores {} in {}Packed",
                    field_name, self.name
                ));
            if packed_field.is_packed(true) {
                match field_type {
                    GpuType::InlineStruct(name) => {
                        write!(
                            r,
                            "    result.{} = {}_unpack(packed_form.{});\n",
                            field_name, name, packed_field.name
                        )
                        .unwrap();
                    }
                    _ => {
                        write!(
                            r,
                            "    result.{} = {}_unpack_{}(packed_form.{});\n",
                            field_name, self.name, field_name, packed_field.name
                        )
                        .unwrap();
                    }
                }
            } else {
                write!(
                    r,
                    "    result.{} = packed_form.{};\n",
                    field_name, packed_field.name
                )
                .unwrap();
            }
        }
        write!(r, "{}", "\n    return result;\n}\n\n").unwrap();
        r
    }

    fn to_shader(&self, target: TargetLang) -> String {
        let mut r = String::new();

        write!(r, "{}", self.generate_structure_def(target)).unwrap();
        write!(r, "{}", self.generate_unpacker()).unwrap();

        r
    }
}

impl GpuType {
    // The type name for the *unpacked* version of the type.
    fn unpacked_typename(&self, target: TargetLang) -> String {
        match self {
            GpuType::Scalar(scalar) => scalar.unpacked_type(target).typename(target).into(),
            GpuType::Vector(scalar, size) => match scalar {
                GpuScalar::F32 | GpuScalar::I32 | GpuScalar::U32 => {
                    format!("{}{}", scalar.unpacked_type(target).typename(target), size)
                }
                _ => {
                    if *size == 1 {
                        "uint".into()
                    } else {
                        format!("uint{}", size)
                    }
                }
            },
            GpuType::InlineStruct(name) => name.to_string(),
            // TODO: probably want to have more friendly names for simple struct refs.
            GpuType::Ref(inner) => {
                if let GpuType::InlineStruct(name) = inner.deref() {
                    format!("{}Ref", name)
                } else {
                    "uint".into()
                }
            }
        }
    }

    fn size(&self, module: &GpuModule) -> usize {
        match self {
            GpuType::Scalar(scalar) => scalar.size(),
            GpuType::Vector(scalar, size) => scalar.size() * size,
            GpuType::InlineStruct(name) => module.resolve_by_name(&name).unwrap().size(module),
            GpuType::Ref(_name) => 4,
        }
    }

    fn alignment(&self, module: &GpuModule) -> usize {
        // TODO: there are alignment problems with vectors of 3
        match self {
            GpuType::Scalar(scalar) => scalar.size(),
            GpuType::Vector(scalar, size) => scalar.size() * size,
            GpuType::InlineStruct(name) => module.resolve_by_name(&name).unwrap().alignment(module),
            GpuType::Ref(_name) => 4,
        }
    }

    /// Report whether type is a scalar or simple vector
    fn is_small(&self) -> bool {
        match self {
            GpuType::Scalar(_) => true,
            GpuType::Vector(_, _) => true,
            GpuType::InlineStruct(_) => false,
            GpuType::Ref(_) => true,
        }
    }

    fn from_syn(ty: &syn::Type) -> Result<Self, String> {
        //println!("gputype {:#?}", ty);
        if let Some(scalar) = GpuScalar::from_syn(ty) {
            return Ok(GpuType::Scalar(scalar));
        }
        if let Some(name) = ty_as_single_ident(ty) {
            // Note: we're not doing any validation here.
            return Ok(GpuType::InlineStruct(name));
        }
        match ty {
            syn::Type::Path(TypePath {
                path: syn::Path { segments, .. },
                ..
            }) => {
                if segments.len() == 1 {
                    let seg = &segments[0];
                    if seg.ident == "Ref" {
                        if let PathArguments::AngleBracketed(args) = &seg.arguments {
                            if args.args.len() == 1 {
                                if let GenericArgument::Type(inner) = &args.args[0] {
                                    let inner_ty = GpuType::from_syn(inner)?;
                                    return Ok(GpuType::Ref(Box::new(inner_ty)));
                                }
                            }
                        }
                    }
                }
                Err("unknown path case".into())
            }
            syn::Type::Array(TypeArray { elem, len, .. }) => {
                if let Some(elem) = GpuScalar::from_syn(&elem) {
                    if let Some(len) = expr_int_lit(len) {
                        // maybe sanity-check length here
                        Ok(GpuType::Vector(elem, len))
                    } else {
                        Err("can't deal with variable length scalar arrays".into())
                    }
                } else {
                    Err("can't deal with non-scalar arrays".into())
                }
            }
            _ => Err("unknown type".into()),
        }
    }
}

impl GpuTypeDef {
    fn from_syn(item: &syn::Item) -> Result<Self, String> {
        match item {
            syn::Item::Struct(ItemStruct {
                ident,
                fields: Fields::Named(FieldsNamed { named, .. }),
                ..
            }) => {
                let mut fields = Vec::new();
                for field in named {
                    let field_ty = GpuType::from_syn(&field.ty)?;
                    let field_name = field.ident.as_ref().ok_or("need name".to_string())?;
                    fields.push((field_name.to_string(), field_ty));
                }
                Ok(GpuTypeDef::Struct(ident.to_string(), fields))
            }
            syn::Item::Enum(ItemEnum {
                ident, variants, ..
            }) => {
                let mut v = Vec::new();
                for variant in variants {
                    let vname = variant.ident.to_string();
                    let mut fields = Vec::new();
                    if let Fields::Unnamed(FieldsUnnamed { unnamed, .. }) = &variant.fields {
                        for field in unnamed {
                            fields.push(GpuType::from_syn(&field.ty)?);
                        }
                    }
                    v.push((vname, fields));
                }
                let en = GpuEnum {
                    name: ident.to_string(),
                    variants: v,
                };
                Ok(GpuTypeDef::Enum(en))
            }
            _ => {
                eprintln!("{:#?}", item);
                Err("unknown item".into())
            }
        }
    }

    fn name(&self) -> &str {
        match self {
            GpuTypeDef::Struct(name, _) => &name,
            GpuTypeDef::Enum(en) => &en.name,
        }
    }

    fn collect_refs(&self, enum_variants: &mut HashSet<String>) {
        if let GpuTypeDef::Enum(en) = self {
            for variant in &en.variants {
                if let Some(GpuType::InlineStruct(name)) = variant.1.first() {
                    enum_variants.insert(name.clone());
                }
            }
        }
    }

    /// Size of the body of the definition.
    fn size(&self, module: &GpuModule) -> usize {
        match self {
            GpuTypeDef::Struct(name, fields) => {
                let mut offset = 0;
                if module.enum_variants.contains(name) {
                    offset += 4;
                }
                for (_name, field) in fields {
                    offset += align_padding(offset, field.alignment(module));
                    offset += field.size(module);
                }
                offset
            }
            GpuTypeDef::Enum(en) => {
                let mut max_offset = 4;
                for (_name, fields) in &en.variants {
                    let mut offset = 4;
                    for field in fields {
                        if let GpuType::InlineStruct(_) = field {
                            if offset == 4 {
                                offset = 0;
                            }
                        }
                        // Alignment needs work :/
                        //offset += align_padding(offset, field.alignment(module));
                        offset += field.size(module);
                    }
                    max_offset = max_offset.max(offset);
                }
                max_offset
            }
        }
    }

    /// Alignment of the body of the definition.
    fn alignment(&self, module: &GpuModule) -> usize {
        match self {
            GpuTypeDef::Struct(name, fields) => {
                let mut alignment = 1;
                if module.enum_variants.contains(name) {
                    alignment = 4;
                }
                for (_name, field) in fields {
                    alignment = alignment.max(field.alignment(module));
                }
                alignment
            }
            GpuTypeDef::Enum(_en) => unimplemented!(),
        }
    }

    // TODO: implement this in new scheme
    /*
    fn to_metal_load_enum(&self, enum_name: &str, module: &GpuModule, r: &mut String) {
        match self {
            GpuTypeDef::Struct(name, fields) => {
                write!(
                    r,
                    "{}Packed {}_load(const thread {} &s) {{\n",
                    name, name, enum_name
                )
                .unwrap();
                write!(r, "    {}Packed r;\n", name).unwrap();
                write!(r, "    r.tag = s.tag;\n").unwrap();
                let mut offset = 4;
                for (fieldname, ty) in fields {
                    offset += align_padding(offset, ty.alignment(module));
                    let mty = ty.metal_typename();
                    // maybe better to load from `body` array rather than pointer foo
                    write!(
                        r,
                        "    r.{} = *((const thread {} *)((const thread char *)&s + {}));\n",
                        fieldname, mty, offset
                    )
                    .unwrap();
                    offset += ty.size(module);
                }
                write!(r, "    return r;\n").unwrap();
                write!(r, "}}\n").unwrap();
            }
            _ => panic!("internal inconsistency"),
        }
    }
    */

    // TODO: implement writers
    /*
    fn to_metal_wr(&self, _module: &GpuModule) -> String {
        let mut r = String::new();
        match self {
            GpuTypeDef::Struct(name, _fields) => {
                // Write of packed structure
                write!(
                    r,
                    "void {}_write(device char *buf, {}Ref ref, {}Packed s) {{\n",
                    name, name, name
                )
                .unwrap();
                write!(r, "    *((device {}Packed *)(buf + ref)) = s;\n", name).unwrap();
                write!(r, "}}\n").unwrap();
            }
            // We don't write individual enum structs, we only write their variants.
            GpuTypeDef::Enum(en) => {
                if en.variants.iter().any(|(_name, fields)| fields.is_empty()) {
                    write!(
                        r,
                        "void {}_write_tag(device char *buf, CmdRef ref, uint tag) {{\n",
                        en.name
                    )
                    .unwrap();
                    write!(r, "    ((device {} *)(buf + ref))->tag = tag;\n", en.name).unwrap();
                    write!(r, "}}\n").unwrap();
                }
            }
        }
        r
    }
    */

    fn to_shader(&self, module: &GpuModule, target: TargetLang) -> String {
        let mut r = String::new();

        match self {
            GpuTypeDef::Struct(name, fields) => {
                let structure = SpecifiedStruct::new(module, name, fields.clone());
                write!(r, "{}", structure.packed_form.to_shader(module, target)).unwrap();
                write!(r, "{}", structure.to_shader(target)).unwrap();
            }
            GpuTypeDef::Enum(en) => {
                let rn = format!("{}Ref", en.name);

                write!(r, "struct {} {{\n", en.name).unwrap();
                write!(r, "    uint tag;\n").unwrap();

                let size = self.size(module);
                // TODO: this sometimes predicts incorrect number of u32s needed to store body (differences with metal alignment)
                let body_size = ((size + 3) >> 2) - 1;

                write!(r, "    uint body[{}];\n", body_size).unwrap();
                write!(r, "}};\n").unwrap();
                write!(
                    r,
                    "inline uint {}_tag({}, {} ref) {{\n",
                    en.name,
                    target.buf_arg(),
                    rn
                )
                .unwrap();

                write!(
                    r,
                    "    uint result = {};\n    return result;\n",
                    target.load_expr(0, 1)
                )
                .unwrap();
                write!(r, "}}\n\n").unwrap();

                if target == TargetLang::Hlsl {
                    let quotient_in_u32x4 = size / (4 * GpuScalar::U32.size());
                    let remainder_in_u32s = (size / 4) % 4;
                    write!(r, "inline void {}_copy(ByteAddressBuffer src, uint src_ref, RWByteAddressBuffer dst, uint dst_ref) {{\n", en.name).unwrap();
                    for i in 0..quotient_in_u32x4 {
                        write!(
                            r,
                            "    uint4 group{} = src.Load4({});\n",
                            i,
                            simplified_add("src_ref", i * 4 * 4)
                        )
                        .unwrap();
                        write!(
                            r,
                            "    dst.Store4({}, group{});\n",
                            simplified_add("dst_ref", i * 4 * 4),
                            i,
                        )
                        .unwrap();
                    }
                    if remainder_in_u32s > 0 {
                        let tail = vector_size_str(remainder_in_u32s);
                        write!(
                            r,
                            "\n    uint{} group{} = src.Load{}({});\n",
                            tail,
                            quotient_in_u32x4,
                            tail,
                            simplified_add("src_ref", quotient_in_u32x4 * 4 * 4)
                        )
                        .unwrap();
                        write!(
                            r,
                            "    dst.Store{}({}, group{});\n",
                            tail,
                            simplified_add("dst_ref", quotient_in_u32x4 * 4 * 4),
                            quotient_in_u32x4
                        )
                        .unwrap();
                    }
                    write!(r, "{}", "}\n\n").unwrap();
                }
            }
        }
        r
    }
}

impl GpuModule {
    fn from_syn(module: &syn::ItemMod) -> Result<Self, String> {
        let name = module.ident.to_string();
        let mut defs = Vec::new();
        let mut enum_variants = HashSet::new();
        if let Some((_brace, items)) = &module.content {
            for item in items {
                let def = GpuTypeDef::from_syn(item)?;
                def.collect_refs(&mut enum_variants);
                defs.push(def);
            }
        }
        Ok(GpuModule {
            name,
            enum_variants,
            defs,
        })
    }

    fn resolve_by_name(&self, name: &str) -> Result<&GpuTypeDef, String> {
        for def in &self.defs {
            if def.name() == name {
                return Ok(&def);
            }
        }
        Err(format!("could not find {} in module", name))
    }

    fn to_shader(&self, target: TargetLang) -> String {
        let mut r = String::new();

        write!(&mut r, "{}", generate_value_extractor(8)).unwrap();
        write!(&mut r, "{}", generate_value_extractor(16)).unwrap();

        for def in &self.defs {
            match def {
                GpuTypeDef::Struct(name, _) => {
                    write!(&mut r, "typedef uint {}Ref;\n", name).unwrap();
                }
                GpuTypeDef::Enum(_) => {
                    write!(&mut r, "typedef uint {}Ref;\n", def.name()).unwrap();
                }
            }
        }

        write!(&mut r, "\n").unwrap();
        for def in &self.defs {
            r.push_str(&def.to_shader(self, target));
        }

        for def in &self.defs {
            let name = def.name();
            if !(self.enum_variants.contains(name)) {
                write!(
                    r,
                    "#define {}_SIZE {}\n",
                    to_snake_case(name).to_uppercase(),
                    def.size(self)
                )
                .unwrap();
            }
            if let GpuTypeDef::Enum(en) = def {
                let mut tag: usize = 0;
                for (name, _fields) in &en.variants {
                    write!(r, "#define {}_{} {}\n", en.name, name, tag).unwrap();
                    tag += 1;
                }
            }
        }
        r
    }
}

fn ty_as_single_ident(ty: &syn::Type) -> Option<String> {
    if let syn::Type::Path(TypePath {
        path: syn::Path { segments, .. },
        ..
    }) = ty
    {
        if segments.len() == 1 {
            let seg = &segments[0];
            if seg.arguments == PathArguments::None {
                return Some(seg.ident.to_string());
            }
        }
    }
    None
}

fn expr_int_lit(e: &Expr) -> Option<usize> {
    if let Expr::Lit(ExprLit {
        lit: Lit::Int(lit_int),
        ..
    }) = e
    {
        lit_int.base10_parse().ok()
    } else {
        None
    }
}

fn align_padding(offset: usize, alignment: usize) -> usize {
    offset.wrapping_neg() & (alignment - 1)
}

/* TODO: make derive macros
#[proc_macro_derive(PietMetal)]
pub fn derive_piet_metal(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as syn::DeriveInput);
    derive_proc_metal_impl(input)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

fn derive_proc_metal_impl(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> {
    println!("input: {:#?}", input);
    match &input.data {
        Data::Struct { .. } => {
            println!("it's a struct!");
        }
        _ => (),
    }
    let s = "this is a string";
    let expanded = quote! {
        fn foo() {
            println!("this was generated by proc macro: {}", #s);
        }
    };
    Ok(expanded)
}
*/

#[proc_macro]
pub fn piet_gpu(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as syn::ItemMod);
    //println!("input: {:#?}", input);
    let module = GpuModule::from_syn(&input).unwrap();
    let gen_gpu_fn = format_ident!("gen_gpu_{}", input.ident);
    let hlsl_result = module.to_shader(TargetLang::Hlsl);
    let msl_result = module.to_shader(TargetLang::Msl);
    let expanded = quote! {
        fn #gen_gpu_fn(lang: &str) -> String {
            match lang {
                "HLSL" => #hlsl_result.into(),
                "MSL" => #msl_result.into(),
                _ => panic!("unkonwn shader lang {}", lang),
            }
        }
    };
    expanded.into()
}

fn to_snake_case(mut str: &str) -> String {
    let mut words = vec![];
    // Preserve leading underscores
    str = str.trim_start_matches(|c: char| {
        if c == '_' {
            words.push(String::new());
            true
        } else {
            false
        }
    });
    for s in str.split('_') {
        let mut last_upper = false;
        let mut buf = String::new();
        if s.is_empty() {
            continue;
        }
        for ch in s.chars() {
            if !buf.is_empty() && buf != "'" && ch.is_uppercase() && !last_upper {
                words.push(buf);
                buf = String::new();
            }
            last_upper = ch.is_uppercase();
            buf.extend(ch.to_lowercase());
        }
        words.push(buf);
    }
    words.join("_")
}


================================================
FILE: piet-metal.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 50;
	objects = {

/* Begin PBXBuildFile section */
		AE20EE122277739F00207011 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = AE20EE112277739F00207011 /* AppDelegate.m */; };
		AE20EE152277739F00207011 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AE20EE142277739F00207011 /* ViewController.m */; };
		AE20EE17227773A000207011 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AE20EE16227773A000207011 /* Assets.xcassets */; };
		AE20EE1A227773A000207011 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AE20EE18227773A000207011 /* Main.storyboard */; };
		AE20EE1D227773A000207011 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = AE20EE1C227773A000207011 /* main.m */; };
		AE95B97A227CBE5A0038195A /* piet_metal.h in Headers */ = {isa = PBXBuildFile; fileRef = AE95B979227CBE590038195A /* piet_metal.h */; };
		AE95B981227CC0020038195A /* libpiet_metal.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AE95B975227CBDCD0038195A /* libpiet_metal.a */; };
		AE99FF612277800100937601 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = AE99FF5E2277800100937601 /* README.md */; };
		AE99FF652277809B00937601 /* PietRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = AE99FF642277809B00937601 /* PietRenderer.m */; };
		AE99FF692277CE7200937601 /* PietRender.metal in Sources */ = {isa = PBXBuildFile; fileRef = AE99FF682277CE7200937601 /* PietRender.metal */; };
		AEA81C9C227A698E0051DAD5 /* SceneEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = AEA81C9B227A698E0051DAD5 /* SceneEncoder.m */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
		AE95B97F227CBFEA0038195A /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = AE20EE052277739F00207011 /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = AE95B974227CBDCD0038195A;
			remoteInfo = piet_metal;
		};
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
		AE20EE0D2277739F00207011 /* piet-metal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "piet-metal.app"; sourceTree = BUILT_PRODUCTS_DIR; };
		AE20EE102277739F00207011 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
		AE20EE112277739F00207011 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
		AE20EE132277739F00207011 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
		AE20EE142277739F00207011 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
		AE20EE16227773A000207011 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
		AE20EE19227773A000207011 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
		AE20EE1B227773A000207011 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		AE20EE1C227773A000207011 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
		AE20EE1E227773A000207011 /* piet_metal.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = piet_metal.entitlements; sourceTree = "<group>"; };
		AE340C0923C546BF00F9D470 /* GenTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GenTypes.h; sourceTree = "<group>"; };
		AE95B96F227CBC1B0038195A /* libpiet_metal.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpiet_metal.a; path = target/debug/libpiet_metal.a; sourceTree = "<group>"; };
		AE95B975227CBDCD0038195A /* libpiet_metal.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libpiet_metal.a; sourceTree = BUILT_PRODUCTS_DIR; };
		AE95B979227CBE590038195A /* piet_metal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = piet_metal.h; path = include/piet_metal.h; sourceTree = "<group>"; };
		AE99FF5E2277800100937601 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
		AE99FF642277809B00937601 /* PietRenderer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PietRenderer.m; sourceTree = "<group>"; };
		AE99FF66227780B000937601 /* PietRenderer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PietRenderer.h; sourceTree = "<group>"; };
		AE99FF672277CDF200937601 /* PietShaderTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PietShaderTypes.h; sourceTree = "<group>"; };
		AE99FF682277CE7200937601 /* PietRender.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = PietRender.metal; sourceTree = "<group>"; };
		AEA81C9B227A698E0051DAD5 /* SceneEncoder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneEncoder.m; sourceTree = "<group>"; };
		AEA81C9D227A69B50051DAD5 /* SceneEncoder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneEncoder.h; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
		AE20EE0A2277739F00207011 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				AE95B981227CC0020038195A /* libpiet_metal.a in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		AE20EE042277739F00207011 = {
			isa = PBXGroup;
			children = (
				AE95B979227CBE590038195A /* piet_metal.h */,
				AE99FF5E2277800100937601 /* README.md */,
				AE20EE0F2277739F00207011 /* TestApp */,
				AE20EE0E2277739F00207011 /* Products */,
				AE95B96E227CBC1A0038195A /* Frameworks */,
			);
			sourceTree = "<group>";
		};
		AE20EE0E2277739F00207011 /* Products */ = {
			isa = PBXGroup;
			children = (
				AE20EE0D2277739F00207011 /* piet-metal.app */,
				AE95B975227CBDCD0038195A /* libpiet_metal.a */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		AE20EE0F2277739F00207011 /* TestApp */ = {
			isa = PBXGroup;
			children = (
				AE20EE102277739F00207011 /* AppDelegate.h */,
				AE20EE112277739F00207011 /* AppDelegate.m */,
				AE20EE16227773A000207011 /* Assets.xcassets */,
				AE20EE1B227773A000207011 /* Info.plist */,
				AE20EE1C227773A000207011 /* main.m */,
				AE20EE18227773A000207011 /* Main.storyboard */,
				AE20EE1E227773A000207011 /* piet_metal.entitlements */,
				AE99FF682277CE7200937601 /* PietRender.metal */,
				AE99FF66227780B000937601 /* PietRenderer.h */,
				AE99FF642277809B00937601 /* PietRenderer.m */,
				AE99FF672277CDF200937601 /* PietShaderTypes.h */,
				AEA81C9D227A69B50051DAD5 /* SceneEncoder.h */,
				AEA81C9B227A698E0051DAD5 /* SceneEncoder.m */,
				AE20EE132277739F00207011 /* ViewController.h */,
				AE20EE142277739F00207011 /* ViewController.m */,
				AE340C0923C546BF00F9D470 /* GenTypes.h */,
			);
			path = TestApp;
			sourceTree = "<group>";
		};
		AE95B96E227CBC1A0038195A /* Frameworks */ = {
			isa = PBXGroup;
			children = (
				AE95B96F227CBC1B0038195A /* libpiet_metal.a */,
			);
			name = Frameworks;
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXHeadersBuildPhase section */
		AE95B971227CBDCD0038195A /* Headers */ = {
			isa = PBXHeadersBuildPhase;
			buildActionMask = 2147483647;
			files = (
				AE95B97A227CBE5A0038195A /* piet_metal.h in Headers */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXHeadersBuildPhase section */

/* Begin PBXNativeTarget section */
		AE20EE0C2277739F00207011 /* piet-metal */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = AE20EE21227773A000207011 /* Build configuration list for PBXNativeTarget "piet-metal" */;
			buildPhases = (
				AE20EE092277739F00207011 /* Sources */,
				AE20EE0A2277739F00207011 /* Frameworks */,
				AE20EE0B2277739F00207011 /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
				AE95B980227CBFEA0038195A /* PBXTargetDependency */,
			);
			name = "piet-metal";
			productName = "piet-metal";
			productReference = AE20EE0D2277739F00207011 /* piet-metal.app */;
			productType = "com.apple.product-type.application";
		};
		AE95B974227CBDCD0038195A /* piet_metal */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = AE95B976227CBDCD0038195A /* Build configuration list for PBXNativeTarget "piet_metal" */;
			buildPhases = (
				AE95B971227CBDCD0038195A /* Headers */,
				AE95B97B227CBE660038195A /* ShellScript */,
			);
			buildRules = (
			);
			dependencies = (
			);
			name = piet_metal;
			productName = piet_metal;
			productReference = AE95B975227CBDCD0038195A /* libpiet_metal.a */;
			productType = "com.apple.product-type.library.static";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		AE20EE052277739F00207011 /* Project object */ = {
			isa = PBXProject;
			attributes = {
				LastUpgradeCheck = 1020;
				ORGANIZATIONNAME = "Raph Levien";
				TargetAttributes = {
					AE20EE0C2277739F00207011 = {
						CreatedOnToolsVersion = 10.2.1;
					};
					AE95B974227CBDCD0038195A = {
						CreatedOnToolsVersion = 10.2.1;
					};
				};
			};
			buildConfigurationList = AE20EE082277739F00207011 /* Build configuration list for PBXProject "piet-metal" */;
			compatibilityVersion = "Xcode 9.3";
			developmentRegion = en;
			hasScannedForEncodings = 0;
			knownRegions = (
				en,
				Base,
			);
			mainGroup = AE20EE042277739F00207011;
			productRefGroup = AE20EE0E2277739F00207011 /* Products */;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				AE20EE0C2277739F00207011 /* piet-metal */,
				AE95B974227CBDCD0038195A /* piet_metal */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		AE20EE0B2277739F00207011 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				AE99FF612277800100937601 /* README.md in Resources */,
				AE20EE17227773A000207011 /* Assets.xcassets in Resources */,
				AE20EE1A227773A000207011 /* Main.storyboard in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
		AE95B97B227CBE660038195A /* ShellScript */ = {
			isa = PBXShellScriptBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			inputFileListPaths = (
			);
			inputPaths = (
			);
			outputFileListPaths = (
			);
			outputPaths = (
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "# When building from Xcode we want to ensure that `cargo` is in PATH.\n# as a convenience, add the default cargo install location\nexport PATH=\"$PATH:${HOME}/.cargo/bin\"\n\nset -e\n\nif [[ $CONFIGURATION = \"Debug\" ]]; then\nRUST_CONFIGURATION=\"debug\"\nRUST_CONFIGURATION_FLAG=\"\"\nelse\nRUST_CONFIGURATION=\"release\"\nRUST_CONFIGURATION_FLAG=\"--release\"\nfi\n\ncd \"${SRCROOT}\"\n\necho \"rust config ${RUST_CONFIGURATION}, action ${ACTION}\"\n\nif [[ ${ACTION:-build} = \"build\" ]]; then\ncargo build $RUST_CONFIGURATION_FLAG\ncp \"${SRCROOT}/target/${RUST_CONFIGURATION}/libpiet_metal.a\" \"${BUILT_PRODUCTS_DIR}/\"\n#cp \"${SRCROOT}/include/piet_metal.h\" \"${BUILT_PRODUCTS_DIR}/\"\nelif [[ $ACTION = \"clean\" ]]; then\necho \"cleaning\"\ncargo clean\nfi\n\nset +e\n";
		};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		AE20EE092277739F00207011 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				AE20EE152277739F00207011 /* ViewController.m in Sources */,
				AE20EE1D227773A000207011 /* main.m in Sources */,
				AE99FF652277809B00937601 /* PietRenderer.m in Sources */,
				AEA81C9C227A698E0051DAD5 /* SceneEncoder.m in Sources */,
				AE99FF692277CE7200937601 /* PietRender.metal in Sources */,
				AE20EE122277739F00207011 /* AppDelegate.m in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
		AE95B980227CBFEA0038195A /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			target = AE95B974227CBDCD0038195A /* piet_metal */;
			targetProxy = AE95B97F227CBFEA0038195A /* PBXContainerItemProxy */;
		};
/* End PBXTargetDependency section */

/* Begin PBXVariantGroup section */
		AE20EE18227773A000207011 /* Main.storyboard */ = {
			isa = PBXVariantGroup;
			children = (
				AE20EE19227773A000207011 /* Base */,
			);
			name = Main.storyboard;
			sourceTree = "<group>";
		};
/* End PBXVariantGroup section */

/* Begin XCBuildConfiguration section */
		AE20EE1F227773A000207011 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_ENABLE_OBJC_WEAK = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICI
Download .txt
gitextract_b08e_b5r/

├── .gitignore
├── Cargo.toml
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── TestApp/
│   ├── AppDelegate.h
│   ├── AppDelegate.m
│   ├── Assets.xcassets/
│   │   ├── AppIcon.appiconset/
│   │   │   └── Contents.json
│   │   └── Contents.json
│   ├── Base.lproj/
│   │   └── Main.storyboard
│   ├── GenTypes.h
│   ├── Info.plist
│   ├── PietRender.metal
│   ├── PietRenderer.h
│   ├── PietRenderer.m
│   ├── PietShaderTypes.h
│   ├── SceneEncoder.h
│   ├── SceneEncoder.m
│   ├── ViewController.h
│   ├── ViewController.m
│   ├── main.m
│   └── piet_metal.entitlements
├── include/
│   └── piet_metal.h
├── piet-gpu-derive/
│   ├── Cargo.toml
│   └── src/
│       └── lib.rs
├── piet-metal.xcodeproj/
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   ├── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       └── IDEWorkspaceChecks.plist
│   └── xcuserdata/
│       └── raph.xcuserdatad/
│           └── xcschemes/
│               └── xcschememanagement.plist
└── src/
    ├── flatten.rs
    ├── lib.rs
    └── main.rs
Download .txt
SYMBOL INDEX (206 symbols across 7 files)

FILE: TestApp/GenTypes.h
  function uint (line 7) | inline uint extract_8bit_value(uint bit_shift, uint package) {
  function uint (line 14) | inline uint extract_16bit_value(uint bit_shift, uint package) {
  type uint (line 21) | typedef uint SimpleGroupRef;
  type uint (line 22) | typedef uint PietCircleRef;
  type uint (line 23) | typedef uint PietStrokeLineRef;
  type uint (line 24) | typedef uint PietFillRef;
  type uint (line 25) | typedef uint PietStrokePolyLineRef;
  type uint (line 26) | typedef uint PietItemRef;
  type SimpleGroupPacked (line 28) | struct SimpleGroupPacked {
  function SimpleGroupPacked (line 34) | inline SimpleGroupPacked SimpleGroup_read(const device char *buf, Simple...
  function uint (line 49) | inline uint SimpleGroup_n_items(const device char *buf, SimpleGroupRef r...
  function PietItemRef (line 54) | inline PietItemRef SimpleGroup_items_ix(const device char *buf, SimpleGr...
  function uint2 (line 59) | inline uint2 SimpleGroup_bbox(const device char *buf, SimpleGroupRef ref) {
  function uint4 (line 64) | inline uint4 SimpleGroup_unpack_bbox(uint2 bbox) {
  type SimpleGroup (line 74) | struct SimpleGroup {
  function SimpleGroup (line 80) | inline SimpleGroup SimpleGroup_unpack(SimpleGroupPacked packed_form) {
  type PietCirclePacked (line 90) | struct PietCirclePacked {
  function PietCirclePacked (line 94) | inline PietCirclePacked PietCircle_read(const device char *buf, PietCirc...
  type PietCircle (line 100) | struct PietCircle {
  function PietCircle (line 103) | inline PietCircle PietCircle_unpack(PietCirclePacked packed_form) {
  type PietStrokeLinePacked (line 110) | struct PietStrokeLinePacked {
  function PietStrokeLinePacked (line 119) | inline PietStrokeLinePacked PietStrokeLine_read(const device char *buf, ...
  function uint (line 140) | inline uint PietStrokeLine_flags(const device char *buf, PietStrokeLineR...
  function uint (line 145) | inline uint PietStrokeLine_rgba_color(const device char *buf, PietStroke...
  function PietStrokeLine_width (line 150) | inline float PietStrokeLine_width(const device char *buf, PietStrokeLine...
  function float2 (line 155) | inline float2 PietStrokeLine_start(const device char *buf, PietStrokeLin...
  function float2 (line 160) | inline float2 PietStrokeLine_end(const device char *buf, PietStrokeLineR...
  type PietStrokeLine (line 165) | struct PietStrokeLine {
  function PietStrokeLine (line 173) | inline PietStrokeLine PietStrokeLine_unpack(PietStrokeLinePacked packed_...
  type PietFillPacked (line 185) | struct PietFillPacked {
  function PietFillPacked (line 193) | inline PietFillPacked PietFill_read(const device char *buf, PietFillRef ...
  function uint (line 211) | inline uint PietFill_flags(const device char *buf, PietFillRef ref) {
  function uint (line 216) | inline uint PietFill_rgba_color(const device char *buf, PietFillRef ref) {
  function uint (line 221) | inline uint PietFill_n_points(const device char *buf, PietFillRef ref) {
  function uint (line 226) | inline uint PietFill_points_ix(const device char *buf, PietFillRef ref) {
  type PietFill (line 231) | struct PietFill {
  function PietFill (line 238) | inline PietFill PietFill_unpack(PietFillPacked packed_form) {
  type PietStrokePolyLinePacked (line 249) | struct PietStrokePolyLinePacked {
  function PietStrokePolyLinePacked (line 257) | inline PietStrokePolyLinePacked PietStrokePolyLine_read(const device cha...
  function uint (line 275) | inline uint PietStrokePolyLine_rgba_color(const device char *buf, PietSt...
  function PietStrokePolyLine_width (line 280) | inline float PietStrokePolyLine_width(const device char *buf, PietStroke...
  function uint (line 285) | inline uint PietStrokePolyLine_n_points(const device char *buf, PietStro...
  function uint (line 290) | inline uint PietStrokePolyLine_points_ix(const device char *buf, PietStr...
  type PietStrokePolyLine (line 295) | struct PietStrokePolyLine {
  function PietStrokePolyLine (line 302) | inline PietStrokePolyLine PietStrokePolyLine_unpack(PietStrokePolyLinePa...
  type PietItem (line 313) | struct PietItem {
  function uint (line 317) | inline uint PietItem_tag(const device char *buf, PietItemRef ref) {
  type uint (line 332) | typedef uint CmdCircleRef;
  type uint (line 333) | typedef uint CmdLineRef;
  type uint (line 334) | typedef uint CmdStrokeRef;
  type uint (line 335) | typedef uint CmdFillRef;
  type uint (line 336) | typedef uint CmdFillEdgeRef;
  type uint (line 337) | typedef uint CmdDrawFillRef;
  type uint (line 338) | typedef uint CmdSolidRef;
  type uint (line 339) | typedef uint CmdRef;
  type CmdCirclePacked (line 340) | struct CmdCirclePacked {
  function CmdCirclePacked (line 344) | CmdCirclePacked CmdCircle_read(const device char *buf, CmdCircleRef ref) {
  function ushort4 (line 347) | ushort4 CmdCircle_bbox(const device char *buf, CmdCircleRef ref) {
  type CmdLinePacked (line 350) | struct CmdLinePacked {
  function CmdLinePacked (line 355) | CmdLinePacked CmdLine_read(const device char *buf, CmdLineRef ref) {
  function float2 (line 358) | float2 CmdLine_start(const device char *buf, CmdLineRef ref) {
  function float2 (line 361) | float2 CmdLine_end(const device char *buf, CmdLineRef ref) {
  type CmdStrokePacked (line 364) | struct CmdStrokePacked {
  function CmdStrokePacked (line 369) | CmdStrokePacked CmdStroke_read(const device char *buf, CmdStrokeRef ref) {
  function CmdStroke_halfWidth (line 372) | float CmdStroke_halfWidth(const device char *buf, CmdStrokeRef ref) {
  function uint (line 375) | uint CmdStroke_rgba_color(const device char *buf, CmdStrokeRef ref) {
  type CmdFillPacked (line 378) | struct CmdFillPacked {
  function CmdFillPacked (line 383) | CmdFillPacked CmdFill_read(const device char *buf, CmdFillRef ref) {
  function float2 (line 386) | float2 CmdFill_start(const device char *buf, CmdFillRef ref) {
  function float2 (line 389) | float2 CmdFill_end(const device char *buf, CmdFillRef ref) {
  type CmdFillEdgePacked (line 392) | struct CmdFillEdgePacked {
  function CmdFillEdgePacked (line 397) | CmdFillEdgePacked CmdFillEdge_read(const device char *buf, CmdFillEdgeRe...
  function CmdFillEdge_sign (line 400) | int CmdFillEdge_sign(const device char *buf, CmdFillEdgeRef ref) {
  function CmdFillEdge_y (line 403) | float CmdFillEdge_y(const device char *buf, CmdFillEdgeRef ref) {
  type CmdDrawFillPacked (line 406) | struct CmdDrawFillPacked {
  function CmdDrawFillPacked (line 411) | CmdDrawFillPacked CmdDrawFill_read(const device char *buf, CmdDrawFillRe...
  function CmdDrawFill_backdrop (line 414) | int CmdDrawFill_backdrop(const device char *buf, CmdDrawFillRef ref) {
  function uint (line 417) | uint CmdDrawFill_rgba_color(const device char *buf, CmdDrawFillRef ref) {
  type CmdSolidPacked (line 420) | struct CmdSolidPacked {
  function CmdSolidPacked (line 424) | CmdSolidPacked CmdSolid_read(const device char *buf, CmdSolidRef ref) {
  function uint (line 427) | uint CmdSolid_rgba_color(const device char *buf, CmdSolidRef ref) {
  type Cmd (line 430) | struct Cmd {
  function Cmd (line 434) | Cmd Cmd_read(const device char *buf, CmdRef ref) {
  function uint (line 437) | uint Cmd_tag(const device char *buf, CmdRef ref) {
  function CmdCirclePacked (line 442) | CmdCirclePacked CmdCircle_load(const thread Cmd &s) {
  function CmdLinePacked (line 449) | CmdLinePacked CmdLine_load(const thread Cmd &s) {
  function CmdFillPacked (line 457) | CmdFillPacked CmdFill_load(const thread Cmd &s) {
  function CmdStrokePacked (line 465) | CmdStrokePacked CmdStroke_load(const thread Cmd &s) {
  function CmdFillEdgePacked (line 473) | CmdFillEdgePacked CmdFillEdge_load(const thread Cmd &s) {
  function CmdDrawFillPacked (line 481) | CmdDrawFillPacked CmdDrawFill_load(const thread Cmd &s) {
  function CmdSolidPacked (line 489) | CmdSolidPacked CmdSolid_load(const thread Cmd &s) {
  function CmdCircle_write (line 497) | void CmdCircle_write(device char *buf, CmdCircleRef ref, CmdCirclePacked...
  function CmdLine_write (line 500) | void CmdLine_write(device char *buf, CmdLineRef ref, CmdLinePacked s) {
  function CmdStroke_write (line 503) | void CmdStroke_write(device char *buf, CmdStrokeRef ref, CmdStrokePacked...
  function CmdFill_write (line 506) | void CmdFill_write(device char *buf, CmdFillRef ref, CmdFillPacked s) {
  function CmdFillEdge_write (line 509) | void CmdFillEdge_write(device char *buf, CmdFillEdgeRef ref, CmdFillEdge...
  function CmdDrawFill_write (line 512) | void CmdDrawFill_write(device char *buf, CmdDrawFillRef ref, CmdDrawFill...
  function CmdSolid_write (line 515) | void CmdSolid_write(device char *buf, CmdSolidRef ref, CmdSolidPacked s) {
  function Cmd_write_tag (line 518) | void Cmd_write_tag(device char *buf, CmdRef ref, uint tag) {

FILE: TestApp/PietShaderTypes.h
  type RenderVertex (line 3) | typedef struct
  type RenderVertexInputIndex (line 11) | typedef enum RenderVertexInputIndex

FILE: TestApp/SceneEncoder.h
  type SimpleGroup (line 21) | struct SimpleGroup {
  type PietCircle (line 28) | typedef struct PietCircle {
  type PietStrokeLine (line 33) | typedef struct PietStrokeLine {
  type PietFill (line 42) | typedef struct PietFill {
  type PietStrokePolyLine (line 50) | typedef struct PietStrokePolyLine {
  type PietItem (line 58) | typedef union PietItem {

FILE: piet-gpu-derive/src/lib.rs
  type TargetLang (line 24) | enum TargetLang {
    method buf_arg (line 72) | fn buf_arg(self) -> &'static str {
    method load_expr (line 80) | fn load_expr(self, offset: usize, size: usize) -> String {
  type GpuScalar (line 31) | enum GpuScalar {
    method unpacked_type (line 102) | fn unpacked_type(self, target: TargetLang) -> GpuScalar {
    method typename (line 113) | fn typename(self, target: TargetLang) -> &'static str {
    method size (line 131) | fn size(self) -> usize {
    method cvt (line 140) | fn cvt(self, inner: &str, target: TargetLang) -> String {
    method cvt_vec (line 145) | fn cvt_vec(self, inner: &str, size: usize, target: TargetLang) -> Stri...
    method from_syn (line 157) | fn from_syn(ty: &syn::Type) -> Option<Self> {
    method fmt (line 172) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  type GpuType (line 44) | enum GpuType {
    method unpacked_typename (line 837) | fn unpacked_typename(&self, target: TargetLang) -> String {
    method size (line 864) | fn size(&self, module: &GpuModule) -> usize {
    method alignment (line 873) | fn alignment(&self, module: &GpuModule) -> usize {
    method is_small (line 884) | fn is_small(&self) -> bool {
    method from_syn (line 893) | fn from_syn(ty: &syn::Type) -> Result<Self, String> {
  type GpuEnum (line 52) | struct GpuEnum {
  type GpuTypeDef (line 57) | enum GpuTypeDef {
    method from_syn (line 940) | fn from_syn(item: &syn::Item) -> Result<Self, String> {
    method name (line 982) | fn name(&self) -> &str {
    method collect_refs (line 989) | fn collect_refs(&self, enum_variants: &mut HashSet<String>) {
    method size (line 1000) | fn size(&self, module: &GpuModule) -> usize {
    method alignment (line 1035) | fn alignment(&self, module: &GpuModule) -> usize {
    method to_shader (line 1119) | fn to_shader(&self, module: &GpuModule, target: TargetLang) -> String {
  type GpuModule (line 62) | struct GpuModule {
    method from_syn (line 1206) | fn from_syn(module: &syn::ItemMod) -> Result<Self, String> {
    method resolve_by_name (line 1224) | fn resolve_by_name(&self, name: &str) -> Result<&GpuTypeDef, String> {
    method to_shader (line 1233) | fn to_shader(&self, target: TargetLang) -> String {
  function simplified_add (line 186) | fn simplified_add(var_name: &str, c: usize) -> String {
  function vector_size_str (line 199) | fn vector_size_str(size: usize) -> &'static str {
  function size_in_uints (line 210) | fn size_in_uints(num_bytes: usize) -> usize {
  function generate_value_extractor (line 216) | fn generate_value_extractor(size_in_bits: u32) -> String {
  type StoredField (line 243) | struct StoredField {
    method generate_unpacker (line 282) | fn generate_unpacker(
  type PackedField (line 252) | struct PackedField {
    method new (line 368) | fn new() -> PackedField {
    method pack (line 377) | fn pack(
    method is_empty (line 413) | fn is_empty(&self) -> bool {
    method is_closed (line 417) | fn is_closed(&self) -> bool {
    method is_packed (line 422) | fn is_packed(&self, struct_result: bool) -> bool {
    method close (line 434) | fn close(&mut self, module: &GpuModule) -> Result<(), String> {
    method generate_reader (line 464) | fn generate_reader(
    method generate_accessor (line 523) | fn generate_accessor(
    method generate_unpackers (line 568) | fn generate_unpackers(&self, packed_struct_name: &str, target: TargetL...
    method size (line 583) | fn size(&self, module: &GpuModule) -> Result<usize, String> {
  type PackResult (line 262) | enum PackResult {
  type PackedStruct (line 269) | struct PackedStruct {
    method new (line 593) | fn new(module: &GpuModule, name: &str, fields: &Vec<(String, GpuType)>...
    method generate_functions (line 630) | fn generate_functions(&self, module: &GpuModule, target: TargetLang) -...
    method generate_structure_def (line 693) | fn generate_structure_def(&self, target: TargetLang) -> String {
    method to_shader (line 726) | fn to_shader(&self, module: &GpuModule, target: TargetLang) -> String {
  type SpecifiedStruct (line 275) | struct SpecifiedStruct {
    method new (line 737) | fn new(module: &GpuModule, name: &str, fields: Vec<(String, GpuType)>)...
    method generate_structure_def (line 747) | fn generate_structure_def(&self, target: TargetLang) -> String {
    method generate_unpacker (line 767) | fn generate_unpacker(&self) -> String {
    method to_shader (line 825) | fn to_shader(&self, target: TargetLang) -> String {
  function ty_as_single_ident (line 1278) | fn ty_as_single_ident(ty: &syn::Type) -> Option<String> {
  function expr_int_lit (line 1294) | fn expr_int_lit(e: &Expr) -> Option<usize> {
  function align_padding (line 1306) | fn align_padding(offset: usize, alignment: usize) -> usize {
  function piet_gpu (line 1338) | pub fn piet_gpu(input: TokenStream) -> TokenStream {
  function to_snake_case (line 1357) | fn to_snake_case(mut str: &str) -> String {

FILE: src/flatten.rs
  function flatten_path (line 10) | pub fn flatten_path(path: &BezPath, tolerance: f64) -> Vec<Vec<Point>> {

FILE: src/lib.rs
  type SimpleGroup (line 17) | struct SimpleGroup {
  type ShortBbox (line 24) | struct ShortBbox([u16; 4]);
    method from_rect (line 89) | fn from_rect(rect: Rect) -> ShortBbox {
  type PietCircle (line 35) | struct PietCircle {
  type PietStrokeLine (line 41) | struct PietStrokeLine {
  type PietFill (line 52) | struct PietFill {
  type PietStrokePolyLine (line 62) | struct PietStrokePolyLine {
  type ItemType (line 72) | enum ItemType {
  type Encoder (line 79) | pub struct Encoder<'a> {
  function point_to_f32s (line 99) | fn point_to_f32s(point: Point) -> (f32, f32) {
  function new (line 104) | pub fn new(buf: &mut [u8]) -> Encoder {
  function alloc (line 114) | pub fn alloc(&mut self, size: usize) -> usize {
  function write_struct (line 122) | pub unsafe fn write_struct<T>(&mut self, ix: usize, s: &T) {
  function begin_group (line 132) | pub fn begin_group(&mut self, n_items: usize) {
  function end_group (line 146) | pub fn end_group(&mut self) {
  function add_item (line 151) | unsafe fn add_item<T>(&mut self, item: &T, bbox: ShortBbox) {
  function circle (line 167) | pub fn circle(&mut self, circle: &Circle) {
  function stroke_line (line 177) | pub fn stroke_line(&mut self, line: Line, width: f32, rgba: u32) {
  function fill (line 195) | pub fn fill(&mut self, points: &[Point], rgba: u32) {
  function polyline (line 209) | pub fn polyline(&mut self, points: &[Point], rgba: u32, width: f32) {
  function encode_points (line 224) | pub fn encode_points(&mut self, points: &[Point]) -> (usize, Rect) {
  function debug_print (line 243) | fn debug_print(&self) {
  function make_cardioid (line 257) | fn make_cardioid(encoder: &mut Encoder) {
  function make_path_test (line 273) | fn make_path_test(encoder: &mut Encoder) {
  function make_tiger (line 286) | fn make_tiger(encoder: &mut Encoder) {
  constant TOLERANCE (line 330) | const TOLERANCE: f64 = 0.1;
  function count_fill_items (line 332) | fn count_fill_items(bezpath: &BezPath) -> usize {
  function count_stroke_items (line 337) | fn count_stroke_items(bezpath: &BezPath) -> usize {
  function encode_path (line 342) | fn encode_path(encoder: &mut Encoder, bezpath: &BezPath, rgba: u32) {
  constant THIN_LINE (line 351) | const THIN_LINE: f32 = 0.7;
  function encode_path_stroke (line 353) | fn encode_path_stroke(encoder: &mut Encoder, bezpath: &BezPath, mut widt...
  function make_test_scene (line 369) | fn make_test_scene(encoder: &mut Encoder) {
  function parse_color (line 375) | fn parse_color(color: &str) -> u32 {
  function init_test_scene (line 388) | pub unsafe extern "C" fn init_test_scene(scene_buf: *mut u8, buf_size: u...

FILE: src/main.rs
  type SimpleGroup (line 5) | struct SimpleGroup {
  function main (line 95) | fn main() {
Condensed preview — 32 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (231K chars).
[
  {
    "path": ".gitignore",
    "chars": 20,
    "preview": "/target\n/build\n.idea"
  },
  {
    "path": "Cargo.toml",
    "chars": 500,
    "preview": "[package]\nname = \"piet-metal\"\nversion = \"0.0.0\"\nauthors = [\"Raph Levien <raph.levien@gmail.com>\"]\ndescription = \"A Metal"
  },
  {
    "path": "LICENSE-APACHE",
    "chars": 11358,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "LICENSE-MIT",
    "chars": 1055,
    "preview": "Copyright (c) 2019 Raph Levien\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this sof"
  },
  {
    "path": "README.md",
    "chars": 933,
    "preview": "# piet-metal\n\nThis repository is currently an experiment in using GPU compute to implement the piet 2D graphics API. In "
  },
  {
    "path": "TestApp/AppDelegate.h",
    "chars": 134,
    "preview": "//  Copyright 2019 The xi-editor authors.\n\n#import <Cocoa/Cocoa.h>\n\n@interface AppDelegate : NSObject <NSApplicationDele"
  },
  {
    "path": "TestApp/AppDelegate.m",
    "chars": 391,
    "preview": "//  Copyright 2019 The xi-editor authors.\n\n#import \"AppDelegate.h\"\n\n@interface AppDelegate ()\n\n@end\n\n@implementation App"
  },
  {
    "path": "TestApp/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 903,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"mac\",\n      \"size\" : \"16x16\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : "
  },
  {
    "path": "TestApp/Assets.xcassets/Contents.json",
    "chars": 62,
    "preview": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "TestApp/Base.lproj/Main.storyboard",
    "chars": 60683,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB\" version=\"3.0\" t"
  },
  {
    "path": "TestApp/GenTypes.h",
    "chars": 14972,
    "preview": "//  Copyright 2019 The xi-editor authors.\n\n// The contents of this file are autogenerated.\n\n// scene from new (merged) g"
  },
  {
    "path": "TestApp/Info.plist",
    "chars": 1040,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "TestApp/PietRender.metal",
    "chars": 26737,
    "preview": "//  Copyright 2019 The xi-editor authors.\n\n#include <metal_stdlib>\nusing namespace metal;\n\n#include \"PietShaderTypes.h\"\n"
  },
  {
    "path": "TestApp/PietRenderer.h",
    "chars": 194,
    "preview": "//  Copyright 2019 The xi-editor authors.\n\n@import MetalKit;\n\n@interface PietRenderer : NSObject<MTKViewDelegate>\n\n- (no"
  },
  {
    "path": "TestApp/PietRenderer.m",
    "chars": 8912,
    "preview": "//  Copyright 2019 The xi-editor authors.\n\n@import MetalKit;\n\n#import \"PietRenderer.h\"\n#import \"PietShaderTypes.h\"\n#incl"
  },
  {
    "path": "TestApp/PietShaderTypes.h",
    "chars": 888,
    "preview": "//  Copyright 2019 The xi-editor authors.\n\ntypedef struct\n{\n    // This is a clip space coordinate (-1 to 1).\n    vector"
  },
  {
    "path": "TestApp/SceneEncoder.h",
    "chars": 1603,
    "preview": "@interface SceneEncoder: NSObject\n\n- (nonnull instancetype)initWithBuffer:(nonnull id<MTLBuffer>)buffer;\n\n- (void)beginG"
  },
  {
    "path": "TestApp/SceneEncoder.m",
    "chars": 3906,
    "preview": "//  Copyright 2019 The xi-editor authors.\n\n@import MetalKit;\n\n#import \"PietShaderTypes.h\"\n#import \"SceneEncoder.h\"\n\n@imp"
  },
  {
    "path": "TestApp/ViewController.h",
    "chars": 139,
    "preview": "//  Copyright 2019 The xi-editor authors.\n\n#import <Cocoa/Cocoa.h>\n\n@import MetalKit;\n\n@interface ViewController : NSVie"
  },
  {
    "path": "TestApp/ViewController.m",
    "chars": 767,
    "preview": "//  Copyright 2019 The xi-editor authors.\n\n#import \"ViewController.h\"\n#import \"PietRenderer.h\"\n\n@implementation ViewCont"
  },
  {
    "path": "TestApp/main.m",
    "chars": 178,
    "preview": "//  Copyright 2019 The xi-editor authors.\n\n#import <Cocoa/Cocoa.h>\n\n#import \"piet_metal.h\"\n\nint main(int argc, const cha"
  },
  {
    "path": "TestApp/piet_metal.entitlements",
    "chars": 322,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "include/piet_metal.h",
    "chars": 75,
    "preview": "#include <stdint.h>\n\nvoid init_test_scene(uint8_t *buf, ssize_t buf_size);\n"
  },
  {
    "path": "piet-gpu-derive/Cargo.toml",
    "chars": 409,
    "preview": "[package]\nname = \"piet-gpu-derive\"\nversion = \"0.0.0\"\nauthors = [\"Raph Levien <raph.levien@gmail.com>\"]\ndescription = \"Pr"
  },
  {
    "path": "piet-gpu-derive/src/lib.rs",
    "chars": 46255,
    "preview": "//! TODO: explain in detail how this works.\n//!\n//! A few notes that will be helpful. Structs are encoded differently de"
  },
  {
    "path": "piet-metal.xcodeproj/project.pbxproj",
    "chars": 20235,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "piet-metal.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 155,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:piet-metal.xcod"
  },
  {
    "path": "piet-metal.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "chars": 238,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "piet-metal.xcodeproj/xcuserdata/raph.xcuserdatad/xcschemes/xcschememanagement.plist",
    "chars": 686,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "src/flatten.rs",
    "chars": 1651,
    "preview": "//  Copyright 2019 The xi-editor authors.\n\n//! Quick and dirty path flattening.\n\n// A proper path flattening algorithm b"
  },
  {
    "path": "src/lib.rs",
    "chars": 11864,
    "preview": "//  Copyright 2019 The xi-editor authors.\n\nuse std::mem;\nuse std::ptr::copy_nonoverlapping;\nuse std::str::FromStr;\n\nuse "
  },
  {
    "path": "src/main.rs",
    "chars": 2273,
    "preview": "#[macro_use]\nextern crate piet_gpu_derive;\n\n//#[derive(PietMetal)]\nstruct SimpleGroup {\n    n_items: u32,\n    items_ix: "
  }
]

About this extraction

This page contains the full source code of the linebender/piet-metal GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 32 files (214.4 KB), approximately 48.9k tokens, and a symbol index with 206 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!