diff --git a/README.md b/README.md index d8c14db5de..193f37831c 100644 --- a/README.md +++ b/README.md @@ -1,295 +1,59 @@ - - - Processing Logo -Processing is a flexible software sketchbook and a programming language designed for learning how to code. - -This repository contains the source code for the [Processing](https://processing.org/) project for people who want to help improve the code. - -## Announcing Processing 4.3.1 - -We’re excited to announce the release of Processing 4.3.1! This update brings tooling improvements and a friendlier experience for contributors. To learn more, read the [Processing 4.3.1 announcement](https://github.com/processing/processing4-carbon-aug-19/wiki/Announcing-Processing-4.3.1). - -Processing was initiated in 2001 by Ben Fry and Casey Reas, who lead the development and maintenance of the project until 2023. We are grateful for their vision and dedication to the project. Processing is also indebted to over two decades of contributions from the broader Processing community. - -> [!NOTE] -> Due to platform limitations, the GitHub Contributors page for this repository does not show the complete list of contributors. However, the [git commit history](https://github.com/processing/processing4/commits/main/) provides a full record of the project's contributions. For contributor graphs before November 13th, refer to [this page](https://github.com/benfry/processing4/graphs/contributors). A comprehensive [list of all contributors](#contributors) is also included below. To see all commits by a contributor, click on the [💻](https://github.com/processing/processing4/commits?author=benfry) emoji below their name. - -## Using Processing - -If you're interested in *using* Processing, head over to the [download page](https://processing.org/download), or read more about the project on the [Processing website](https://processing.org/). There are also several [tutorials](https://processing.org/tutorials) that provide a helpful introduction. They are complemented by hundreds of examples that are included with the software itself. - -## Getting Help -For assistance with your own sketches, projects, or code, please post your question on the Processing forum: https://discourse.processing.org/. Our community is full of experienced developers and knowledgeable users who are eager to help. Before you post, please take a moment to read the [guidelines on asking questions](https://discourse.processing.org/t/guidelines-asking-questions/2147) to make sure you get the best possible help. We’re incredibly grateful for the support and knowledge shared by everyone on the forum over the years. - -## Contributing to Processing -Processing is a labor of love, built over decades by people who believe in this community and genuinely enjoy contributing to it. If you want to fix a bug that’s been bothering you or give back to the project in other ways, you’re in the right place! We invite you to think of this repository as a community garden. We’re here to cultivate something beautiful together, and that takes kindness, dedication, and patience. For detailed guidelines on how to contribute, please see our [CONTRIBUTING.md](CONTRIBUTING.md). - -We understand that contributing to open source can be intimidating, but mistakes are part of learning—and we are all learners here. We do not assume knowledge or imply that somebody should already know any particular thing in order to contribute. Whether you’re a newcomer or an expert, your knowledge and contributions are valuable. Never hesitate to ask questions, open an issue, a pull request, or write a comment. We also encourage you to step in if you can: reply to issues, review pull requests, or help out in whatever way feels right for you. - -While we assume good intentions, and will give everyone a chance to learn, we have zero tolerance for repeated harassment, harmful behavior, or toxicity of any kind. Please read our [Code of Conduct](https://github.com/processing/processing4?tab=coc-ov-file) and join us in creating a safe and supportive environment through your words and actions. - -## Building Processing -Building Processing locally on your machine will let you troubleshoot and make sure your contributions work as intended before submitting them to this repository. It also gives you the flexibility to experiment and learn more about how Processing is structured. +# Vulkan in Processing -For a quick start: -1. Fork and clone the repository. -1. Open it in IntelliJ IDEA. -1. Install the required [Ant plugin](https://plugins.jetbrains.com/plugin/23025-ant). -1. Hit Run. -For more information and detailed instructions, follow our [How to Build Processing](build/README.md) guide. -## Contact Information -For technical support or troubleshooting with your project, please post on the [Processing Forum](https://discourse.processing.org/). +This project aims to add a new Vulkan-based renderer to the Processing framework (https://github.com/processing/processing4). -For bug reports or feature requests, please [create an issue](https://github.com/processing/processing4/issues). +Currently, it's part of a university dissertation project. However later I plan to release this as a library rather than just being a copy+paste of the whole of Processing. -For non-technical inquiries, here’s how to get in touch: +## How it works -- For press inquiries, general information about the Processing software, or other non-technical questions, contact [hello@processing.org](mailto:hello@processing.org). -- For anything related to the Processing Foundation or broader topics beyond the software, please reach out to [foundation@processingfoundation.org](mailto:foundation@processingfoundation.org). +![updated-design-1](https://github.com/user-attachments/assets/1ed4bbb1-88d4-4c30-8ce0-eec2ca128483) -## License & Copyright +(*Note: this diagram isn't fully up-to-date*) -- The **core library** is licensed under the GNU Lesser General Public License version 2.1 ([LGPL-2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html)). -- Everything else including the **PDE** is licensed under the GNU General Public License version 2 ([GPL-2.0](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html)). -- The **reference**, including the JavaDoc comments, is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License ([CC-BY-NC-SA-4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/)). +This framework works by keeping most of the PGraphicsOpenGL code intact and simply emulating OpenGL behaviour through a thin OpenGL-to-Vulkan translation layer. This layer also is specifically optimised to work with certain elements of Processing. This layer is bounded to the PGL abstraction layer. We use LWJGL to use the Vulkan API. -For complete licensing information about the Processing core library and software, see [LICENSE.md](LICENSE.md) +## Features +- New PV3D and PV2D renderers +- OpenGL-GLSL to Vulkan-GLSL shader converter +- Automatic multithreading, meaning sketches can utilise 100% of the CPU. -For licensing information about the Processing website see the [processing-website README](https://github.com/processing/processing-website/blob/main/README.md#licenses). -Copyright (c) 2015-now The Processing Foundation +## What works/doesn't work -## Contributors +### WORKING: +- Primitive 2D shapes (rect, ellipse, line, etc) +- Primitive 3D shapes (box, sphere, etc) +- Textures +- Depth buffer +- Immediate rendering mode -Add yourself to the contributors list [here](https://github.com/processing/processing4-carbon-aug-19/issues/839)! +### PARTIALLY WORKING: +- Retained rendering mode (works mostly, but still some bugs, particularly with the StaticParticlesRetained example sketch) +- Custom PShaders (uniform arrays and uniforms that add up to over 256 bytes do not work yet, also GL-to-VK shader converter is still imperfect and some complex shaders will fail to compile) +- PGL (highly recommend against its usage in Processing sketches) +- hint() function (not fully tested yet) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Ben Fry
Ben Fry

💻 🤔 🚇 🧑‍🏫 🚧 🖋 📢
Casey Reas
Casey Reas

💻 🤔 🚇 🧑‍🏫 🖋 📢
codeanticode
codeanticode

💻
Manindra Moharana
Manindra Moharana

💻
Jakub Valtar
Jakub Valtar

💻
A Samuel Pottinger
A Samuel Pottinger

💻
Gottfried Haider
Gottfried Haider

💻
Akarshit Wal
Akarshit Wal

💻
Peter Kalauskas
Peter Kalauskas

💻
Daniel Shiffman
Daniel Shiffman

💻
Joel Moniz
Joel Moniz

💻
Lonnen
Lonnen

💻
Florian Jenett
Florian Jenett

💻
Scott Murray
Scott Murray

💻
Federico Bond
Federico Bond

💻
pvrs12
pvrs12

💻
George Bateman
George Bateman

💻
Sean McKenna
Sean McKenna

💻
kfeuz
kfeuz

💻
David Wicks
David Wicks

💻
Wilm Thoben
Wilm Thoben

💻
Ana
Ana

💻
Amnon Owed
Amnon Owed

💻
Gal Sasson
Gal Sasson

💻
scollovati
scollovati

💻
Yong Joseph Bakos
Yong Joseph Bakos

💻
Kenichi Ito
Kenichi Ito

💻
Efratror
Efratror

💻
Alexis Engelke
Alexis Engelke

💻
tyfkda
tyfkda

💻
Simon Greenwold
Simon Greenwold

💻
Rune Skjoldborg Madsen
Rune Skjoldborg Madsen

💻
Leslie Watkins
Leslie Watkins

💻
Rostyslav Zatserkovnyi
Rostyslav Zatserkovnyi

💻
Dan
Dan

💻
Daniel Howe
Daniel Howe

💻
Josh Giesbrecht
Josh Giesbrecht

💻
liquidex
liquidex

💻
bgc
bgc

💻
Mohammad Umair
Mohammad Umair

💻
T Michail
T Michail

💻
ohommos
ohommos

💻
Jonathan Feinberg
Jonathan Feinberg

💻
David Fokkema
David Fokkema

💻
liquid
liquid

💻
Kisaru Liyanage
Kisaru Liyanage

💻
BouB
BouB

💻
atk
atk

💻
Xerxes Rånby
Xerxes Rånby

💻
Will Rabalais
Will Rabalais

💻
Utkarsh Tiwari
Utkarsh Tiwari

💻
Prince-Polka
Prince-Polka

💻
jamesjgrady
jamesjgrady

💻
Raphaël de Courville
Raphaël de Courville

💻
Satoshi Okita
Satoshi Okita

💻
Carlos Andrés Rocha
Carlos Andrés Rocha

💻
Vincent Vijn
Vincent Vijn

💻
dzaima
dzaima

💻
mingness
mingness

🚇
Dora Do
Dora Do

🚇
Stef Tervelde
Stef Tervelde

💻
allcontributors[bot]
allcontributors[bot]

💻
Dave
Dave

💻
TN8001
TN8001

💻
Sigmund Hansen
Sigmund Hansen

💻
Rodrigo Bonifácio
Rodrigo Bonifácio

💻
Aidan Pieper
Aidan Pieper

💻
Liam James
Liam James

💻
james gilles
james gilles

💻
Elie Zananiri
Elie Zananiri

💻
Cosimo Cecchi
Cosimo Cecchi

💻
Liam Middlebrook
Liam Middlebrook

💻
Martin Yrjölä
Martin Yrjölä

💻
Michał Urbański
Michał Urbański

💻
Paco
Paco

💻
Patrick Ryan
Patrick Ryan

💻
Paweł Goliński
Paweł Goliński

💻
Rupesh Kumar
Rupesh Kumar

💻
Suhaib Khan
Suhaib Khan

💻
Yves BLAKE
Yves BLAKE

💻
M. Ernestus
M. Ernestus

💻
Francis Li
Francis Li

💻
Parag Jain
Parag Jain

💻
roopa vasudevan
roopa vasudevan

💻
kiwistrongis
kiwistrongis

💻
Alessandro Ranellucci
Alessandro Ranellucci

💻
Alexandre B A Villares
Alexandre B A Villares

💻
Heracles
Heracles

💻
Arya Gupta
Arya Gupta

💻
Damien Quartz
Damien Quartz

💻
Shubham Rathore
Shubham Rathore

💻
Grigoriy Titaev
Grigoriy Titaev

💻
Guilherme Silveira
Guilherme Silveira

💻
Héctor López Carral
Héctor López Carral

💻
Jeremy Douglass
Jeremy Douglass

💻
Jett LaRue
Jett LaRue

💻
Jim
Jim

💻 🐛
Joan Perals
Joan Perals

💻
Josh Holinaty
Josh Holinaty

💻
Keito Takeda
Keito Takeda

💻
Victor Osório
Victor Osório

💻
Torben
Torben

💻
Tobias Pristupin
Tobias Pristupin

💻
Thomas Leplus
Thomas Leplus

💻
Arnoud van der Leer
Arnoud van der Leer

💻
Stanislas Marçais / Knupel
Stanislas Marçais / Knupel

💻
Sanchit Kapoor
Sanchit Kapoor

💻
Miles Fogle
Miles Fogle

💻
Miguel Valadas
Miguel Valadas

💻
Maximilien Tirard
Maximilien Tirard

💻
Matthew Russell
Matthew Russell

💻
dcuartielles
dcuartielles

💻
Jayson Haebich
Jayson Haebich

💻
jordirosa
jordirosa

💻
Justin Shrake
Justin Shrake

💻
Kevin
Kevin

💻
kgtkr
kgtkr

💻
Mark Luffel
Mark Luffel

💻
Никита Король
Никита Король

💻
raguenets
raguenets

💻
robog-two
robog-two

💻
teddywing
teddywing

💻
chikuwa
chikuwa

💻
ಠ_ಠ
ಠ_ಠ

💻
Abe Pazos
Abe Pazos

💻
Alex
Alex

💻
Alexander Hurst
Alexander Hurst

💻
Anıl
Anıl

💻
Barış
Barış

💻
Brian Sapozhnikov
Brian Sapozhnikov

💻
Carlos Mario Rodriguez Perdomo
Carlos Mario Rodriguez Perdomo

💻
CyberFlame
CyberFlame

💻
Dhruv Jawali
Dhruv Jawali

💻
FlorisVO
FlorisVO

💻
Frank Leon Rose
Frank Leon Rose

💻
Greg Borenstein
Greg Borenstein

💻
Guillermo Perez
Guillermo Perez

💻
Henning Kiel
Henning Kiel

💻
J David Eisenberg
J David Eisenberg

💻
Jordan Ephron
Jordan Ephron

💻
Jason Sigal
Jason Sigal

💻
Jordan Orelli
Jordan Orelli

💻
Kalle
Kalle

💻
Laureano López
Laureano López

💻
Lesley Wagner
Lesley Wagner

💻
Mark Slee
Mark Slee

💻
MARTIN LEOPOLD GROEDL
MARTIN LEOPOLD GROEDL

💻
Martin Prout
Martin Prout

💻
Mathias Herberts
Mathias Herberts

💻
Diya Solanki
Diya Solanki

🚇
Neil C Smith
Neil C Smith

🚇
kate hollenbach
kate hollenbach

💻 📦 🧑‍🏫 🐛
Rishabdev Tudu
Rishabdev Tudu

📖 💻
Pau
Pau

📖
Junology
Junology

💻
Jaap Meijers
Jaap Meijers

📖
Xin Xin
Xin Xin

📋 🤔
Benjamin Fox
Benjamin Fox

💻
e1dem
e1dem

💻
+### NOT WORKING / NOT IMPLEMENTED YET: +- Anti-aliasing +- PGraphics (rendering to an off-screen buffer) +- Keeping the previous framebuffer, i.e. drawing without calling background() +- Texture mipmapping +- Materials and lighting (the uniforms for the lighting shaders exceeds the 256 byte limit) +- Probably loads more I haven't noticed yet +- Resizing the window +- Some window GLFW stuff, such as keyboard input, mouse clicking and scrolling, window name and icon, etc. - - +### Notes: +- I recently got a new laptop and multithreading seems to cause slowdowns due to Vulkan commands being WAY faster than on my old laptop... I'll need to fix that. +- I'd like to expose Vulkan in sketches using PVK, similar to PGL. My hope is that it could help make learning Vulkan substantially easier than setting up and doing everything from scratch. +- There's some things in Processing that could work better if it used pure Vulkan rather than the GL-to-VK translation layer; PShapes and Vulkan's fixed command buffers, and improved retained mode to name a couple. +- Need to install external LWJGL JAR's to the core/library folder... I'll need to figure out how to make ANT install this automatically. +- PGraphicsOpenGL has modifications to use multiple buffers to avoid ThreadNodes writing to the same buffer at the same time. - +## Performance +TODO diff --git a/core/.classpath b/core/.classpath index 54476ed979..85f67d851e 100644 --- a/core/.classpath +++ b/core/.classpath @@ -1,13 +1,43 @@ - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/.gitignore b/core/.gitignore index 01625bcfdf..738a286121 100644 --- a/core/.gitignore +++ b/core/.gitignore @@ -1,9 +1,20 @@ bin bin-test -/library/gluegen-rt*.jar -/library/jogl-all*.jar + +/natives core-sources.jar -different/build \ No newline at end of file +different/build + +DepthTest.java +DirectPGL.java +HighQuantityBug.java +ImageTest.java +LoadsOfImagesPShape.java +LoadsOfImagesTest.java +PShapeTest.java +Sketch1.java +Sketch2.java +ThreadBug1.java \ No newline at end of file diff --git a/core/.settings/org.eclipse.jdt.core.prefs b/core/.settings/org.eclipse.jdt.core.prefs index 9c1f33157e..a29df58c20 100644 --- a/core/.settings/org.eclipse.jdt.core.prefs +++ b/core/.settings/org.eclipse.jdt.core.prefs @@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.compliance=17 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate @@ -99,8 +99,8 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=11 +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=17 org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 org.eclipse.jdt.core.formatter.align_type_members_on_columns=false @@ -114,7 +114,6 @@ org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_c org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=18 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 org.eclipse.jdt.core.formatter.alignment_for_assignment=0 -org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16 org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16 @@ -209,11 +208,9 @@ org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false org.eclipse.jdt.core.formatter.indentation.size=2 -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert @@ -241,7 +238,6 @@ org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default=insert org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert @@ -313,7 +309,6 @@ org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case=insert org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default=insert org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert @@ -464,7 +459,6 @@ org.eclipse.jdt.core.formatter.use_on_off_tags=false org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false -org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true diff --git a/core/library/gluegen-rt.jar b/core/library/gluegen-rt.jar new file mode 100644 index 0000000000..d92fbcc59c Binary files /dev/null and b/core/library/gluegen-rt.jar differ diff --git a/core/library/jogl-all.jar b/core/library/jogl-all.jar new file mode 100644 index 0000000000..e11abbf501 Binary files /dev/null and b/core/library/jogl-all.jar differ diff --git a/core/library/lwjgl-assimp-natives-linux.jar b/core/library/lwjgl-assimp-natives-linux.jar new file mode 100644 index 0000000000..c63e7aab43 Binary files /dev/null and b/core/library/lwjgl-assimp-natives-linux.jar differ diff --git a/core/library/lwjgl-assimp-natives-windows.jar b/core/library/lwjgl-assimp-natives-windows.jar new file mode 100644 index 0000000000..24dc79c97f Binary files /dev/null and b/core/library/lwjgl-assimp-natives-windows.jar differ diff --git a/core/library/lwjgl-assimp-sources.jar b/core/library/lwjgl-assimp-sources.jar new file mode 100644 index 0000000000..734617ed52 Binary files /dev/null and b/core/library/lwjgl-assimp-sources.jar differ diff --git a/core/library/lwjgl-assimp.jar b/core/library/lwjgl-assimp.jar new file mode 100644 index 0000000000..6838a4f628 Binary files /dev/null and b/core/library/lwjgl-assimp.jar differ diff --git a/core/library/lwjgl-glfw-natives-linux.jar b/core/library/lwjgl-glfw-natives-linux.jar new file mode 100644 index 0000000000..f61b10e0e2 Binary files /dev/null and b/core/library/lwjgl-glfw-natives-linux.jar differ diff --git a/core/library/lwjgl-glfw-natives-windows.jar b/core/library/lwjgl-glfw-natives-windows.jar new file mode 100644 index 0000000000..460854ce7f Binary files /dev/null and b/core/library/lwjgl-glfw-natives-windows.jar differ diff --git a/core/library/lwjgl-glfw-sources.jar b/core/library/lwjgl-glfw-sources.jar new file mode 100644 index 0000000000..4d02503927 Binary files /dev/null and b/core/library/lwjgl-glfw-sources.jar differ diff --git a/core/library/lwjgl-glfw.jar b/core/library/lwjgl-glfw.jar new file mode 100644 index 0000000000..bfd66f7350 Binary files /dev/null and b/core/library/lwjgl-glfw.jar differ diff --git a/core/library/lwjgl-natives-linux.jar b/core/library/lwjgl-natives-linux.jar new file mode 100644 index 0000000000..3cb446edcd Binary files /dev/null and b/core/library/lwjgl-natives-linux.jar differ diff --git a/core/library/lwjgl-natives-windows.jar b/core/library/lwjgl-natives-windows.jar new file mode 100644 index 0000000000..a5aa07f19d Binary files /dev/null and b/core/library/lwjgl-natives-windows.jar differ diff --git a/core/library/lwjgl-openal-natives-linux.jar b/core/library/lwjgl-openal-natives-linux.jar new file mode 100644 index 0000000000..dad6b41d28 Binary files /dev/null and b/core/library/lwjgl-openal-natives-linux.jar differ diff --git a/core/library/lwjgl-openal-natives-windows.jar b/core/library/lwjgl-openal-natives-windows.jar new file mode 100644 index 0000000000..aff5b02a0f Binary files /dev/null and b/core/library/lwjgl-openal-natives-windows.jar differ diff --git a/core/library/lwjgl-openal-sources.jar b/core/library/lwjgl-openal-sources.jar new file mode 100644 index 0000000000..b980a5e630 Binary files /dev/null and b/core/library/lwjgl-openal-sources.jar differ diff --git a/core/library/lwjgl-openal.jar b/core/library/lwjgl-openal.jar new file mode 100644 index 0000000000..3f5d1eb494 Binary files /dev/null and b/core/library/lwjgl-openal.jar differ diff --git a/core/library/lwjgl-shaderc-natives-linux.jar b/core/library/lwjgl-shaderc-natives-linux.jar new file mode 100644 index 0000000000..eda019f7ca Binary files /dev/null and b/core/library/lwjgl-shaderc-natives-linux.jar differ diff --git a/core/library/lwjgl-shaderc-natives-windows.jar b/core/library/lwjgl-shaderc-natives-windows.jar new file mode 100644 index 0000000000..574747224d Binary files /dev/null and b/core/library/lwjgl-shaderc-natives-windows.jar differ diff --git a/core/library/lwjgl-shaderc-sources.jar b/core/library/lwjgl-shaderc-sources.jar new file mode 100644 index 0000000000..fdff160b44 Binary files /dev/null and b/core/library/lwjgl-shaderc-sources.jar differ diff --git a/core/library/lwjgl-shaderc.jar b/core/library/lwjgl-shaderc.jar new file mode 100644 index 0000000000..9b67b586e5 Binary files /dev/null and b/core/library/lwjgl-shaderc.jar differ diff --git a/core/library/lwjgl-sources.jar b/core/library/lwjgl-sources.jar new file mode 100644 index 0000000000..813fe41eea Binary files /dev/null and b/core/library/lwjgl-sources.jar differ diff --git a/core/library/lwjgl-stb-natives-linux.jar b/core/library/lwjgl-stb-natives-linux.jar new file mode 100644 index 0000000000..cfa76f9ccc Binary files /dev/null and b/core/library/lwjgl-stb-natives-linux.jar differ diff --git a/core/library/lwjgl-stb-natives-windows.jar b/core/library/lwjgl-stb-natives-windows.jar new file mode 100644 index 0000000000..c70da6e544 Binary files /dev/null and b/core/library/lwjgl-stb-natives-windows.jar differ diff --git a/core/library/lwjgl-stb-sources.jar b/core/library/lwjgl-stb-sources.jar new file mode 100644 index 0000000000..adb6d13f5b Binary files /dev/null and b/core/library/lwjgl-stb-sources.jar differ diff --git a/core/library/lwjgl-stb.jar b/core/library/lwjgl-stb.jar new file mode 100644 index 0000000000..a506634b4d Binary files /dev/null and b/core/library/lwjgl-stb.jar differ diff --git a/core/library/lwjgl-vma-natives-linux.jar b/core/library/lwjgl-vma-natives-linux.jar new file mode 100644 index 0000000000..800733cd75 Binary files /dev/null and b/core/library/lwjgl-vma-natives-linux.jar differ diff --git a/core/library/lwjgl-vma-natives-windows.jar b/core/library/lwjgl-vma-natives-windows.jar new file mode 100644 index 0000000000..4e823d9c9e Binary files /dev/null and b/core/library/lwjgl-vma-natives-windows.jar differ diff --git a/core/library/lwjgl-vma-sources.jar b/core/library/lwjgl-vma-sources.jar new file mode 100644 index 0000000000..9ae5c3ca29 Binary files /dev/null and b/core/library/lwjgl-vma-sources.jar differ diff --git a/core/library/lwjgl-vma.jar b/core/library/lwjgl-vma.jar new file mode 100644 index 0000000000..c3921a6994 Binary files /dev/null and b/core/library/lwjgl-vma.jar differ diff --git a/core/library/lwjgl-vulkan-sources.jar b/core/library/lwjgl-vulkan-sources.jar new file mode 100644 index 0000000000..2cd3361532 Binary files /dev/null and b/core/library/lwjgl-vulkan-sources.jar differ diff --git a/core/library/lwjgl-vulkan.jar b/core/library/lwjgl-vulkan.jar new file mode 100644 index 0000000000..c02916dae2 Binary files /dev/null and b/core/library/lwjgl-vulkan.jar differ diff --git a/core/library/lwjgl.jar b/core/library/lwjgl.jar new file mode 100644 index 0000000000..24c309ad10 Binary files /dev/null and b/core/library/lwjgl.jar differ diff --git a/core/src/processing/GL2VK/Frame.java b/core/src/processing/GL2VK/Frame.java new file mode 100644 index 0000000000..aafeca50b6 --- /dev/null +++ b/core/src/processing/GL2VK/Frame.java @@ -0,0 +1,47 @@ +package processing.GL2VK; + +import java.nio.LongBuffer; + +import static org.lwjgl.system.MemoryStack.stackGet; + +/** + * Wraps the needed sync objects for an in flight frame + * + * This frame's sync objects must be deleted manually + * */ +public class Frame { + + private final long imageAvailableSemaphore; + private final long renderFinishedSemaphore; + private final long fence; + + public Frame(long imageAvailableSemaphore, long renderFinishedSemaphore, long fence) { + this.imageAvailableSemaphore = imageAvailableSemaphore; + this.renderFinishedSemaphore = renderFinishedSemaphore; + this.fence = fence; + } + + public long imageAvailableSemaphore() { + return imageAvailableSemaphore; + } + + public LongBuffer pImageAvailableSemaphore() { + return stackGet().longs(imageAvailableSemaphore); + } + + public long renderFinishedSemaphore() { + return renderFinishedSemaphore; + } + + public LongBuffer pRenderFinishedSemaphore() { + return stackGet().longs(renderFinishedSemaphore); + } + + public long fence() { + return fence; + } + + public LongBuffer pFence() { + return stackGet().longs(fence); + } +} \ No newline at end of file diff --git a/core/src/processing/GL2VK/GL2VK.java b/core/src/processing/GL2VK/GL2VK.java new file mode 100644 index 0000000000..a44279d6d6 --- /dev/null +++ b/core/src/processing/GL2VK/GL2VK.java @@ -0,0 +1,1364 @@ +package processing.GL2VK; + +import java.nio.IntBuffer; +import java.nio.ShortBuffer; +import java.util.ArrayList; +import java.util.HashSet; + +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GL2ES2; + +import processing.GL2VK.ShaderSPIRVUtils.SPIRV; +import processing.GL2VK.ShaderSPIRVUtils.ShaderKind; +import processing.vulkan.PSurfaceVK; + +import static org.lwjgl.vulkan.VK10.VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; +import static processing.GL2VK.ShaderSPIRVUtils.compileShader; +import static org.lwjgl.vulkan.VK10.VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +public class GL2VK { + + public static final int GL_VERTEX_BUFFER = 1; + public static final int GL_INDEX_BUFFER = 2; + + public static final int GL_VERTEX_SHADER = 1; + public static final int GL_FRAGMENT_SHADER = 2; + + public static final int GL_COMPILE_STATUS = 1; + public static final int GL_INFO_LOG_LENGTH = 2; + + public static final int GL_UNSIGNED_BYTE = 1; + public static final int GL_UNSIGNED_SHORT = 2; + public static final int GL_UNSIGNED_INT = 3; + + public static final int GL_INT = 4; + public static final int GL_BYTE = 5; + public static final int GL_SHORT = 6; + public static final int GL_FLOAT = 7; + public static final int GL_BOOL = 8; + + public static final int GL_TRUE = 1; + public static final int GL_FALSE = 0; + + public static final int STREAM_DRAW = 1; + public static final int STREAM_READ = 2; + public static final int STATIC_DRAW = 3; + public static final int DYNAMIC_DRAW = 4; + + public static final int DEPTH_TEST = 1; + public static final int BLEND = 2; + public static final int MULTISAMPLE = 3; + public static final int POLYGON_SMOOTH = 4; + public static final int CULL_FACE = 5; + + + public static final boolean RETAINED_MODE = true; + public static final boolean IMMEDIATE_MODE = false; + + public static final int DEBUG_MODE = 42; + + + + // Shaders aren't actually anything significant, they're really temporary data structures + // to create a vulkan pipeline. + private class GLShader { + public String source = ""; + public boolean successfulCompile = false; + public int type; + public SPIRV spirv = null; + public String log = ""; + + // Use for vertex shaders only. See notes in glCompileShader + // for why we're oddly putting this here. + public ShaderAttribInfo attribInfo = null; + + // Once the vert and frag shaders are linked it will + // be combined into one ArrayList + public ArrayList uniforms = new ArrayList<>(); + public ArrayList samplerBindings = new ArrayList<>(); + + public GLShader(int type) { + this.type = type; + } + + public void setUniforms(ArrayList uniforms) { + this.uniforms = uniforms; + for (GLUniform u : uniforms) { + // I could and should do it the proper way to + // ensure GL_VERTEX_SHADER is the same meaning for GLUniform + // class but let's be real; it's an int with 2 different values. + u.vertexFragment = type; + } + } + + public void setSamplerBindings(ArrayList bindings) { + this.samplerBindings = bindings; + } + } + + // Attrib pointers are the most stupid thing ever that + // we need an entire class to do it. + private class GLAttribPointer { + public GL2VKPipeline program = null; + + public GLAttribPointer(GL2VKPipeline program) { + this.program = program; + } + } + + // In processing, the glUniform* variable may be called before the pipeline is + // initialised. We can't just init the pipeline because vertexAttribPointer may + // not have been called yet and we end up with a pretty broken pipeline. + // The solution is to temporarily store our glUniform commands and then once our + // pipeline exists, we may call them straight after. + private class TempUniformState { + public int cmdID = 0; + + public int location = 0; + public float valf0 = 0f; + public float valf1 = 0f; + public float valf2 = 0f; + public float valf3 = 0f; + + public int vali0 = 0; + public int vali1 = 0; + public int vali2 = 0; + public int vali3 = 0; + + // For float matricies + public FloatBuffer mat = null; + + public TempUniformState(int loc, float val0) { + cmdID = 1; + this.location = loc; + this.valf0 = val0; + } + public TempUniformState(int loc, float val0, float val1) { + cmdID = 2; + this.location = loc; + this.valf0 = val0; + this.valf1 = val1; + } + public TempUniformState(int loc, float val0, float val1, float val2) { + cmdID = 3; + this.location = loc; + this.valf0 = val0; + this.valf1 = val1; + this.valf2 = val2; + } + public TempUniformState(int loc, float val0, float val1, float val2, float val3) { + cmdID = 4; + this.location = loc; + this.valf0 = val0; + this.valf1 = val1; + this.valf2 = val2; + this.valf3 = val3; + } + + public TempUniformState(int loc, int val0) { + cmdID = 5; + this.location = loc; + this.vali0 = val0; + } + + public TempUniformState(int loc, int val0, int val1) { + cmdID = 6; + this.location = loc; + this.vali0 = val0; + this.vali1 = val1; + } + + public TempUniformState(int loc, int val0, int val1, int val2) { + cmdID = 7; + this.location = loc; + this.vali0 = val0; + this.vali1 = val1; + this.vali2 = val2; + } + + public TempUniformState(int loc, int val0, int val1, int val2, int val3) { + cmdID = 8; + this.location = loc; + this.vali0 = val0; + this.vali1 = val1; + this.vali2 = val2; + this.vali3 = val3; + } + + // Anything else + public TempUniformState(int loc, int cmdID, FloatBuffer buff) { + this.cmdID = cmdID; + this.location = loc; + mat = buff; + } + + public void execute() { + switch (cmdID) { + case 1: + glUniform1f(location, valf0); + break; + case 2: + glUniform2f(location, valf0, valf1); + break; + case 3: + glUniform3f(location, valf0, valf1, valf2); + break; + case 4: + glUniform4f(location, valf0, valf1, valf2, valf3); + break; + case 5: + glUniform1i(location, vali0); + break; + case 6: + glUniform2i(location, vali0, vali1); + break; + case 7: + glUniform3i(location, vali0, vali1, vali2); + break; + case 8: + glUniform4i(location, vali0, vali1, vali2, vali3); + break; + case 99: + glUniformMatrix4fv(location, 1, false, mat); + break; + } + } + } + + private VulkanSystem system = null; + + // This is mostly used for debug. + private HashSet inuseBuffers = new HashSet<>(); + + private GraphicsBuffer[] buffers = new GraphicsBuffer[4096]; + private GL2VKPipeline[] programs = new GL2VKPipeline[1024]; + private GLShader[] shaders = new GLShader[1024]; + // Here contains the buffers, GL2VKPipeline contains the binding descriptors + private TextureBuffer[] textures = new TextureBuffer[4096]; + + private ArrayList tempUniformStates = new ArrayList<>(); + + // Vulkan locations != OpenGL attrib locations + // Attribs are universally unique, meaning that any 2 programs will never have any + // attribute locations that are the same. (e.g. Program 1 has 1 2 3, Program 2 has 4 5 6) + // In vulkan, 2 different programs can have different attribute locations + // (Program 1 has 1 2 3, Program 2 has 1 2 3 4) + // Because of + private GLAttribPointer[] glAttribs = new GLAttribPointer[4096]; + private boolean warningsEnabled = true; + + private int bufferIndex = 1; + private int programIndex = 1; + private int shaderIndex = 1; + private int attribIndex = 1; + private int textureIndex = 1; + + private int boundBuffer = 0; + private int boundProgram = 0; + private int boundTexture = 0; + + private boolean changeProgram = true; + private boolean autoMode = true; + private int autoNodeIndex = 0; + private boolean bufferMultithreaded = true; + private boolean depthWriteEnable = true; + private boolean depthTestEnable = true; + private int frameCount = 0; + + // For special thing in drawElements(). + private int indicesOverflowDetectionCount = 0; + + // Used to convert shaders + // Is an instance because it keeps the state from the + // fragment shader. + private GL2VKShaderConverter shaderConverter = new GL2VKShaderConverter(); + + + + // Constructor + public GL2VK(PSurfaceVK surface, int numThreadNodes) { + system = new VulkanSystem(); + system.initVulkan(surface, numThreadNodes); + } + + public GL2VK(int debugNumber) { + if (debugNumber != DEBUG_MODE) { + system = new VulkanSystem(); +// system.initVulkan(new PSurfaceVK(), 1); + } + } + + private void warn(String mssg) { + if (warningsEnabled) { + System.err.println("GL2VK WARNING "+mssg); + } + } + + public void bufferMultithreaded(boolean onoff) { + bufferMultithreaded = onoff; + } + + public void glGenBuffers(int count, IntBuffer out) { + for (int i = 0; i < count; i++) { + // Create new buffer object + // We may be in debug mode so we may not use system + if (system == null) { + buffers[bufferIndex] = new GraphicsBuffer(); + } + else { + buffers[bufferIndex] = new GraphicsBuffer(system); + } + + // Put it into the int array so we get back our + // ids to our allocated buffers. + out.put(i, bufferIndex++); + } + out.rewind(); + } + + + public void glBindBuffer(int type, int vbo) { + boundBuffer = vbo; +// System.out.println("glBindBuffer"+vbo); + } + + private int getBufferUsage(int target) { + int vkusage = 0; + switch (target) { + case GL_VERTEX_BUFFER: + vkusage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + break; + case GL_INDEX_BUFFER: + vkusage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + break; + } + return vkusage; + } + + + private void recordInuseBuffer(Buffer buff) { +// if (inuseBuffers.contains(buff)) { +//// warn("glBufferData: buffer "+boundBuffer+" in use."); +//// Thread.dumpStack(); +// } +// else { +// inuseBuffers.add(buff); +// } + } + + + // Method for null buffers to only buffer the data. + public void glBufferData(int target, int size, int usage) { + + + if (boundBuffer <= 0) { + warn("glBufferData: no bound buffer."); + return; + } + if (buffers[boundBuffer] == null) { + warn("glBufferData: buffer "+boundBuffer+" doesn't exist."); + return; + } + + + // STATIC_DRAW used by immediate rendering + if (usage == STATIC_DRAW) { + buffers[boundBuffer].createBufferAuto(size, getBufferUsage(target), IMMEDIATE_MODE); + } + // And dynamic used for immediate. + else if (usage == DYNAMIC_DRAW) { + buffers[boundBuffer].createBufferAuto(size, getBufferUsage(target), RETAINED_MODE); + } + else warn("Unknown usage "+usage+"."); + + } + + public void glBufferData(int target, int size, ByteBuffer data, int usage) { + + if (boundBuffer <= 0) { + warn("glBufferData: no bound buffer."); + return; + } + if (buffers[boundBuffer] == null) { + warn("glBufferData: buffer "+boundBuffer+" doesn't exist."); + return; + } + + + // Note: target is for specifying vertex_array, indicies_array + // which we'll likely need. + + // STATIC_DRAW used by immediate rendering + if (usage == STATIC_DRAW) { + buffers[boundBuffer].createBufferAuto(size, getBufferUsage(target), IMMEDIATE_MODE); + if (data != null) { + if (bufferMultithreaded) { + recordInuseBuffer(data); + system.nodeBufferData(buffers[boundBuffer], size, data, buffers[boundBuffer].getInstance()); + } + else { + buffers[boundBuffer].bufferDataImmediate(data, size, buffers[boundBuffer].getInstance()); + } + } + } + // And dynamic used for immediate. + else if (usage == DYNAMIC_DRAW) { + buffers[boundBuffer].createBufferAuto(size, getBufferUsage(target), RETAINED_MODE); + if (data != null) buffers[boundBuffer].bufferDataRetained(data, size, buffers[boundBuffer].getInstance()); + } + else warn("Unknown usage "+usage+"."); + +// ByteBuffer newData = null; +// System.out.println(boundBuffer+"VK BUFFER "+buffers[boundBuffer].bufferID); +// if (data != null) { +// // newData = ByteBuffer.allocateDirect(data.capacity()); +// // newData.order(ByteOrder.LITTLE_ENDIAN); +// data.rewind(); +// +// int max = data.capacity(); +// if (32 < max) max = 64; +// +// for (int i = 0; i < max; i+=4) { +// float f = data.getFloat(); +// // if (f > 1.0f) f /= 512f; +// System.out.print(f+" "); +// // newData.putFloat(f); +// } +// data.rewind(); +// System.out.println(); +// +// for (int i = 0; i < max; i+=4) { +// short x = data.getShort(); +// // if (f > 1.0f) f /= 512f; +// System.out.print(x+" "); +// // newData.putFloat(f); +// } +// data.rewind(); +// // newData.rewind(); +// System.out.println(); +// } + } + + public void glBufferData(int target, int size, FloatBuffer data, int usage) { + if (boundBuffer <= 0) { + warn("glBufferData: no bound buffer."); + return; + } + if (buffers[boundBuffer] == null) { + warn("glBufferData: buffer "+boundBuffer+" doesn't exist."); + return; + } + +// System.out.println("FLOAT BUFFER "+boundBuffer+":"); +// int max = data.capacity(); +// if (32 < max) max = 64; +// +// for (int i = 0; i < max; i+=4) { +// float f = data.get(); +// System.out.print(f+" "); +// } +// data.rewind(); +// System.out.println(); + + if (usage == STATIC_DRAW) { + buffers[boundBuffer].createBufferAuto(size, getBufferUsage(target), IMMEDIATE_MODE); + if (data != null) { + if (bufferMultithreaded) { + recordInuseBuffer(data); + system.nodeBufferData(buffers[boundBuffer], size, data, buffers[boundBuffer].getInstance()); + } + else { + buffers[boundBuffer].bufferDataImmediate(data, size, buffers[boundBuffer].getInstance()); + } + } + } + else if (usage == DYNAMIC_DRAW) { + buffers[boundBuffer].createBufferAuto(size, getBufferUsage(target), RETAINED_MODE); + if (data != null) buffers[boundBuffer].bufferDataRetained(data, size, buffers[boundBuffer].getInstance()); + } + else warn("Unknown usage "+usage+"."); + buffers[boundBuffer].increaseInstance(); + } + + public void glBufferData(int target, int size, ShortBuffer data, int usage) { + if (boundBuffer <= 0) { + warn("glBufferData: no bound buffer."); + return; + } + if (buffers[boundBuffer] == null) { + warn("glBufferData: buffer "+boundBuffer+" doesn't exist."); + return; + } + + if (usage == STATIC_DRAW) { + buffers[boundBuffer].createBufferAuto(size, getBufferUsage(target), IMMEDIATE_MODE); + if (data != null) { + if (bufferMultithreaded) { + recordInuseBuffer(data); + system.nodeBufferData(buffers[boundBuffer], size, data, buffers[boundBuffer].getInstance()); + } + else { + buffers[boundBuffer].bufferDataImmediate(data, size, buffers[boundBuffer].getInstance()); + } + } + } + else if (usage == DYNAMIC_DRAW) { + buffers[boundBuffer].createBufferAuto(size, getBufferUsage(target), RETAINED_MODE); + if (data != null) buffers[boundBuffer].bufferDataRetained(data, size, buffers[boundBuffer].getInstance()); + } + else warn("Unknown usage "+usage+"."); + buffers[boundBuffer].increaseInstance(); + } + + public void glBufferData(int target, int size, IntBuffer data, int usage) { + if (boundBuffer <= 0) { + warn("glBufferData: no bound buffer."); + return; + } + if (buffers[boundBuffer] == null) { + warn("glBufferData: buffer "+boundBuffer+" doesn't exist."); + return; + } + + if (usage == STATIC_DRAW) { + buffers[boundBuffer].createBufferAuto(size, getBufferUsage(target), IMMEDIATE_MODE); + if (data != null) { + if (bufferMultithreaded) { + recordInuseBuffer(data); + system.nodeBufferData(buffers[boundBuffer], size, data, buffers[boundBuffer].getInstance()); + } + else { + buffers[boundBuffer].bufferDataImmediate(data, size, buffers[boundBuffer].getInstance()); + } + } + } + else if (usage == DYNAMIC_DRAW) { + buffers[boundBuffer].createBufferAuto(size, getBufferUsage(target), RETAINED_MODE); + if (data != null) buffers[boundBuffer].bufferDataRetained(data, size, buffers[boundBuffer].getInstance()); + } + else warn("Unknown usage "+usage+"."); + buffers[boundBuffer].increaseInstance(); + } + + + private boolean pipelineInitiated() { + return programs[boundProgram].initiated(); + } + + int drawCounts = 0; + + private void selectNodeAuto() { + if (autoMode) { + if (drawCounts >= 2) { + selectNode(autoNodeIndex++); + if (autoNodeIndex >= getNodesCount()) autoNodeIndex = 0; + drawCounts = 0; + } + } + } + + private boolean checkAndPrepareProgram() { + if (boundProgram <= 0) { + warn("checkAndPrepareProgram: No program bound."); + return false; + } + if (programs[boundProgram] == null || programs[boundProgram].attribInfo == null) { + warn("checkAndPrepareProgram: program "+boundProgram+" doesn't exist or isn't set up properly"); + return false; + } + + // Update states + programs[boundProgram].depthWriteEnable = depthWriteEnable; + programs[boundProgram].depthTestEnable = depthTestEnable; + + // Switch pipeline if the pipeline's state changed (e.g. depth testing) + if (programs[boundProgram].updatedState()) { + + // Pipeline may not exist. Let's create it if it doesn't exist. + if (programs[boundProgram].needsNewStateCreation()) { + programs[boundProgram].createGraphicsPipeline(); + } + system.updateNodePipeline(programs[boundProgram].getPipeline()); + + // Call our pending uniforms + for (TempUniformState u : tempUniformStates) { + u.execute(); + } + tempUniformStates.clear(); + } + // Bad coding here but I think we still need to call it because reasons. + else { + system.updateNodePipeline(programs[boundProgram].getPipeline()); + } + + + // Remember, this function we're in right now gets called with every draw command. + // First we need to make sure our texture is prepared in our pipeline. + if (boundTexture != 0) { + programs[boundProgram].addTextureIfAbsent(boundTexture, textures[boundTexture]); + + // Next call the bind descriptor command + system.nodeBindDescriptorSet( + programs[boundProgram].getLayout(), + programs[boundProgram].getCurrentTextureDescriptor(boundTexture) + ); + } + + return true; + } + + public void glDrawArrays(int mode, int first, int count) { + // Mode not used + if (checkAndPrepareProgram() == false) return; + +// int stride = programs[boundProgram].attribInfo.bindingSize; +// System.out.println("CHECK YOUR STRIDE: "+stride); +// for (Long val : programs[boundProgram].getVKBuffers()) { +// System.out.println(val); +// } + system.nodeDrawArrays(programs[boundProgram].getVKBuffers(), count, first); + selectNodeAuto(); + } + + + // In normal openGL, the offset argument is instead a pointer to the indicies, but + // in jogl it's the bound buffer?? + // Either way, it's java and Processing we're writing this for, so it is what it is. + public void glDrawElements(int mode, int count, int type, int offset) { + drawCounts++; + + // Mode not used + if (checkAndPrepareProgram() == false) return; + + // Here's a patch to a bug that took me forever to solve: + // Processing uses 16-bit indices buffers, but it means that when rendering large + // geometry we are most likely gonna encounter a short overflow which means the bits + // rolls back to 0. + // It seems the processing devs were very aware of this, even more so aware of a quirk in + // openGL: the offset for some reason affects the overflow so that it still accesses the correct + // indicies and doesn't start at 0 again. + // Super weird, super bad and niche design, but super convenient, because that's what openGL is. Convenient. + // How do we emulate this convenient behaviour in vulkan? + // Simple: check to see if our indices buffer has overflowed and give a vertex index offset so we don't start at 0. + int vertexOffset = 0; + + // TODO: Proper check that indicies buffer actually uses short instead of just assuming it. + + int ioffset = offset/2; + + // if offset > 30000 there's a pretty good chance that indices have overflowed. + if (ioffset > 30000) { + // Now for the unhinged part: we need to check the contents of the index buffer. + // We can only do that by mapping the buffer. + if (buffers[boundBuffer].indexBuffer) { + if (buffers[boundBuffer].indexInstantAccessBuffer != null) { + + ShortBuffer buff = buffers[boundBuffer].indexInstantAccessBuffer; + + try { + if (ioffset < buff.capacity()) { + // for (int i = -5; i < 8; i++) { + // System.out.print(buff.get(ioffset+i)+" "); + // } + + // Now approx compare numbers. + // We want the point where we're overflowing + // (so really big number and then really small number like 0) + if (buff.get(ioffset-2) > 32700 && buff.get(ioffset-1) > 32700 && buff.get(ioffset) == 0) { + // HIT + indicesOverflowDetectionCount++; + + // Now we can set our magical number. + // // for some reason works better when it's 32764. + // Whatever let's stay consistent + vertexOffset = indicesOverflowDetectionCount*32768; +// System.out.println("HIT "+vertexOffset); + } + else { +// System.out.println("no hit (unmatching overflow vals)"); + } + } + else { +// System.out.println("no hit (offset out of bounds)"); + } + } + catch (RuntimeException e) { + // Continue on. +// System.out.println("no hit (exception)"); + } + } + else { +// System.out.println("no hit (null indexInstantAccessBuffer)"); + } + } + else { +// System.out.println("no hit (not index buffer, this shouldnt happen)"); + } + } + + system.nodeDrawIndexed(count, buffers[boundBuffer].getCurrBuffer(), programs[boundProgram].getVKBuffers(), offset, vertexOffset, type); + selectNodeAuto(); + } + + + // Probably not going to fully implement glEnableVertexAttribArray or glDisableVertexAttribArray + // because chances are, when we use glVertexAttribPointer, we're being pretty clear that we do, + // indeed, want to use the vertexAttrib. And it's not like glDisableVertexAttribArray is going to + // have any effect, you can't disable vertex attribs in a pipeline that's already been created. + public void glVertexAttribPointer(int glindex, int size, int type, boolean normalized, int stride, int offset) { + if (boundBuffer <= 0) { + warn("glVertexAttribPointer: don't forget to bind a buffer!"); + } + if (glindex == -1 || glAttribs[glindex] == null) { + warn("glVertexAttribPointer: Vertex attrib "+glindex+" doesn't exist."); + return; + } + + + + + // Convert from gl index to vk location + // We also need the program to see what we're doing + GL2VKPipeline program = glAttribs[glindex].program; + // It's such a mess, I'm so sorry + int vkLocation = program.getVKAttribLocation(glindex); + + +// System.out.println("ATTRIB BUFFER "+glindex+" "+buffers[boundBuffer].bufferID); +// System.out.println("glVertexAttribPointer bind "+boundBuffer); + program.bind(boundBuffer, buffers[boundBuffer]); + program.vertexAttribPointer(vkLocation, size, type, normalized, stride, offset); + } + + + public void glEnableVertexAttribArray(int index) { +// System.out.println("glEnableVertexAttribArray binding "+boundBuffer+" index "+index); + } + + public void glDisableVertexAttribArray(int index) { +// System.out.println("glDisableVertexAttribArray binding "+boundBuffer+" index "+index); + } + + public int glCreateProgram() { + int ret = programIndex; + // Null if it's in debug mode. + if (system == null) { + programs[programIndex++] = new GL2VKPipeline(); + } + else { + programs[programIndex++] = new GL2VKPipeline(system); + } + return ret; + } + + public int glGetAttribLocation(int program, String name) { + if (programs[program] == null) { + warn("glGetAttribLocation: program "+program+" doesn't exist."); + return 0; + } +// System.out.println(name+" "+programs[program].getGLAttribLocation(name)); + return programs[program].getGLAttribLocation(name); + } + + + public int glCreateShader(int type) { + int ret = shaderIndex; + shaders[shaderIndex++] = new GLShader(type); + return ret; + } + + // TODO: add minor error checking. + public void glShaderSource(int shader, String source) { + if (shaders[shader] == null) { + warn("glShaderSource: shader "+shader+" doesn't exist."); + } + // TODO: convert from opengl-style glsl to vulkan glsl + shaders[shader].source = source; + } + + public String glGetShaderSource(int shader) { + if (shaders[shader] == null) { + warn("glGetShaderSource: shader "+shader+" doesn't exist."); + } + return shaders[shader].source; + } + + public void glDeleteShader(int shader) { + shaders[shader] = null; + } + + + public void glCompileShader(int shader) { + GLShader sh = shaders[shader]; + if (sh == null) { + warn("glCompileShader: shader "+shader+" doesn't exist."); + } + + // Convert the shader to openGL + sh.source = shaderConverter.convert(sh.source, sh.type); + +// System.out.println(sh.source); + + + + glCompileVKShader(shader); + } + + // This is simple stuff to understand (i hope) + public void glCompileVKShader(int shader) { + GLShader sh = shaders[shader]; + if (sh == null) { + warn("glCompileShader: shader "+shader+" doesn't exist."); + } + + ShaderKind shaderKind; + if (sh.type == GL_VERTEX_SHADER) { + shaderKind = ShaderKind.VERTEX_SHADER; + } + else if (sh.type == GL_FRAGMENT_SHADER) { + shaderKind = ShaderKind.FRAGMENT_SHADER; + } + else shaderKind = ShaderKind.VERTEX_SHADER; + + try { + sh.spirv = compileShader(sh.source, shaderKind); + sh.successfulCompile = true; + } + catch (RuntimeException e) { + sh.successfulCompile = false; + sh.log = e.getMessage(); + } + + + // TODO: failed compiles just throws an exception. Actually check to see if it compiled successfully + // or not. + + if (sh.successfulCompile) { + // Here, we must get attribute information. + // + // "Why not do it when we create our pipeline or when we + // attach the shader?" + // + // Because as soon as we compile the shader, the program could + // call glGetAttribLocation before attaching the shader or + // linking the program. + // Thanks, opengl, you're so messy that we have to do things weirdly. + if (sh.type == GL_VERTEX_SHADER) { + sh.attribInfo = new ShaderAttribInfo(sh.source); + } + + // Also let's parse the uniform shader too + sh.setUniforms(UniformParser.parseUniforms(sh.source)); + + // And the samplers + sh.setSamplerBindings(UniformParser.parseSamplers(sh.source)); + } + } + + + // This function populates the glAttribs array which contains information about + // the vulkan program, and nothing else. + // It literally just loops over or something + private void addGLAttribs(GL2VKPipeline program) { + int l = program.attribInfo.nameToLocation.size(); + for (int i = 0; i < l; i++) { + glAttribs[attribIndex++] = new GLAttribPointer(program); + } + } + + + + public void glAttachShader(int program, int shader) { + // Some error checking. + GLShader sh = shaders[shader]; + if (sh == null) { + warn("glGetShaderiv: shader "+shader+" doesn't exist."); + return; + } + if (!sh.successfulCompile) { + warn("glAttachShader: Can't attach shader that hasn't been compiled or failed compilation."); + return; + } + if (programs[program] == null) { + warn("glAttachShader: program "+program+" doesn't exist."); + return; + } + if (programs[program].initiated()) { + warn("glAttachShader: program "+program+" is already up and running! You can't change programs once they're created."); + return; + } + + if (sh.type == GL_VERTEX_SHADER) { + programs[program].vertShaderSPIRV = shaders[shader].spirv; + // Of course we'll need the attrib info to our pipeline. + // This function has been modified so that instead of vk locations, we get + // back gl locations. + // Welcome to the absolute unhinged nature of opengl. + programs[program].addAttribInfo(sh.attribInfo, attribIndex); + + // And then + // We'll need gl attribs since + // gl attrib locations != vulkan attrib locations. + addGLAttribs(programs[program]); + } + else if (sh.type == GL_FRAGMENT_SHADER) { + programs[program].fragShaderSPIRV = shaders[shader].spirv; + } + + // And also add the uniform attribs to the GLPipeline + programs[program].addUniforms(sh.uniforms); + programs[program].addSamplers(sh.samplerBindings); + +// System.out.println("PROGRAM "+program+" to "+sh.source); + } + + // Mainly just used for getting shader compilation status. + public void glGetShaderiv(int shader, int pname, IntBuffer params) { + GLShader sh = shaders[shader]; + if (sh == null) { + warn("glGetShaderiv: shader "+shader+" doesn't exist."); + return; + } + if (pname == GL_COMPILE_STATUS) { + int status = GL_FALSE; + if (sh.successfulCompile == true) { + status = GL_TRUE; + } + params.put(0, status); + } + if (pname == GL_INFO_LOG_LENGTH) { + params.put(0, shaders[shader].log.length()); + } + } + + public String glGetShaderInfoLog(int shader) { + if (shaders[shader] == null) { + warn("glGetShaderInfoLog: shader "+shader+" doesn't exist."); + return ""; + } + return shaders[shader].log; + } + + // Because we create our pipeline on draw command calls, this effectively does nothing. + public void glLinkProgram(int program) { + + } + + public void glUseProgram(int program) { + if (program == 0) { + return; + } + if (program != boundProgram) { + changeProgram = true; + } + boundProgram = program; + } + + public int glGetUniformLocation(int program, String name) { + // Remember, we renamed "texture" to "texturegl2vk" since "texture" is a keyword + // in vulkan glsl + if (name.equals("texture")) { + name = "texturegl2vk"; + } + + if (programs[program] == null) { + warn("getUniformLocation: program "+program+" doesn't exist."); + return -1; + } +// System.out.println(name+" "+programs[program].getUniformLocation(name)); + return programs[program].getUniformLocation(name); + } + + + + // TODO: each pipeline will need its uniforms re-pushed when it switches. + public void glUniform1f(int location, float value0) { + GLUniform uniform = programs[boundProgram].getUniform(location); + + // Don't bother for now. + if (uniform.isSampler) return; + + if (pipelineInitiated()) { + system.nodePushConstants(programs[boundProgram].getLayout(), uniform.vertexFragment, uniform.offset, value0); + } + else { + tempUniformStates.add(new TempUniformState(location, value0)); + } + } + + + public void glUniform2f(int location, float value0, float value1) { + GLUniform uniform = programs[boundProgram].getUniform(location); + + // Don't bother for now. + if (uniform.isSampler) return; + + if (pipelineInitiated()) { + system.nodePushConstants(programs[boundProgram].getLayout(), uniform.vertexFragment, uniform.offset, value0, value1); + } + else { + tempUniformStates.add(new TempUniformState(location, value0, value1)); + } + } + + public void glUniform3f(int location, float value0, float value1, float value2) { + GLUniform uniform = programs[boundProgram].getUniform(location); + + // Don't bother for now. + if (uniform.isSampler) return; + + if (pipelineInitiated()) { + system.nodePushConstants(programs[boundProgram].getLayout(), uniform.vertexFragment, uniform.offset, value0, value1, value2); + } + else { + tempUniformStates.add(new TempUniformState(location, value0, value1, value2)); + } + } + + + public void glUniform4f(int location, float value0, float value1, float value2, float value3) { + GLUniform uniform = programs[boundProgram].getUniform(location); + + // Don't bother for now. + if (uniform.isSampler) return; + + + if (pipelineInitiated()) { + system.nodePushConstants(programs[boundProgram].getLayout(), uniform.vertexFragment, uniform.offset, value0, value1, value2, value3); + } + else { + tempUniformStates.add(new TempUniformState(location, value0, value1, value2, value3)); + } + } + + // TODO: add other uniform functions. + + + public void glUniformMatrix4fv(int location, int count, boolean transpose, ByteBuffer mat) { + + // TODO: warn if count is > 1 + + GLUniform uniform = programs[boundProgram].getUniform(location); + + // Don't bother for now. + if (uniform.isSampler) return; + + // Not actually used by processing so ok to comment this out +// if (pipelineInitiated()) { + system.nodePushConstants(programs[boundProgram].getLayout(), uniform.vertexFragment, uniform.offset, mat); +// } +// else { +// tempUniformStates.add(new TempUniformState(location, 99, mat)); +// } + } + + public void glUniform1i(int location, int value0) { + + GLUniform uniform = programs[boundProgram].getUniform(location); + + // Don't bother for now. + if (uniform.isSampler) return; + + if (pipelineInitiated()) { + system.nodePushConstants(programs[boundProgram].getLayout(), uniform.vertexFragment, uniform.offset, value0); + } + else { + tempUniformStates.add(new TempUniformState(location, value0)); + } + } + + public void glUniform2i(int location, int value0, int value1) { + GLUniform uniform = programs[boundProgram].getUniform(location); + + // Don't bother for now. + if (uniform.isSampler) return; + + if (pipelineInitiated()) { + system.nodePushConstants(programs[boundProgram].getLayout(), uniform.vertexFragment, uniform.offset, value0, value1); + } + else { + tempUniformStates.add(new TempUniformState(location, value0, value1)); + } + } + + public void glUniform3i(int location, int value0, int value1, int value2) { + GLUniform uniform = programs[boundProgram].getUniform(location); + + // Don't bother for now. + if (uniform.isSampler) return; + + if (pipelineInitiated()) { + system.nodePushConstants(programs[boundProgram].getLayout(), uniform.vertexFragment, uniform.offset, value0, value1, value2); + } + else { + tempUniformStates.add(new TempUniformState(location, value0, value1, value2)); + } + } + + public void glUniform4i(int location, int value0, int value1, int value2, int vulue3) { + GLUniform uniform = programs[boundProgram].getUniform(location); + + // Don't bother for now. + if (uniform.isSampler) return; + + if (pipelineInitiated()) { + system.nodePushConstants(programs[boundProgram].getLayout(), uniform.vertexFragment, uniform.offset, value0, value1, value2, vulue3); + } + else { + tempUniformStates.add(new TempUniformState(location, value0, value1, value2, vulue3)); + } + } + + public void glUniformMatrix4fv(int location, int count, boolean transpose, FloatBuffer mat) { +// System.out.println("glUniformMatrix4fv"); + if (count > 1) { + warn("glUniformMatrix4fv: count above 1 not supported yet."); + } + +// System.out.println("MATRIX "+transpose); +// mat.rewind(); +// for (int y = 0; y < 4; y++) { +// for (int x = 0; x < 4; x++) { +// System.out.print(" "+mat.get()); +// } +// System.out.println(); +// } +// mat.rewind(); + + + GLUniform uniform = programs[boundProgram].getUniform(location); + + // Don't bother for now. + if (uniform.isSampler) return; + + if (pipelineInitiated()) { + system.nodePushConstants(programs[boundProgram].getLayout(), uniform.vertexFragment, uniform.offset, mat); + } + else { + tempUniformStates.add(new TempUniformState(location, 99, mat)); + } + } + + + public ByteBuffer glMapBuffer(int target, int access) { + return buffers[boundBuffer].map(buffers[boundBuffer].getInstance()); + } + + public void glUnmapBuffer(int target) { + buffers[boundBuffer].unmap(buffers[boundBuffer].getInstance()); + buffers[boundBuffer].increaseInstance(); + } + + public void glClearColor(float r, float g, float b, float a) { + system.nodeClearColor(r, g, b, a); + } + + // Modifies data in the TextureBuffer. + public void glTexSubImage2D(int target, int level, int xOffset, int yOffset, + int width, int height, int format, int type, + Buffer data) { + + if (textures[boundTexture] == null) { + warn("glTexSubImage2D: texture "+boundTexture+" doesn't exist."); + } + + if (data == null) { + + } + else if (data instanceof IntBuffer) { + textures[boundTexture].bufferData((IntBuffer)data, xOffset, yOffset, width, height); + } + else { + System.err.println("have fun playing the types guessing game"); + } + } + + // Actually creates the TextureBuffer + public void glTexImage2D(int target, int level, int internalFormat, int width, + int height, int border, int format, int type, + Buffer data) { + + // Ignore this + if (width == 0 || height == 0) return; + + textures[boundTexture].createBuffer(width, height); + + if (data == null) { + } + else if (data instanceof IntBuffer) { + textures[boundTexture].bufferData((IntBuffer)data, 0, 0, width, height); + } + else { + System.err.println("have fun playing the types guessing game"); + } + } + + // Processing only uses 1 texture unit. + // We can worry about texture unit emulation later. + public void glBindTexture(int tex) { + boundTexture = tex; + } + + public void glGenTextures(int count, IntBuffer out) { + for (int i = 0; i < count; i++) { + // Create new buffer object + textures[textureIndex] = new TextureBuffer(system); + + // Put it into the int array so we get back our + // ids to our allocated buffers. + out.put(i, textureIndex++); + } + out.rewind(); + } + + public void glEnable(int value) { + if (value == DEPTH_TEST) { + depthTestEnable = true; + } + else if (value == BLEND) { + + } + else if (value == MULTISAMPLE) { + + } + else if (value == POLYGON_SMOOTH) { + + } + else if (value == CULL_FACE) { + + } + } + + + public void glDisable(int value) { + if (value == DEPTH_TEST) { + depthTestEnable = false; + } + else if (value == BLEND) { + + } + else if (value == MULTISAMPLE) { + + } + else if (value == POLYGON_SMOOTH) { + + } + else if (value == CULL_FACE) { + + } + } + + public void glDepthMask(boolean value) { + depthWriteEnable = value; + } + + +// public void glUniform2f(int location, float value0, float value1) { +// if (programs[boundProgram] == null) { +// warn("glAttachShader: program "+boundProgram+" doesn't exist."); +// return; +// } +// if (checkAndPrepareProgram() == false) return; +// +// GLUniform uniform = programs[boundProgram].getUniform(location); +// +// int size = 8; +// ByteBuffer buffer = ByteBuffer.allocate(size); +// buffer.order(ByteOrder.LITTLE_ENDIAN); +// buffer.putFloat(value0); +// buffer.putFloat(value1); +// buffer.rewind(); +// +// system.nodePushConstants(programs[boundProgram].pipelineLayout, uniform.vertexFragment, uniform.offset, buffer); +// } + + + + private void cleanup() { + system.cleanupNodes(); + + // Clean up graphics buffers + for (int i = 0; i < buffers.length; i++) { + if (buffers[i] != null) { + buffers[i].destroy(); + } + } + + // Clean up pipelines + for (int i = 0; i < programs.length; i++) { + if (programs[i] != null) { + programs[i].clean(); + } + } + + // Clean up textures + for (int i = 0; i < textures.length; i++) { + if (textures[i] != null) { + textures[i].clean(); + } + } + + system.cleanupRest(); + } + + public boolean shouldClose() { + return system.shouldClose(); + } + + public void close() { + cleanup(); + } + + + public void beginRecord() { + GraphicsBuffer.setFrame(system.getFrame()); + inuseBuffers.clear(); + indicesOverflowDetectionCount = 0; + for (int i = 1; i < bufferIndex; i++) { + buffers[i].reset(); + } + if (frameCount < 5) { + bufferMultithreaded = false; + } + else { + bufferMultithreaded = true; + } + frameCount++; + + system.beginRecord(); + changeProgram = true; + } + + public void endRecord() { + system.endRecord(); + } + + public void selectNode(int node) { + system.selectNode(node); + } + + public void enableAutoMode() { + autoMode = true; + } + + public void disableAutoMode() { + autoMode = false; + } + + public int getNodesCount() { + return system.getNodesCount(); + } + + public void setMaxNodes(int v) { + system.setMaxNodes(v); + } + + public GL2VKPipeline getPipeline(int program) { + return programs[program]; + } +} \ No newline at end of file diff --git a/core/src/processing/GL2VK/GL2VKPipeline.java b/core/src/processing/GL2VK/GL2VKPipeline.java new file mode 100644 index 0000000000..7dce3998e4 --- /dev/null +++ b/core/src/processing/GL2VK/GL2VKPipeline.java @@ -0,0 +1,843 @@ +package processing.GL2VK; + +import static org.lwjgl.system.MemoryStack.stackPush; +import static org.lwjgl.vulkan.VK10.VK_COLOR_COMPONENT_A_BIT; +import static org.lwjgl.vulkan.VK10.VK_COLOR_COMPONENT_B_BIT; +import static org.lwjgl.vulkan.VK10.VK_COLOR_COMPONENT_G_BIT; +import static org.lwjgl.vulkan.VK10.VK_COLOR_COMPONENT_R_BIT; +import static org.lwjgl.vulkan.VK10.VK_CULL_MODE_BACK_BIT; +import static org.lwjgl.vulkan.VK10.VK_FRONT_FACE_CLOCKWISE; +import static org.lwjgl.vulkan.VK10.VK_LOGIC_OP_COPY; +import static org.lwjgl.vulkan.VK10.VK_NULL_HANDLE; +import static org.lwjgl.vulkan.VK10.VK_POLYGON_MODE_FILL; +import static org.lwjgl.vulkan.VK10.VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; +import static org.lwjgl.vulkan.VK10.VK_SAMPLE_COUNT_1_BIT; +import static org.lwjgl.vulkan.VK10.VK_SHADER_STAGE_FRAGMENT_BIT; +import static org.lwjgl.vulkan.VK10.VK_SHADER_STAGE_VERTEX_BIT; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_SUCCESS; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; +import static org.lwjgl.vulkan.VK10.VK_COMPARE_OP_LESS_OR_EQUAL; +import static org.lwjgl.vulkan.VK10.vkCreateGraphicsPipelines; +import static org.lwjgl.vulkan.VK10.vkCreatePipelineLayout; +import static org.lwjgl.vulkan.VK10.vkCreateShaderModule; +import static org.lwjgl.vulkan.VK10.vkDestroyPipeline; +import static org.lwjgl.vulkan.VK10.vkDestroyPipelineLayout; +import static org.lwjgl.vulkan.VK10.vkDestroyShaderModule; +import static org.lwjgl.vulkan.VK10.vkCreateDescriptorSetLayout; +import static org.lwjgl.vulkan.VK10.vkDestroyDescriptorSetLayout; +import static org.lwjgl.vulkan.VK10.vkCreateDescriptorPool; +import static org.lwjgl.vulkan.VK10.vkAllocateDescriptorSets; +import static org.lwjgl.vulkan.VK10.vkUpdateDescriptorSets; +import static org.lwjgl.vulkan.VK10.VK_BLEND_FACTOR_SRC_ALPHA; +import static org.lwjgl.vulkan.VK10.VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; +import static org.lwjgl.vulkan.VK10.VK_BLEND_OP_ADD; +import static org.lwjgl.vulkan.VK10.VK_BLEND_FACTOR_ONE; +import static org.lwjgl.vulkan.VK10.VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; +import static org.lwjgl.vulkan.VK10.VK_BLEND_OP_ADD; +import static org.lwjgl.vulkan.VK10.VK_CULL_MODE_NONE; +import static org.lwjgl.vulkan.VK10.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_COMPARE_OP_LESS; + +import java.nio.ByteBuffer; +import java.nio.LongBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; + +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkDescriptorImageInfo; +import org.lwjgl.vulkan.VkDescriptorPoolCreateInfo; +import org.lwjgl.vulkan.VkDescriptorPoolSize; +import org.lwjgl.vulkan.VkDescriptorSetAllocateInfo; +import org.lwjgl.vulkan.VkDescriptorSetLayoutBinding; +import org.lwjgl.vulkan.VkDescriptorSetLayoutCreateInfo; +import org.lwjgl.vulkan.VkExtent2D; +import org.lwjgl.vulkan.VkGraphicsPipelineCreateInfo; +import org.lwjgl.vulkan.VkOffset2D; +import org.lwjgl.vulkan.VkPipelineColorBlendAttachmentState; +import org.lwjgl.vulkan.VkPipelineColorBlendStateCreateInfo; +import org.lwjgl.vulkan.VkPipelineDepthStencilStateCreateInfo; +import org.lwjgl.vulkan.VkPipelineInputAssemblyStateCreateInfo; +import org.lwjgl.vulkan.VkPipelineLayoutCreateInfo; +import org.lwjgl.vulkan.VkPipelineMultisampleStateCreateInfo; +import org.lwjgl.vulkan.VkPipelineRasterizationStateCreateInfo; +import org.lwjgl.vulkan.VkPipelineShaderStageCreateInfo; +import org.lwjgl.vulkan.VkPipelineVertexInputStateCreateInfo; +import org.lwjgl.vulkan.VkPipelineViewportStateCreateInfo; +import org.lwjgl.vulkan.VkPushConstantRange; +import org.lwjgl.vulkan.VkRect2D; +import org.lwjgl.vulkan.VkShaderModuleCreateInfo; +import org.lwjgl.vulkan.VkVertexInputAttributeDescription; +import org.lwjgl.vulkan.VkVertexInputBindingDescription; +import org.lwjgl.vulkan.VkViewport; +import org.lwjgl.vulkan.VkWriteDescriptorSet; + +import processing.GL2VK.ShaderSPIRVUtils.SPIRV; + + +// Buffer bindings in opengl look like this +// buffer 6 +// buffer 7 +// buffer 8 +// Let's say we call +// bindBuffer(6) +// vertexAttribPointer(...) +// bindBuffer(5) +// vertexAttribPointer(...) +// bindBuffer(8) +// vertexAttribPointer(...) +// The question is, what would vulkan's bindings be? +// ... +// Whenever we bind a buffer, we need to add it to a list along +// with the actual buffer so we can later use it to vkcmdBindVertexArrays() +// so +// bindBuffer(6) +// enableVertexArrays() +// vertexAttribPointer(...) +// - vkbuffer[0] = buffer(6) +// - Create VertexAttribsBinding with binding 0 +// +// bindBuffer(5) +// vertexAttribPointer(...) +// - vkbuffer[1] = buffer(5) +// - Create VertexAttribsBinding with binding 1 +// +// bindBuffer(8) +// vertexAttribPointer(...) +// - vkbuffer[2] = buffer(8) +// - Create VertexAttribsBinding with binding 2 +// +// We don't care what our vkbindings are, as long as it's in the correct order +// as we're passing the lists in when we call vkCmdBindVertexArrays(). + +// TODO: Properly fix up compileshaders +// TODO: Turn gl2vkBinding to buffer pointers so that we can use it in vkCmdBindVertexBuffers. +// It's very simple, when you call drawArrays in GL2VK, simply pass a list of buffers[].bufferID +// to the command. +// Maybe not so simple, because challenge to overcome: we need to somehow pass that big ol array +// through atomicLongs. + +public class GL2VKPipeline { + + // In order to change textures, we need to bind different descriptorsets; + // this class handles the creation of these descriptor sets and binds the particular image to it. + private class TextureDescriptor { + // For the descriptor sets + private long descriptorPool = -1; + // Need several, one for each frame. + private long[] descriptorSets = null; + + + public TextureDescriptor(TextureBuffer img) { + // Must only be called when the pipeline is initiated + if (currentState == -1) { + throw new RuntimeException("TextureDescriptor must be called after pipeline has been initiated"); + } + initDescritpors(samplerBindings.size()); + updateImage(img); + } + + public long get() { + return descriptorSets[system.getFrame()]; + } + + private void initDescritpors(int layoutBindingsSize) { + try(MemoryStack stack = stackPush()) { + // Create descriptor pool + // First, we need poolsize + VkDescriptorPoolSize.Buffer poolSize = VkDescriptorPoolSize.calloc(layoutBindingsSize, stack); + + // First, the samplers. + for (int index = 0; index < layoutBindingsSize; index++) { + poolSize.get(index).type(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + poolSize.get(index).descriptorCount(vkbase.swapChainImages.size()); + index++; + } + + // Actually create the pool + VkDescriptorPoolCreateInfo poolInfo = VkDescriptorPoolCreateInfo.calloc(stack); + poolInfo.sType(VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO); + poolInfo.pPoolSizes(poolSize); + poolInfo.maxSets(vkbase.swapChainImages.size()); + LongBuffer pDescriptorPool = stack.mallocLong(1); + + if(vkCreateDescriptorPool(system.device, poolInfo, null, pDescriptorPool) != VK_SUCCESS) { + throw new RuntimeException("Failed to create descriptor pool"); + } + descriptorPool = pDescriptorPool.get(0); + + // Ok, time to create the set + // Remember there's a separate descriptorset for each frame. + // Use the pipeline's layout. Copy for each frame. + LongBuffer layouts = stack.mallocLong(vkbase.swapChainImages.size()); + for(int i = 0;i < layouts.capacity();i++) { + layouts.put(i, descriptorSetLayout); + } + + // Allocation info + VkDescriptorSetAllocateInfo allocInfo = VkDescriptorSetAllocateInfo.calloc(stack); + allocInfo.sType(VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO); + allocInfo.descriptorPool(descriptorPool); + allocInfo.pSetLayouts(layouts); + + + LongBuffer pDescriptorSets = stack.mallocLong(vkbase.swapChainImages.size()); + + if(vkAllocateDescriptorSets(vkbase.device, allocInfo, pDescriptorSets) != VK_SUCCESS) { + throw new RuntimeException("Failed to allocate descriptor sets"); + } + + // Cool! We got our descriptor sets. Put it into our cool array. + descriptorSets = new long[pDescriptorSets.capacity()]; + for (int i = 0; i < pDescriptorSets.capacity(); i++) { + descriptorSets[i] = pDescriptorSets.get(i); + } + + } + } + + private void updateImage(TextureBuffer imgBuffer) { + try(MemoryStack stack = stackPush()) { + + // Only need 1 info. + // We don't need multiple because we don't have an array of images. + VkDescriptorImageInfo.Buffer imageInfo = VkDescriptorImageInfo.calloc(1, stack); + imageInfo.imageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + imageInfo.imageView(imgBuffer.imageView); + imageInfo.sampler(imgBuffer.sampler); + + VkWriteDescriptorSet.Buffer samplerDescriptorWrite = VkWriteDescriptorSet.calloc(1, stack); + samplerDescriptorWrite.sType(VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET); + // TODO: Multiple samplers and binding the right texture to the right binding + samplerDescriptorWrite.dstBinding(0); + samplerDescriptorWrite.dstArrayElement(0); + samplerDescriptorWrite.descriptorType(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + samplerDescriptorWrite.descriptorCount(1); + samplerDescriptorWrite.pImageInfo(imageInfo); + + // Remember, multiple descriptor sets. + // Update all of them. + for (int i = 0; i < descriptorSets.length; i++) { + samplerDescriptorWrite.dstSet(descriptorSets[i]); + + vkUpdateDescriptorSets(system.device, samplerDescriptorWrite, null); + } + } + } + } + + private class PipelineState { + public PipelineState(long g, long l) { + this.graphicsPipeline = g; + this.pipelineLayout = l; + } + + public long graphicsPipeline = -1; + + // Public for use with push constants. + public long pipelineLayout = -1; + } + + private VulkanSystem system; + private VKSetup vkbase; + + public SPIRV vertShaderSPIRV = null; + public SPIRV fragShaderSPIRV = null; + + private long currentState = -1; + private HashMap states = new HashMap<>(); + + + + private long descriptorSetLayout = -1; + + // Pipeline's state + public boolean depthTestEnable = false; + public boolean depthWriteEnable = false; + + + // We assign the offsets when we add the uniforms to the pipeline, + // NOT during source code parsing (check notes in the ShaderAttribInfo.parseUniforms() method) + private int currUniformOffset = 0; + + public ShaderAttribInfo attribInfo = null; + + private HashMap gl2vkBinding = new HashMap<>(); + private HashMap attribNameToGLLocation = new HashMap<>(); + private int[] GLLocationToVKLocation = new int[1024]; + private HashMap name2UniformLocation = new HashMap<>(); + public ArrayList uniforms = new ArrayList<>(); + private ArrayList samplerBindings = new ArrayList<>(); + + // Textures are based on the OpenGL bindings, so use a standard array + private TextureDescriptor[] textures = new TextureDescriptor[4096]; + + private int boundBinding = 0; + private int totalVertexAttribsBindings = 0; + + + + + public GL2VKPipeline(VulkanSystem system) { + this.system = system; + this.vkbase = system.vkbase; + } + + // Only use this constructor for testing purposes + public GL2VKPipeline() { + } + + + private long pow(int input, int exp) { + return (long)Math.pow(input, exp); + } + + public long getHash() { + return + pow(2, depthTestEnable ? 1 : 0) * + pow(3, depthWriteEnable ? 1 : 0); + } + + public long getPipeline() { + return states.get(currentState).graphicsPipeline; + } + + public long getLayout() { + return states.get(currentState).pipelineLayout; + } + + // Same from the tutorial + private long createShaderModule(ByteBuffer spirvCode) { + try(MemoryStack stack = stackPush()) { + + VkShaderModuleCreateInfo createInfo = VkShaderModuleCreateInfo.calloc(stack); + + createInfo.sType(VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO); + createInfo.pCode(spirvCode); + + LongBuffer pShaderModule = stack.mallocLong(1); + + if(vkCreateShaderModule(vkbase.device, createInfo, null, pShaderModule) != VK_SUCCESS) { + throw new RuntimeException("Failed to create shader module"); + } + + return pShaderModule.get(0); + } + } + + + public boolean needsNewStateCreation() { + return !states.containsKey(currentState); + } + + public boolean initiated() { + return currentState != -1; + } + + // Checks if the state needs to be changed (because i.e. depth test enabled/disabled) + // and if so sets currentState and returns true. + public boolean updatedState() { + if (currentState != getHash()) { + currentState = getHash(); + return true; + } + else return false; + } + + + public void createGraphicsPipeline() { + + try(MemoryStack stack = stackPush()) { + + // Let's compile the GLSL shaders into SPIR-V at runtime using the shaderc library + // Check ShaderSPIRVUtils class to see how it can be done + if (vertShaderSPIRV == null || fragShaderSPIRV == null) { + throw new RuntimeException("Shaders must be compiled before calling createGraphicsPipeline()"); + } + + // Do the descriptorset stuff + createDescriptorLayout(); + + // Completely unnecessary code + long vertShaderModule = createShaderModule(vertShaderSPIRV.bytecode()); + long fragShaderModule = createShaderModule(fragShaderSPIRV.bytecode()); + + +// long vertShaderModule = createShaderModule(vertShaderSPIRV.bytecode()); +// long fragShaderModule = createShaderModule(fragShaderSPIRV.bytecode()); + + ByteBuffer entryPoint = stack.UTF8("main"); + + VkPipelineShaderStageCreateInfo.Buffer shaderStages = VkPipelineShaderStageCreateInfo.calloc(2, stack); + + VkPipelineShaderStageCreateInfo vertShaderStageInfo = shaderStages.get(0); + + vertShaderStageInfo.sType(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO); + vertShaderStageInfo.stage(VK_SHADER_STAGE_VERTEX_BIT); + vertShaderStageInfo.module(vertShaderModule); + vertShaderStageInfo.pName(entryPoint); + + VkPipelineShaderStageCreateInfo fragShaderStageInfo = shaderStages.get(1); + + fragShaderStageInfo.sType(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO); + fragShaderStageInfo.stage(VK_SHADER_STAGE_FRAGMENT_BIT); + fragShaderStageInfo.module(fragShaderModule); + fragShaderStageInfo.pName(entryPoint); + + // ===> VERTEX STAGE <=== + + VkPipelineVertexInputStateCreateInfo vertexInputInfo = VkPipelineVertexInputStateCreateInfo.calloc(stack); + vertexInputInfo.sType(VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO); + vertexInputInfo.pVertexBindingDescriptions(getBindingDescriptions()); + vertexInputInfo.pVertexAttributeDescriptions(getAttributeDescriptions()); + + // ===> ASSEMBLY STAGE <=== + + VkPipelineInputAssemblyStateCreateInfo inputAssembly = VkPipelineInputAssemblyStateCreateInfo.calloc(stack); + inputAssembly.sType(VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO); + inputAssembly.topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); + inputAssembly.primitiveRestartEnable(false); + + // ===> VIEWPORT & SCISSOR + + VkViewport.Buffer viewport = VkViewport.calloc(1, stack); + viewport.x(0.0f); + viewport.y(0.0f); + viewport.width(vkbase.swapChainExtent.width()); + viewport.height(vkbase.swapChainExtent.height()); + + viewport.minDepth(0.0f); + viewport.maxDepth(1.0f); + + VkRect2D.Buffer scissor = VkRect2D.calloc(1, stack); + scissor.offset(VkOffset2D.calloc(stack).set(0, 0)); + scissor.extent(vkbase.swapChainExtent); + + VkPipelineViewportStateCreateInfo viewportState = VkPipelineViewportStateCreateInfo.calloc(stack); + viewportState.sType(VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO); + viewportState.pViewports(viewport); + viewportState.pScissors(scissor); + + // ===> RASTERIZATION STAGE <=== + + VkPipelineRasterizationStateCreateInfo rasterizer = VkPipelineRasterizationStateCreateInfo.calloc(stack); + rasterizer.sType(VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO); + rasterizer.depthClampEnable(false); + rasterizer.rasterizerDiscardEnable(false); + rasterizer.polygonMode(VK_POLYGON_MODE_FILL); + rasterizer.lineWidth(1.0f); +// rasterizer.cullMode(VK_CULL_MODE_BACK_BIT); + rasterizer.cullMode(VK_CULL_MODE_NONE); +// rasterizer.frontFace(VK_FRONT_FACE_CLOCKWISE); + rasterizer.depthBiasEnable(false); + + // ===> MULTISAMPLING <=== + + VkPipelineMultisampleStateCreateInfo multisampling = VkPipelineMultisampleStateCreateInfo.calloc(stack); + multisampling.sType(VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO); + multisampling.sampleShadingEnable(false); + multisampling.rasterizationSamples(VK_SAMPLE_COUNT_1_BIT); + + VkPipelineDepthStencilStateCreateInfo depthStencil = VkPipelineDepthStencilStateCreateInfo.calloc(stack); + depthStencil.sType(VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO); + depthStencil.depthTestEnable(depthTestEnable); + depthStencil.depthWriteEnable(depthWriteEnable); + depthStencil.depthCompareOp(VK_COMPARE_OP_LESS_OR_EQUAL); + depthStencil.depthBoundsTestEnable(false); + depthStencil.minDepthBounds(0.0f); // Optional + depthStencil.maxDepthBounds(1.0f); // Optional + depthStencil.stencilTestEnable(false); + + // ===> COLOR BLENDING <=== + + VkPipelineColorBlendAttachmentState.Buffer colorBlendAttachment = VkPipelineColorBlendAttachmentState.calloc(1, stack); + colorBlendAttachment.colorWriteMask(VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT); + colorBlendAttachment.blendEnable(true); + colorBlendAttachment.srcColorBlendFactor(VK_BLEND_FACTOR_SRC_ALPHA); // Source factor + colorBlendAttachment.dstColorBlendFactor(VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA); // Destination factor + colorBlendAttachment.colorBlendOp(VK_BLEND_OP_ADD); // Blend operation for color + colorBlendAttachment.srcAlphaBlendFactor(VK_BLEND_FACTOR_ONE); // Source alpha factor + colorBlendAttachment.dstAlphaBlendFactor(VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA); // Destination alpha factor + colorBlendAttachment.alphaBlendOp(VK_BLEND_OP_ADD); // Blend operation for alpha + colorBlendAttachment.colorWriteMask(VK_COLOR_COMPONENT_R_BIT | + VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | + VK_COLOR_COMPONENT_A_BIT); // Write mask + + VkPipelineColorBlendStateCreateInfo colorBlending = VkPipelineColorBlendStateCreateInfo.calloc(stack); + colorBlending.sType(VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO); + colorBlending.logicOpEnable(false); +// colorBlending.logicOp(VK_LOGIC_OP_COPY); + colorBlending.pAttachments(colorBlendAttachment); +// colorBlending.blendConstants(stack.floats(0.0f, 0.0f, 0.0f, 0.0f)); + + // ===> PIPELINE LAYOUT CREATION <=== + + // PUSH CONSTANTS + int vertexSize = 0; + int fragmentSize = 0; + int totalSize = 0; + + // Need these just in case the offset is set to something stupidly high + int maxOffsettedSizeVertex = 0; + int maxOffsettedSizeFragment = 0; + // Now we must compile our uniforms list into this pushConstants thing + // All we really need to do is set the size. + for (GLUniform uni : uniforms) { + if (uni.vertexFragment == GLUniform.VERTEX) { + vertexSize += uni.size; + totalSize += uni.size; + if (uni.offset+uni.size > maxOffsettedSizeVertex) + maxOffsettedSizeVertex = uni.offset+uni.size; + } + if (uni.vertexFragment == GLUniform.FRAGMENT) { + fragmentSize += uni.size; + totalSize += uni.size; + if (uni.offset+uni.size > maxOffsettedSizeFragment) + maxOffsettedSizeFragment = uni.offset+uni.size; + } + } + + if (maxOffsettedSizeVertex > vertexSize) vertexSize = maxOffsettedSizeVertex; +// if (maxOffsettedSizeFragment > fragmentSize) fragmentSize = maxOffsettedSizeFragment; + if (maxOffsettedSizeFragment > totalSize) totalSize = maxOffsettedSizeFragment; + + +// vertexSize = Util.roundToMultiple8(vertexSize); + + + int limit = system.getPushConstantsSizeLimit(); + if (totalSize > limit) { + Util.emergencyExit( + "Uniform variables totals up to "+totalSize+" bytes, greater than the push constant limit of "+limit+" bytes", + "on this gpu.", + "Unfortunately, uniform sizes greater than "+limit+" bytes is not supported yet.", + "The only solution for now is to remove some of the uniforms in your shader", + "(both vertex and fragment) to reduce uniform size, I'm sorry :(" + ); + // Program will exit after this. + } + + // Here, for each uniform, we specify a pushConstant + int numBlocks = 0; + if (vertexSize > 0) numBlocks++; + if (fragmentSize > 0) numBlocks++; + VkPushConstantRange.Buffer pushConstants = VkPushConstantRange.calloc(numBlocks, stack); + + // 0: vertex + if (vertexSize > 0) { + pushConstants.get(0).offset(0); + pushConstants.get(0).size(vertexSize); + pushConstants.get(0).stageFlags(VK_SHADER_STAGE_VERTEX_BIT); + } + + // No fragment variables? Forget about push constants + // 1: fragment + if (fragmentSize > 0) { + pushConstants.get(1).offset(vertexSize); + pushConstants.get(1).size(fragmentSize); + pushConstants.get(1).stageFlags(VK_SHADER_STAGE_FRAGMENT_BIT); + } + + // Now pipeline layout info (only 1) + VkPipelineLayoutCreateInfo pipelineLayoutInfo = VkPipelineLayoutCreateInfo.calloc(stack); + pipelineLayoutInfo.sType(VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO); + if (samplerBindings.size() > 0) { + pipelineLayoutInfo.pSetLayouts(stack.longs(descriptorSetLayout)); + } + + if (numBlocks > 0) { + pipelineLayoutInfo.pPushConstantRanges(pushConstants); + } + + LongBuffer pPipelineLayout = stack.longs(VK_NULL_HANDLE); + + if(vkCreatePipelineLayout(vkbase.device, pipelineLayoutInfo, null, pPipelineLayout) != VK_SUCCESS) { + throw new RuntimeException("Failed to create pipeline layout"); + } + + + ///////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////// + + + + VkGraphicsPipelineCreateInfo.Buffer pipelineInfo = VkGraphicsPipelineCreateInfo.calloc(1, stack); + pipelineInfo.sType(VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO); + pipelineInfo.pStages(shaderStages); + pipelineInfo.pVertexInputState(vertexInputInfo); + pipelineInfo.pInputAssemblyState(inputAssembly); + pipelineInfo.pViewportState(viewportState); + pipelineInfo.pRasterizationState(rasterizer); + pipelineInfo.pMultisampleState(multisampling); + pipelineInfo.pDepthStencilState(depthStencil); + pipelineInfo.pColorBlendState(colorBlending); + pipelineInfo.layout(pPipelineLayout.get(0)); + pipelineInfo.renderPass(system.renderPass); + pipelineInfo.subpass(0); + pipelineInfo.basePipelineHandle(VK_NULL_HANDLE); + pipelineInfo.basePipelineIndex(-1); + + LongBuffer pGraphicsPipeline = stack.mallocLong(1); + + if(vkCreateGraphicsPipelines(vkbase.device, VK_NULL_HANDLE, pipelineInfo, null, pGraphicsPipeline) != VK_SUCCESS) { + throw new RuntimeException("Failed to create graphics pipeline"); + } + + // Create new state + if (states.containsKey(getHash())) { + System.out.println("WARNING Pipelines state "+getHash()+" already exists"); + } + + PipelineState state = new PipelineState(pGraphicsPipeline.get(0), pPipelineLayout.get(0)); + + states.put(getHash(), state); + + // ===> RELEASE RESOURCES <=== + vertShaderSPIRV.free(); + fragShaderSPIRV.free(); + + vkDestroyShaderModule(vkbase.device, vertShaderModule, null); + vkDestroyShaderModule(vkbase.device, fragShaderModule, null); + + vertShaderModule = -1; + fragShaderModule = -1; + } + } + + + // Hoohoohooo let's have some fun with descriptors. + // TODO: at some point, normal descriptors and not just samplers. + private void createDescriptorLayout() { + try(MemoryStack stack = stackPush()) { + + int index = 0; + int layoutBindingsSize = samplerBindings.size(); + VkDescriptorSetLayoutBinding.Buffer layoutBindings = VkDescriptorSetLayoutBinding.calloc(layoutBindingsSize, stack); + + // First, the samplers. + for (Integer binding : samplerBindings) { + layoutBindings.get(index).binding(binding); + layoutBindings.get(index).descriptorCount(1); + layoutBindings.get(index).descriptorType(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + layoutBindings.get(index).pImmutableSamplers(null); + layoutBindings.get(index).stageFlags(VK_SHADER_STAGE_FRAGMENT_BIT); + } + + // Actually, there is no next, it's just samplers for now. + + // Now let's create our thing ! + + // Create descriptor layout + VkDescriptorSetLayoutCreateInfo layoutInfo = VkDescriptorSetLayoutCreateInfo.calloc(stack); + layoutInfo.sType(VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO); + // Set to our layout bindings from above. + layoutInfo.pBindings(layoutBindings); + + LongBuffer pDescriptorSetLayout = stack.mallocLong(1); + if(vkCreateDescriptorSetLayout(system.device, layoutInfo, null, pDescriptorSetLayout) != VK_SUCCESS) { + throw new RuntimeException("Failed to create descriptor set layout"); + } + descriptorSetLayout = pDescriptorSetLayout.get(0); + + // Done! For now. + } + } + + + // Remember, everytime you create a new pipeline, you should add the currently bound texture. + public void addTextureIfAbsent(int binding, TextureBuffer img) { + if (currentState == -1) return; + + // Don't add if it already exists + if (textures[binding] == null) { + textures[binding] = new TextureDescriptor(img); + } + } + + + public long getCurrentTextureDescriptor(int texBinding) { + return textures[texBinding].get(); + } + + + public VkVertexInputBindingDescription.Buffer getBindingDescriptions() { + VkVertexInputBindingDescription.Buffer bindingDescriptions = + VkVertexInputBindingDescription.calloc(gl2vkBinding.size()); + + int i = 0; + for (VertexAttribsBinding vab : gl2vkBinding.values()) { + vab.updateBindingDescription(bindingDescriptions.get(i++)); + } + + return bindingDescriptions.rewind(); + } + + + public VkVertexInputAttributeDescription.Buffer getAttributeDescriptions() { + VkVertexInputAttributeDescription.Buffer attributeDescriptions = + VkVertexInputAttributeDescription.calloc(attribInfo.nameToLocation.size()); + + int i = 0; + for (VertexAttribsBinding vab : gl2vkBinding.values()) { + vab.updateAttributeDescriptions(attributeDescriptions, i); + i += vab.getSize(); + } + + return attributeDescriptions; + } + + + // Used when glBindBuffer is called, so that we know to create a new binding + // for our vertexattribs vulkan pipeline. This should be called in glVertexAttribPointer + // function. + public void bind(int glIndex, GraphicsBuffer buffer) { + boundBinding = glIndex; + // Automatically allocate new binding and increate binding count by one. + if (!gl2vkBinding.containsKey(boundBinding)) { + gl2vkBinding.put(glIndex, new VertexAttribsBinding(totalVertexAttribsBindings++, attribInfo)); + } + // It all flowssss. In a very complicated, spaghetti'd way. + // Tell me a better way to do it though. + // Technically the actual buffer can change during the pipeline so + // allow that to be dyncamically updated. + gl2vkBinding.get(glIndex).buffer = buffer; + } + + // This should only be used with debug mode + public void bind(int glIndex) { + bind(glIndex, null); + } + + // Create global variable so it can be cached and hence avoiding garbage collection + private ArrayList bufferArray = new ArrayList<>(); + + // This is a pretty bad solution, but efficient. + // Loop through the list, making sure the position in the array + // aligns with the myBinding value in VertexAttribsBinding. + // We just naively add an item if the list isn't big enough + public ArrayList getVKBuffers() { + for (VertexAttribsBinding binding : gl2vkBinding.values()) { + while (binding.myBinding > bufferArray.size()-1) { + bufferArray.add(0L); + } + bufferArray.set(binding.myBinding, binding.buffer.getCurrBuffer()); + } + + return bufferArray; + } + + public void vertexAttribPointer(int vklocation, int count, int type, boolean normalized, int stride, int offset) { + // Remember, a gl buffer binding of 0 means no bound buffer, + // and by default in this class, means bind() hasn't been called. + if (boundBinding == 0) { + System.err.println("BUG WARNING vertexAttribPointer called with no bound buffer."); + return; + } + gl2vkBinding.get(boundBinding).vertexAttribPointer(vklocation, count, type, normalized, stride, offset); + } + + // Not actually used but cool to have + public void vertexAttribPointer(int location) { + gl2vkBinding.get(boundBinding).vertexAttribPointer(location); + } + + public int addAttribInfo(ShaderAttribInfo attribInfo, int startIndex) { + this.attribInfo = attribInfo; + // Of course we need a way to get our locations from the name. + int count = 0; + for (Entry entry : attribInfo.nameToLocation.entrySet()) { + attribNameToGLLocation.put(entry.getKey(), startIndex); + GLLocationToVKLocation[startIndex] = entry.getValue(); + startIndex++; + count++; + } + + // Returns the iterations + return count; + } + + public int getGLAttribLocation(String name) { + if (!attribNameToGLLocation.containsKey(name)) return -1; + return attribNameToGLLocation.get(name); + } + + public int getVKAttribLocation(int glAttribLocation) { + return GLLocationToVKLocation[glAttribLocation]; + } + + public void addUniforms(ArrayList uniforms) { + for (GLUniform uniform : uniforms) { + // Assign offset to the uniform + // But only if it hasn't been manually assigned already + // (via the layout(offset=...) token in the shader). + if (uniform.offset == -1) { + uniform.offset = currUniformOffset; + } + else { + currUniformOffset = uniform.offset; + } + currUniformOffset += uniform.size; + + + // +1 because opengl objects start at index 1. + // You'll need to remember to sub one whenever you access this + // uniform arrayList. + this.uniforms.add(uniform); + name2UniformLocation.put(uniform.name, this.uniforms.size()); + } + } + + public void addSamplers(ArrayList bindings) { + for (Integer binding : bindings) { + samplerBindings.add(binding); + } + } + + // Remember we start from 1, not 0. + public GLUniform getUniform(int index) { + return uniforms.get(index-1); + } + + public int getUniformLocation(String name) { + if (!name2UniformLocation.containsKey(name)) return -1; + return name2UniformLocation.get(name); + } + + // Depricated but leave these in for the unit tests + public void compileVertex(String source) { + // vertShaderSPIRV = compileShaderFile("resources/shaders/09_shader_base.vert", VERTEX_SHADER); + attribInfo = new ShaderAttribInfo(source); + } + + public void compileFragment(String source) { +// fragShaderSPIRV = compileShaderFile("resources/shaders/09_shader_base.frag", FRAGMENT_SHADER); + } + + + public void clean() { + for (PipelineState state: states.values()) { + vkDestroyPipeline(system.device, state.graphicsPipeline, null); + vkDestroyPipelineLayout(system.device, state.pipelineLayout, null); + vkDestroyDescriptorSetLayout(system.device, descriptorSetLayout, null); + + } + } +} \ No newline at end of file diff --git a/core/src/processing/GL2VK/GL2VKShaderConverter.java b/core/src/processing/GL2VK/GL2VKShaderConverter.java new file mode 100644 index 0000000000..0ac8cfaffd --- /dev/null +++ b/core/src/processing/GL2VK/GL2VKShaderConverter.java @@ -0,0 +1,763 @@ +package processing.GL2VK; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; + +public class GL2VKShaderConverter { + public final static int VERTEX = 1; + public final static int FRAGMENT = 2; + + // Public so we can check them in tests sorry not sorry. + public HashMap vertexVaryingLocations = new HashMap<>(); + public int vertUniformSize = -1; + private int currBinding = 0; + + // Unused +// private boolean fragmentBlocked = false; +// private String blockedFragmentString = ""; + + public void reset() { + vertexVaryingLocations.clear(); + vertUniformSize = -1; + currBinding = 0; + } + + + // To convert the shader from OpenGL glsl to Vulkan GLSL: + // 1- append #version 450 if not done already + // 2- convert "attribute" keyword to "in" + // 3- Keep track and specify location for "in" variables, append "layout(location=...)" + // 4- convert "varying" keyword to "out" + // 5- Keep track and specify location for "out" variables, append "layout(location=...)" + // 6- Collect all uniform variables and put them into a block. + // 7- For the fragment shader, append "layout(offset=...)" from the uniform block size of + // the vertex shader (unfortunately means we have no choice but to compile vertex programs first) + // 8- Replace all instances of uniform names with the structure name e.g. u_time -> uni.u_time + // 9- replace gl_FragColor to gl2vkFragColor and add layout(location = 0) out vec4 gl2vkFragColor + // in fragment shader. + + // NOTES: we'll need to add some sort of cache to remember varying variables from the vertex + // to fragment shader. Unfortunately, shaders are compiled at a stage where the pipeline (or "program") + // isn't present, so it's the best option we've got. + // + // This is unfortunately one of the disadvantages of writing a GL to VK interpreter; best write your + // programs in pure vulkan to begin with. + // + // Methods like "clearCache" upon program creation and specifying which shader is vertex and which is + // fragment can help with ensuring the shaders are linked correctly. + // + // It's also important to add proper error checking techniques (perhaps put "glLinkProgram" to use) + // to properly check if the cache from vertex to fragment, or fragment to vertex were properly found, + // and if not, halt the program and give a GL2VK warning. + // + // On second thought, looking through the requirements, we'll need to compile vertex first then fragment. + // There's no other way, unless we recompile fragment, which is so much overhead. + // Yeah, I can really see why a gl-to-vk layer is a bad idea now. + // And why OpenGL has a big overhead. + // + // Also, remember to ignore comments, and switch on ignoremode if we're in a comment block /* */ + public String convert(String source, int type) throws RuntimeException { + + + // C1: remove comments + source = removeComments(source); + + + // C2: convert "attrib" to "in" VERTEX ONLY + if (type == VERTEX) source = attribute2In(source); + + // C3: convert "varying" to out VERTEX ONLY + if (type == VERTEX) source = varying2Out(source); + + // C4: convert "varying" to in FRAGMENT ONLY + // May throw exception if a vertex shader hasn't been compiled. + if (type == FRAGMENT) source = varying2In(source); + + source = spaceOutSymbols(source); + + // C5: convert uniforms (put into block, replace instances, add offset to fragment) + source = convertUniforms(source, type); + + // C6: replace texture2D to texture. + source = textureXDToTexture(source); + + // C7: convert gl_FragColor to gl2vk_FragColor and add out variable. + if (type == FRAGMENT) source = replaceFragOut(source); + + // C8: append "gl_Position.y *= -1.0;" at the end of the main body + if (type == VERTEX) source = invertY(source); + + // C9: Finally, append the version + source = appendVersion(source); + + source = despaceSemicolons(source); + + // Print converted source (debug) + if (false) { + String[] sourceLines = source.split("\n"); + for (int i = 0; i < sourceLines.length; i++) { + System.out.println((i+1)+" "+sourceLines[i]); + + } + } + + return source; + } + + // Let's just steal UniformParser's hasType method lol + public static boolean hasType(String val) { + return UniformParser.hasType(val); + } + + // Let's steal that too + public static String removeSemicolon(String input) { + return UniformParser.removeSemicolon(input); + } + + + // TODO: also filter comment blocks /* */ + private String filterComments(String line) { + // Single line + int index = line.indexOf("//"); + if (index != -1) { + return line.substring(0, index); + } + return line; + } + + public static String despaceSemicolons(String line) { + line = line.replace(" ;", ";"); + return line; + } + + + public static String spaceOutSymbols(String line) { +// line = line.replaceAll("\\+(?!=)", " + "); + line = line.replace(",", " , "); + line = line.replace("==", " == "); + line = line.replace("+=", " += "); + line = line.replace("-=", " -= "); + line = line.replace("*=", " *= "); + line = line.replace("/=", " /= "); + line = line.replace(">=", " >= "); + line = line.replace("<=", " <= "); + line = line.replace("!=", " != "); + line = line.replace("++", " ++ "); + line = line.replace("--", " -- "); +// line = line.replaceAll(">(?!=)", " > "); // Detect > but not >= +// line = line.replaceAll("<(?!=)", " < "); // Detect < but not <= + line = line.replaceAll("!(?!=)", " ! "); // Detect > but not >= + line = line.replaceAll("&(?!=)", " & "); // single & + line = line.replaceAll("\\|(?!=)", " | "); // single | + line = line.replaceAll("-(?!=)", " - "); + line = line.replaceAll("\\*(?!=)", " * "); + line = line.replaceAll("\\/(?!=)", " / "); + line = line.replace("(", " ( "); + line = line.replace(")", " ) "); + line = line.replace("{", "{ "); + line = line.replace("}", " }"); + line = line.replace(";", " ;"); + + // Regex failed on me so let's just program it ourselves for == + String nline = ""; + char[] arr = line.toCharArray(); + for (int i = 0; i < arr.length; i++) { + try { + // -=, += etc + if ( arr[i-1] != '=' + && arr[i-1] != '+' + && arr[i-1] != '-' + && arr[i-1] != '*' + && arr[i-1] != '/' + && arr[i-1] != '&' + && arr[i-1] != '|' + && arr[i-1] != '<' + && arr[i-1] != '>' + + && arr[i] == '=' + && arr[i+1] != '=') { + nline += " = "; + } + // ++ + else if (arr[i-1] != '+' + + && arr[i] == '+' + && arr[i+1] != '=' + && arr[i+1] != '+' + ) { + nline += " + "; + } + // -- + else if (arr[i-1] != '-' + + && arr[i] == '-' + && arr[i+1] != '=' + && arr[i+1] != '-' + ) { + nline += " - "; + } + + // space out dot (we want it for uniform.xyz but not for 0.0) + else if ( (arr[i-1] < '0' + || arr[i-1] > '9') + + && arr[i] == '.' + ) + + { + nline += " ."; + } + + else nline += arr[i]; + + } + catch (RuntimeException e) { + nline += arr[i]; + } + } + line = nline; + return line; + } + + + // All methods are public so that they can be tested + public String removeComments(String source) { + // Split it up + String[] lines = source.split("\n"); + // Remove comments + // And then reconstruct it + String reconstructed = ""; + boolean blockComment = false; + for (String line : lines) { + // Single line + String lineb = filterComments(line)+"\n"; + + // Multiline + for (int i = 0; i < lineb.length(); i++) { + try { + if ((lineb.charAt(i) == '/') && (lineb.charAt(i+1) == '*')) { + blockComment = true; + } + else if ((lineb.charAt(i-2) == '*') && (lineb.charAt(i-1) == '/')) { + blockComment = false; + } + } + catch (IndexOutOfBoundsException e) { + + } + + if (!blockComment) { + reconstructed += lineb.charAt(i); + } + } + } + + + return reconstructed; + } + + // Very easy + public String appendVersion(String source) { + if (!source.contains("#version 450")) { + source = "#version 450\n"+source; + } + source = source.replace("#version 0null", ""); + return source; + } + + // IMPORTANT NODE: vertex shaders only + public String attribute2In(String source) { + int inLocationIndex = 0; + String[] lines = source.split("\n"); + String reconstructed = ""; + for (String line : lines) { + // Variable names could easily contain the word "attribute" in them. + // That's why we go through and actually check the attribute token + + + String[] elements = line.replaceAll("\t", "").trim().split(" "); + String reconstructedLine = ""; + for (int i = 0; i < elements.length; i++) { + // Try catch block so I don't need to put index checks everywhere + try { + // Now look for "attribute" followed by a type. + if (elements[i].equals("attribute") && hasType(elements[i+1])) { + // Append layout variable + reconstructedLine += "layout(location = "+inLocationIndex+") "; + + // Replace "attribute" with "in" + reconstructedLine += "in "; + inLocationIndex++; + } + else { + reconstructedLine += elements[i]+" "; + } + } + catch (RuntimeException e) { + // Just continue with original line + reconstructedLine = line; + break; + } + } + reconstructed += reconstructedLine; + + reconstructed += "\n"; + } + return reconstructed; + } + + + // IMPORTANT NODE: vertex shaders only + // Very similar to attribute2In but we're replacing "varying" instead. + public String varying2Out(String source) { + int outLocationIndex = 0; + String[] lines = source.split("\n"); + String reconstructed = ""; + for (String line : lines) { + + + String[] elements = line.replaceAll("\t", "").trim().split(" "); + String reconstructedLine = ""; + for (int i = 0; i < elements.length; i++) { + // Try catch block so I don't need to put index checks everywhere + try { + // Now look for "varying" followed by a type. + + if (elements[i].equals("varying") && hasType(elements[i+1])) { + // Name should come right after the type + String name = removeSemicolon(elements[i+2]); + + vertexVaryingLocations.put(name, outLocationIndex); + + reconstructedLine += "layout(location = "+outLocationIndex+") "; + + reconstructedLine += "out "; + + outLocationIndex++; + } + else { + reconstructedLine += elements[i]+" "; + } + } + catch (RuntimeException e) { + // Just continue with original line + reconstructedLine = line; + break; + } + } + reconstructed += reconstructedLine; + + reconstructed += "\n"; + } + return reconstructed; + } + + + // IMPORTANT NODE: fragment shaders only + public String varying2In(String source) throws RuntimeException { + String[] lines = source.split("\n"); + String reconstructed = ""; + + for (String line : lines) { + + + String[] elements = line.replaceAll("\t", "").trim().split(" "); + String reconstructedLine = ""; + for (int i = 0; i < elements.length; i++) { + // Try catch block so I don't need to put index checks everywhere + try { + // Now look for "varying" followed by a type. + + if (elements[i].equals("varying") && hasType(elements[i+1])) { + // Name should come right after the type + String name = removeSemicolon(elements[i+2]); + + if (!vertexVaryingLocations.containsKey(name)) { + // We used to have blocking functionality for when a fragment shader is + // compiled before a vertex. + // Too complicated, too unnecessary, let's just warn the user. + Util.emergencyExit( + "glCompileShader()", + "One of the following possibilities occured: ", + "- Syntax error in your vertex/fragment shader", + "- Fragment shader was compiled before your vertex shader", + "- Fragment shader was compiled out-of-order from your vertex shader.", + " E.g. compile vert0, compile frag1, compile frag0.", + "In GL2VK, you must compile your vertex/fragment shader pair in order,", + "one after the other." + ); + break; + } + + reconstructedLine += "layout(location = "+vertexVaryingLocations.get(name)+") "; + + reconstructedLine += "in "; + } + else { + reconstructedLine += elements[i]+" "; + } + } + catch (RuntimeException e) { + // Just continue with original line + reconstructedLine = line; + break; + } + } + + // Unused scrapped feature. +// if (block) { +// fragmentBlocked = true; +// blockedFragmentString = original; +// throw new RuntimeException(); +// } + reconstructed += reconstructedLine; + + reconstructed += "\n"; + } + return reconstructed; + } + + + + + public String convertUniforms(String source, int type) { + // Quick check before doing any processing + if (type == FRAGMENT && vertUniformSize == -1) { + // Unused auto-fixing feature +// fragmentBlocked = true; +// blockedFragmentString = source; + + Util.emergencyExit( + "Uniform error", + "One of the following possibilities occured: ", + "- Syntax error in your vertex/fragment shader", + "- Fragment shader was compiled before your vertex shader", + "- Fragment shader was compiled out-of-order from your vertex shader.", + " E.g. compile vert0, compile frag1, compile frag0.", + "In GL2VK, you must compile your vertex/fragment shader pair in order,", + "one after the other." + ); + +// throw new RuntimeException(); + } + else if (type == VERTEX) { + // Mark as 0 to mark that the vertex has run + // and to begin counting. + vertUniformSize = 0; + } + + // Step 1: get uniforms, erase them + String[] lines = source.split("\n"); + + String reconstructed1 = ""; + + HashSet uniformsSet = new HashSet<>(); + ArrayList uniforms = new ArrayList<>(); + for (String line : lines) { + + String reconstructedLine = ""; + String[] elements = line.replaceAll("\t", "").trim().split(" "); + for (int i = 0; i < elements.length; i++) { + // Try catch block so I don't need to put index checks everywhere + try { + + if (elements[i].equals("uniform") && hasType(elements[i+1])) { + // Erase the line + reconstructedLine = ""; + + // Samplers of course use descriptor sets, not push constants. + // For that we put it into a different array. + if (elements[i+1].equals("sampler1D") || elements[i+1].equals("sampler2D") || elements[i+1].equals("sampler3D")) { + // FIX: texture is now a keyword and can't be a variable name. + // Rename the texture keyword. We'll rename the usage of that variable later in the program too. + if (elements[i+2].equals("texture")) { + reconstructedLine = "layout(binding = "+currBinding+") uniform "+elements[i+1]+" texturegl2vk;"; + // Of course, we should really check to avoid duplicate texturegl2vk variables but its whatever. + } + else { + // rather than add it to lists to pass it to a later state, we + // can simply modify it now since samplers aren't in a block. + // Very easy, just append layout(binding = x) + reconstructedLine += "layout(binding = "+currBinding+") "+line; + } + + currBinding++; + + // No more here. + break; + } + else { + // Annnnd let's just slap on element[1 and 2] because that + // should be the type and name (with semicolon) + uniforms.add(elements[i+1]+" "+elements[i+2]+";"); + + // Keep track of size on vertex shader + if (type == VERTEX) vertUniformSize += UniformParser.typeToSize(elements[i+1]); + + // simultaneously populate the hashset + uniformsSet.add(removeSemicolon(elements[i+2])); + + break; + } + } + else { + reconstructedLine += elements[i]+" "; + } + } + catch (RuntimeException e) { + // Just continue with original line + reconstructedLine = line; + } + } + reconstructed1 += reconstructedLine; + + reconstructed1 += "\n"; + } + + /////////////////////////////////////////////////////////////////////////////////////// + + // uniform block code + // Only bother if we actually have any uniforms + int blockLines = 0; + if (!uniforms.isEmpty()) { + // And now append the block at the top. + String block = "layout(push_constant) uniform gltovkuniforms_struct {"; + blockLines++; + boolean once = true; + for (String u : uniforms) { + // For a fragment shader we need to add offset to go into + // the constant push fragment range. + if (once && type == FRAGMENT) { + block += "\n layout(offset="+vertUniformSize+") "+u; + once = false; + } + else block += "\n "+u; + + blockLines++; + } + block += "\n} gltovkuniforms;\n"; + blockLines++; + + // Add our uniform block to reconstructed + reconstructed1 = block+reconstructed1; + } + + + //////////////////////////////////////////////////////////////////////////////// + + String reconstructed2 = ""; + // Let's do it again + lines = reconstructed1.split("\n"); + // TODO: variable arrays + // Skip the uniform block. + int index = 0; + int bracketDepth = 0; + for (String line : lines) { + // If we're in the block, continue. + if (index < blockLines) { + reconstructed2 += line+"\n"; + index++; + continue; + } + + String reconstructedLine = ""; + +// line = spaceOutSymbols(line); + + + String[] elements = line.replaceAll("\t", "").trim().split(" "); + + for (int i = 0; i < elements.length; i++) { + // Try catch block so I don't need to put index checks everywhere + try { + + if (uniformsSet.contains(elements[i])) { + // Replace with gltovkuniforms.[varname] + reconstructedLine += "gltovkuniforms."+elements[i]; + } + // FIX: rename texture uniforms to texturegl2vk + else if (elements[i].equals("texture")) { + reconstructedLine += "texturegl2vk"; + } + else { + reconstructedLine += elements[i]+" "; + } + + } + catch (RuntimeException e) { + // Just continue with original line + reconstructedLine = line; + } + } + reconstructed2 += reconstructedLine; + + reconstructed2 += "\n"; + + index++; + } + + + return reconstructed2; + } + + // Just steal countChars from UniformParser lmao + public static int countChars(String line, String c) { + return UniformParser.countChars(line, c); + } + + + + // OpenGL's "up" is in the negative direction, and down is positive. In Vulkan, + // this is inversed. Easiest way to solve this problem is simply to append the line + // "gl_Position.y *= -1.;" at the end of the main() function. + // Find the main function, find the closing bracket, and just above it, add + // "gl_Position.y *= -1.;". + + // This time for this one we're not reconstructing it, but instead finding the index of + // the end of the main function, and then inserting the line directly into source. + public String invertY(String source) { + String[] lines = source.split("\n"); + + boolean inMainBody = false; + int bracketDepth = 0; + int insertionIndex = -1; + + int index = 0; + + // Step 1: find main. + for (String originalLine : lines) { + +// String line = spaceOutSymbols(originalLine); + + String[] elements = originalLine.replaceAll("\t", "").trim().split(" "); + for (int i = 0; i < elements.length; i++) { + try { + + if (!inMainBody) { + // Main found, now to find the end of the body. + if (elements[i].equals("void") && (elements[i+1].equals("main") || elements[i+1].equals("main("))) { + inMainBody = true; + } + // There should be a { bracket right after main thus kickstarting our + // counting. + } + // Now in the main body, count brackets, once we reach 0, we know we've exited. + else { + if (elements[i].equals("{")) { + bracketDepth++; + } + if (elements[i].equals("}")) { + bracketDepth--; + // Bracket depth 0? We've reached the end of the body. + // Insertion point aquired. + if (bracketDepth == 0) { + insertionIndex = index; +// System.out.println(insertionIndex); + break; + } + } + } + + + } + catch (RuntimeException e) { + + } + } + + // Quit early if we've already found our index + if (insertionIndex != -1) { + break; + } + + index += originalLine.length()+1; +// System.out.println(originalLine+" "+originalLine.length()); + } + + // At this point we should have our insertion point + // If not, uhoh. + + if (insertionIndex == -1) { + System.err.println("Shader converter invertY: Couldn't find main body."); + } + else { + // Insert our famous line. + String insertLine = "\n gl_Position.y *= -1.0;\n"; + source = source.substring(0, insertionIndex)+insertLine+source.substring(insertionIndex); + } + + return source; + } + + + + // IMPORTANT: fragment only + public String textureXDToTexture(String source) { + + String[] lines = source.split("\n"); + String reconstructed = ""; + + for (String line : lines) { + +// line = spaceOutSymbols(line); + + String[] elements = line.replaceAll("\t", "").trim().split(" "); + String reconstructedLine = ""; + for (int i = 0; i < elements.length; i++) { + // No try/catch block this time because no exceptions should happen there + if (elements[i].equals("texture1D") || elements[i].equals("texture2D") || elements[i].equals("texture3D")) { + reconstructedLine += "texture "; + } + else { + reconstructedLine += elements[i]+" "; + } + } + reconstructed += reconstructedLine; + + reconstructed += "\n"; + } + + return reconstructed; + } + + + + public String replaceFragOut(String source) { + + String[] lines = source.split("\n"); + String reconstructed = ""; + + for (String line : lines) { + +// line = spaceOutSymbols(line); + + String[] elements = line.replaceAll("\t", "").trim().split(" "); + String reconstructedLine = ""; + for (int i = 0; i < elements.length; i++) { + // No try/catch block this time because no exceptions should happen there + + // This time just one condition + if (elements[i].equals("gl_FragColor")) { + reconstructedLine += "gl2vk_FragColor "; + } + else { + reconstructedLine += elements[i]+" "; + } + } + reconstructed += reconstructedLine; + + reconstructed += "\n"; + } + + // Then, we need to add + // layout(location = 0) out vec4 gl2vk_FragColor; + // Let's just add it at the top + if (!reconstructed.contains("layout(location = 0) out vec4 gl2vk_FragColor;")) { + reconstructed = "layout(location = 0) out vec4 gl2vk_FragColor;\n"+reconstructed; + } + + return reconstructed; + } + +} \ No newline at end of file diff --git a/core/src/processing/GL2VK/GLExample.java b/core/src/processing/GL2VK/GLExample.java new file mode 100644 index 0000000000..583c24d9a3 --- /dev/null +++ b/core/src/processing/GL2VK/GLExample.java @@ -0,0 +1,753 @@ +package processing.GL2VK; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +//import org.joml.*; +//import org.joml.Math; + +public class GLExample { + + // ======= CLASSES ======= // +// private static class Vertex { +// private static final int SIZEOF = (2 + 3) * Float.BYTES; +// +// private Vector2fc pos; +// private Vector3fc color; +// +// public Vertex(float x, float y, float r, float g, float b) { +// this.pos = new Vector2f(x, y); +// this.color = new Vector3f(r,g,b); +// } +// +// // NOTES FOR VERTEX BINDING: +// // in OpenGL, bindBuffer specifies which buffer index to bind. +// // This index can be used in vulkan's bindingDescription.binding(index) +// // If we want an interleaved buffer: then +// // bindBuffer(1); +// // vertexAttribPointer("pos" ... ) +// // bindBuffer(1) +// // vertexAttribPointer("color" ... ) +// // +// // If we want a separate buffer: +// // bindBuffer(1); +// // vertexAttribPointer("pos" ... ) +// // bindBuffer(2) +// // vertexAttribPointer("color" ... ) +// // +// // All we do is create a new bindingdescription whenever a new +// // buffer is bound when vertexAttribPointer is called. +// } +// +// public static void main(String[] args) { +// try { +// GL2VK gl = new GL2VK(1200, 800); +//// triangles(gl); +//// trianglesSeparate(gl); +//// throttleTest(gl); +//// indices(gl); +//// indicesUniform(gl); +//// coolIndicies(gl); +// +// } +// catch (Exception e) { +// e.printStackTrace(); +// System.exit(1); +// } +// } +// +// +// private static void createIndicesSquare(ByteBuffer vertexBuffer, ByteBuffer colorBuffer, ByteBuffer indexBuffer) { +// +// vertexBuffer.order(ByteOrder.LITTLE_ENDIAN); +// colorBuffer.order(ByteOrder.LITTLE_ENDIAN); +// indexBuffer.order(ByteOrder.LITTLE_ENDIAN); +// +//// {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}}, +//// {{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}}, +//// {{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}}, +//// {{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}} +// vertexBuffer.putFloat(-0.5f); +// vertexBuffer.putFloat(-0.5f); +// colorBuffer.putFloat(1f); +// colorBuffer.putFloat(0f); +// colorBuffer.putFloat(0f); +// +// vertexBuffer.putFloat(0.5f); +// vertexBuffer.putFloat(-0.5f); +// colorBuffer.putFloat(0f); +// colorBuffer.putFloat(1f); +// colorBuffer.putFloat(0f); +// +// vertexBuffer.putFloat(0.5f); +// vertexBuffer.putFloat(0.5f); +// colorBuffer.putFloat(0f); +// colorBuffer.putFloat(0f); +// colorBuffer.putFloat(1f); +// +// vertexBuffer.putFloat(-0.5f); +// vertexBuffer.putFloat(0.5f); +// colorBuffer.putFloat(1f); +// colorBuffer.putFloat(0f); +// colorBuffer.putFloat(1f); +// +// indexBuffer.putShort((short)0); +// indexBuffer.putShort((short)1); +// indexBuffer.putShort((short)2); +// indexBuffer.putShort((short)2); +// indexBuffer.putShort((short)3); +// indexBuffer.putShort((short)0); +// +// vertexBuffer.rewind(); +// colorBuffer.rewind(); +// indexBuffer.rewind(); +// } +// +// private static void createIndicesSquareProcessingShader(ByteBuffer vertexBuffer, ByteBuffer colorBuffer, ByteBuffer indexBuffer) { +// +// vertexBuffer.order(ByteOrder.LITTLE_ENDIAN); +// colorBuffer.order(ByteOrder.LITTLE_ENDIAN); +// indexBuffer.order(ByteOrder.LITTLE_ENDIAN); +// +//// {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}}, +//// {{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}}, +//// {{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}}, +//// {{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}} +// vertexBuffer.putFloat(-0.5f); +// vertexBuffer.putFloat(-0.5f); +// vertexBuffer.putFloat(0f); +// vertexBuffer.putFloat(1f); +// +// colorBuffer.putFloat(1f); +// colorBuffer.putFloat(0f); +// colorBuffer.putFloat(0f); +// colorBuffer.putFloat(1f); +// +// vertexBuffer.putFloat(0.5f); +// vertexBuffer.putFloat(-0.5f); +// vertexBuffer.putFloat(0f); +// vertexBuffer.putFloat(1f); +// +// colorBuffer.putFloat(0f); +// colorBuffer.putFloat(1f); +// colorBuffer.putFloat(0f); +// colorBuffer.putFloat(1f); +// +// vertexBuffer.putFloat(0.5f); +// vertexBuffer.putFloat(0.5f); +// vertexBuffer.putFloat(0f); +// vertexBuffer.putFloat(1f); +// +// colorBuffer.putFloat(0f); +// colorBuffer.putFloat(0f); +// colorBuffer.putFloat(1f); +// colorBuffer.putFloat(1f); +// +// vertexBuffer.putFloat(-0.5f); +// vertexBuffer.putFloat(0.5f); +// vertexBuffer.putFloat(0f); +// vertexBuffer.putFloat(1f); +// +// colorBuffer.putFloat(1f); +// colorBuffer.putFloat(0f); +// colorBuffer.putFloat(1f); +// colorBuffer.putFloat(1f); +// +// indexBuffer.putShort((short)0); +// indexBuffer.putShort((short)1); +// indexBuffer.putShort((short)2); +// indexBuffer.putShort((short)2); +// indexBuffer.putShort((short)3); +// indexBuffer.putShort((short)0); +// +// vertexBuffer.rewind(); +// colorBuffer.rewind(); +// indexBuffer.rewind(); +// } +// +// +// +// +// public static void coolIndicies(GL2VK gl) { +// +// // Create the data +// ByteBuffer vertexBuffer = ByteBuffer.allocate(Float.BYTES * 6 * 4); +// ByteBuffer colorBuffer = ByteBuffer.allocate(Float.BYTES * 6 * 4); +// ByteBuffer indexBuffer = ByteBuffer.allocate(Short.BYTES * 6); +// createIndicesSquareProcessingShader(vertexBuffer, colorBuffer, indexBuffer); +// +// // Gen buffers +// IntBuffer out = IntBuffer.allocate(3); +// gl.glGenBuffers(3, out); +// int glVertBuff = out.get(0); +// int glColBuff = out.get(1); +// int glIndexBuff = out.get(2); +// +// +// // Create our gpu program +// int program = gl.glCreateProgram(); +// int vertShader = gl.glCreateShader(GL2VK.GL_VERTEX_SHADER); +// int fragShader = gl.glCreateShader(GL2VK.GL_FRAGMENT_SHADER); +// +// // Shader source +// gl.glShaderSource(vertShader, Util.readFile("src/processing/opengl/shaders/ColorVert.glsl")); +// gl.glShaderSource(fragShader, Util.readFile("src/processing/opengl/shaders/ColorFrag.glsl")); +// // Compile the shaders +// gl.glCompileShader(vertShader); +// gl.glCompileShader(fragShader); +// // Check shaders +// IntBuffer compileStatus = IntBuffer.allocate(1); +// gl.glGetShaderiv(vertShader, GL2VK.GL_COMPILE_STATUS, compileStatus); +// if (compileStatus.get(0) == GL2VK.GL_FALSE) { +// System.out.println(gl.glGetShaderInfoLog(vertShader)); +// System.exit(1); +// } +// gl.glGetShaderiv(fragShader, GL2VK.GL_COMPILE_STATUS, compileStatus); +// if (compileStatus.get(0) == GL2VK.GL_FALSE) { +// System.out.println(gl.glGetShaderInfoLog(fragShader)); +// System.exit(1); +// } +// // Attach the shaders +// gl.glAttachShader(program, vertShader); +// gl.glAttachShader(program, fragShader); +// // Don't need em anymore +// gl.glDeleteShader(vertShader); +// gl.glDeleteShader(fragShader); +// +// gl.glLinkProgram(program); +// +// +// // Setup up attribs +// int position = gl.glGetAttribLocation(program, "position"); +// int color = gl.glGetAttribLocation(program, "color"); +// +// gl.glBindBuffer(GL2VK.GL_VERTEX_BUFFER, glVertBuff); +// gl.glBufferData(GL2VK.GL_VERTEX_BUFFER, vertexBuffer.capacity(), vertexBuffer, 0); +// gl.glVertexAttribPointer(position, 4, GL2VK.GL_FLOAT, false, 4*4, 0); +// gl.glBindBuffer(GL2VK.GL_VERTEX_BUFFER, glColBuff); +// gl.glBufferData(GL2VK.GL_VERTEX_BUFFER, colorBuffer.capacity(), colorBuffer, 0); +// gl.glVertexAttribPointer(color, 4, GL2VK.GL_FLOAT, false, 4*4, 0); +// +// gl.glBindBuffer(GL2VK.GL_INDEX_BUFFER, glIndexBuff); +// gl.glBufferData(GL2VK.GL_INDEX_BUFFER, indexBuffer.capacity(), indexBuffer, 0); +// +// gl.glUseProgram(program); +// +// int transformMatrix = gl.getUniformLocation(program, "transformMatrix"); +// if (transformMatrix == -1) { +// System.out.println("Missing transformMatrix!"); +// System.exit(1); +// } +// +// +// boolean multithreaded = true; +// int threadIndex = 0; +// float qtime = 0f; +// +// Matrix4f transform = new Matrix4f(); +// +// while (!gl.shouldClose()) { +// transform.identity(); +// transform.rotateZ((qtime * Math.toRadians(90))); +//// transform.lookAt(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f); +//// transform.perspective((float) Math.toRadians(45), +//// (float)1200 / (float)00, 0.1f, 10.0f); +//// transform.m11(transform.m11() * -1); +// +// qtime += 0.02f; +// +// gl.beginRecord(); +// +// +// if (multithreaded) gl.selectNode((threadIndex++)%gl.getNodesCount()); +// else gl.selectNode(0); +// +//// ByteBuffer buff = ByteBuffer.allocateDirect(64); +//// buff.order(ByteOrder.LITTLE_ENDIAN); +//// transform.get(buff); +// +// FloatBuffer buff = ByteBuffer.allocateDirect(64).asFloatBuffer(); +// transform.get(buff); +// +// gl.glUniformMatrix4fv(transformMatrix, 1, false, buff); +// +// gl.glBindBuffer(GL2VK.GL_INDEX_BUFFER, glIndexBuff); +// +// gl.glDrawElements(0, 6, GL2VK.GL_UNSIGNED_SHORT, 0); +// gl.endRecord(); +// +//// frameWait(); +// } +// gl.close(); +// } +// +// +// +// public static void indicesUniform(GL2VK gl) { +// +// // Create the data +// ByteBuffer vertexBuffer = ByteBuffer.allocate(Float.BYTES * 6 * 2); +// ByteBuffer colorBuffer = ByteBuffer.allocate(Float.BYTES * 6 * 3); +// ByteBuffer indexBuffer = ByteBuffer.allocate(Short.BYTES * 6); +// createIndicesSquare(vertexBuffer, colorBuffer, indexBuffer); +// +// // Gen buffers +// IntBuffer out = IntBuffer.allocate(3); +// gl.glGenBuffers(3, out); +// int glVertBuff = out.get(0); +// int glColBuff = out.get(1); +// int glIndexBuff = out.get(2); +// +// +// // Create our gpu program +// int program = gl.glCreateProgram(); +// int vertShader = gl.glCreateShader(GL2VK.GL_VERTEX_SHADER); +// int fragShader = gl.glCreateShader(GL2VK.GL_FRAGMENT_SHADER); +// +// // Shader source +// gl.glShaderSource(vertShader, Util.readFile("resources/shaders/uniform.vert")); +// gl.glShaderSource(fragShader, Util.readFile("resources/shaders/uniform.frag")); +// // Compile the shaders +// gl.glCompileVKShader(vertShader); +// gl.glCompileVKShader(fragShader); +// // Check shaders +// IntBuffer compileStatus = IntBuffer.allocate(1); +// gl.glGetShaderiv(vertShader, GL2VK.GL_COMPILE_STATUS, compileStatus); +// if (compileStatus.get(0) == GL2VK.GL_FALSE) { +// System.out.println(gl.glGetShaderInfoLog(vertShader)); +// System.exit(1); +// } +// gl.glGetShaderiv(fragShader, GL2VK.GL_COMPILE_STATUS, compileStatus); +// if (compileStatus.get(0) == GL2VK.GL_FALSE) { +// System.out.println(gl.glGetShaderInfoLog(fragShader)); +// System.exit(1); +// } +// // Attach the shaders +// gl.glAttachShader(program, vertShader); +// gl.glAttachShader(program, fragShader); +// // Don't need em anymore +// gl.glDeleteShader(vertShader); +// gl.glDeleteShader(fragShader); +// +// gl.glLinkProgram(program); +// +// +// // Setup up attribs +// int position = gl.glGetAttribLocation(program, "inPosition"); +// int color = gl.glGetAttribLocation(program, "inColor"); +// +// gl.glBindBuffer(GL2VK.GL_VERTEX_BUFFER, glVertBuff); +// gl.glBufferData(GL2VK.GL_VERTEX_BUFFER, vertexBuffer.capacity(), vertexBuffer, 0); +// gl.glVertexAttribPointer(position, 2, GL2VK.GL_FLOAT, false, 2*4, 0); +// gl.glBindBuffer(GL2VK.GL_VERTEX_BUFFER, glColBuff); +// gl.glBufferData(GL2VK.GL_VERTEX_BUFFER, colorBuffer.capacity(), colorBuffer, 0); +// gl.glVertexAttribPointer(color, 3, GL2VK.GL_FLOAT, false, 3*4, 0); +// +// gl.glBindBuffer(GL2VK.GL_INDEX_BUFFER, glIndexBuff); +// gl.glBufferData(GL2VK.GL_INDEX_BUFFER, indexBuffer.capacity(), indexBuffer, 0); +// +// +// +// +// gl.glUseProgram(program); +// +// boolean multithreaded = true; +// int threadIndex = 0; +// +// double qtime = 0d; +// +// +// int u_pos = gl.getUniformLocation(program, "u_pos"); +// int u_brightness = gl.getUniformLocation(program, "u_brightness"); +// int u_pos_secondary = gl.getUniformLocation(program, "u_pos_secondary"); +// int u_r = gl.getUniformLocation(program, "u_r"); +// int u_g = gl.getUniformLocation(program, "u_g"); +// int u_b = gl.getUniformLocation(program, "u_b"); +// int extra_red = gl.getUniformLocation(program, "extra_red"); +// +// if (u_pos == -1) System.out.println("UHOH u_pos -1"); +// if (u_pos_secondary == -1) System.out.println("UHOH u_pos_secondary -1"); +// if (u_r == -1) System.out.println("UHOH u_r -1"); +// +// +// while (!gl.shouldClose()) { +// gl.beginRecord(); +// +// if (multithreaded) gl.selectNode((threadIndex++)%gl.getNodesCount()); +// else gl.selectNode(0); +// +// qtime += 0.1d; +// +// gl.glBindBuffer(GL2VK.GL_INDEX_BUFFER, glIndexBuff); +// +// gl.glUniform1f(u_r, 1f); +// gl.glUniform1f(u_g, 1f); +// gl.glUniform1f(u_b, 1f); +//// gl.glUniform1f(u_brightness, (float)Math.sin(qtime)*0.5f+0.5f); +// gl.glUniform1f(u_brightness, (float)Math.sin(qtime)*0.5f+0.5f); +// gl.glUniform1f(extra_red, 1f); +// +// +// gl.glUniform2f(u_pos, (float)Math.sin(qtime)*0.5f, (float)Math.cos(qtime)*0.5f); +//// gl.glUniform2f(u_pos_secondary, 0, (float)Math.cos(qtime*2.238f)*0.2f); +//// gl.glUniform2f(u_pos_secondary, 0, (float)Math.cos(qtime*2.238f)*0.2f); +// +// gl.glDrawElements(0, 6, GL2VK.GL_UNSIGNED_SHORT, 0); +// gl.endRecord(); +// +//// frameWait(); +// } +// gl.close(); +// } +// +// +// +// // Draw a square with indicies +// public static void indices(GL2VK gl) { +// +// // Create the data +// ByteBuffer vertexBuffer = ByteBuffer.allocate(Float.BYTES * 6 * 2); +// ByteBuffer colorBuffer = ByteBuffer.allocate(Float.BYTES * 6 * 3); +// ByteBuffer indexBuffer = ByteBuffer.allocate(Short.BYTES * 6); +// createIndicesSquare(vertexBuffer, colorBuffer, indexBuffer); +// +// // Gen buffers +// IntBuffer out = IntBuffer.allocate(3); +// gl.glGenBuffers(3, out); +// int glVertBuff = out.get(0); +// int glColBuff = out.get(1); +// int glIndexBuff = out.get(2); +// +// +// // Create our gpu program +// int program = gl.glCreateProgram(); +// int vertShader = gl.glCreateShader(GL2VK.GL_VERTEX_SHADER); +// int fragShader = gl.glCreateShader(GL2VK.GL_FRAGMENT_SHADER); +// +// // Shader source +// gl.glShaderSource(vertShader, Util.readFile("resources/shaders/shader.vert")); +// gl.glShaderSource(fragShader, Util.readFile("resources/shaders/shader.frag")); +// // Compile the shaders +// gl.glCompileVKShader(vertShader); +// gl.glCompileVKShader(fragShader); +// // Attach the shaders +// gl.glAttachShader(program, vertShader); +// gl.glAttachShader(program, fragShader); +// // Don't need em anymore +// gl.glDeleteShader(vertShader); +// gl.glDeleteShader(fragShader); +// +// gl.glLinkProgram(program); +// +// +// // Setup up attribs +// int position = gl.glGetAttribLocation(program, "inPosition"); +// int color = gl.glGetAttribLocation(program, "inColor"); +// +// gl.glBindBuffer(GL2VK.GL_VERTEX_BUFFER, glVertBuff); +// gl.glBufferData(GL2VK.GL_VERTEX_BUFFER, vertexBuffer.capacity(), vertexBuffer, 0); +// gl.glVertexAttribPointer(position, 2, GL2VK.GL_FLOAT, false, 2*4, 0); +// gl.glBindBuffer(GL2VK.GL_VERTEX_BUFFER, glColBuff); +// gl.glBufferData(GL2VK.GL_VERTEX_BUFFER, colorBuffer.capacity(), colorBuffer, 0); +// gl.glVertexAttribPointer(color, 3, GL2VK.GL_FLOAT, false, 3*4, 0); +// +// gl.glBindBuffer(GL2VK.GL_INDEX_BUFFER, glIndexBuff); +// gl.glBufferData(GL2VK.GL_INDEX_BUFFER, indexBuffer.capacity(), indexBuffer, 0); +// +// +// +// +// gl.glUseProgram(program); +// +// boolean multithreaded = true; +// int threadIndex = 0; +// +// while (!gl.shouldClose()) { +// gl.beginRecord(); +// +// if (multithreaded) gl.selectNode((threadIndex++)%gl.getNodesCount()); +// else gl.selectNode(0); +// +// gl.glBindBuffer(GL2VK.GL_INDEX_BUFFER, glIndexBuff); +// gl.glDrawElements(0, 6, GL2VK.GL_UNSIGNED_SHORT, 0); +// gl.endRecord(); +// +// frameWait(); +// } +// gl.close(); +// } +// +// +// +// +// private static void createVertices(Vertex[] buffer) { +// +// final float TRIANGLE_SIZE = 0.04f; +// +// int l = buffer.length; +// for (int i = 0; i < l; i+=3) { +// float r = (float)Math.random(); +// float g = (float)Math.random(); +// float b = (float)Math.random(); +// +// float x1 = (float)Math.random()*2f-1f; +// float y1 = (float)Math.random()*2f-1f; +// float x2 = x1+TRIANGLE_SIZE; +// float y2 = y1; +// float x3 = x1+TRIANGLE_SIZE/2f; +// float y3 = y1+TRIANGLE_SIZE; +// buffer[i] = new Vertex(x1,y1,r,g,b); +// if (i+1 < l) buffer[i+1] = new Vertex(x2,y2,r,g,b); +// if (i+2 < l) buffer[i+2] = new Vertex(x3,y3,r,g,b); +// } +// } +// +// private static void memcpy(ByteBuffer buffer, Vertex[] vertices) { +// buffer.order(ByteOrder.LITTLE_ENDIAN); +// for(Vertex vertex : vertices) { +// buffer.putFloat(vertex.pos.x()); +// buffer.putFloat(vertex.pos.y()); +// +// buffer.putFloat(vertex.color.x()); +// buffer.putFloat(vertex.color.y()); +// buffer.putFloat(vertex.color.z()); +// } +// } +// +// +// private static void copyVertex(ByteBuffer buffer, Vertex[] vertices) { +// buffer.order(ByteOrder.LITTLE_ENDIAN); +// for(Vertex vertex : vertices) { +// buffer.putFloat(vertex.pos.x()); +// buffer.putFloat(vertex.pos.y()); +// } +// } +// +// +// private static void copyColor(ByteBuffer buffer, Vertex[] vertices) { +// buffer.order(ByteOrder.LITTLE_ENDIAN); +// for(Vertex vertex : vertices) { +// buffer.putFloat(vertex.color.x()); +// buffer.putFloat(vertex.color.y()); +// buffer.putFloat(vertex.color.z()); +// } +// } +// +// +// public static void triangles(GL2VK gl) { +// Vertex[] vertices = new Vertex[1000]; +// createVertices(vertices); +// +// // Gen buffers +// IntBuffer out = IntBuffer.allocate(1); +// gl.glGenBuffers(1, out); +// int vertexBuffer = out.get(0); +// +// // Create our gpu program +// int program = gl.glCreateProgram(); +// int vertShader = gl.glCreateShader(GL2VK.GL_VERTEX_SHADER); +// int fragShader = gl.glCreateShader(GL2VK.GL_FRAGMENT_SHADER); +// +// // Shader source +// gl.glShaderSource(vertShader, Util.readFile("resources/shaders/shader.vert")); +// gl.glShaderSource(fragShader, Util.readFile("resources/shaders/shader.frag")); +// // Compile the shaders +// gl.glCompileVKShader(vertShader); +// gl.glCompileVKShader(fragShader); +// // Attach the shaders +// gl.glAttachShader(program, vertShader); +// gl.glAttachShader(program, fragShader); +// // Don't need em anymore +// gl.glDeleteShader(vertShader); +// gl.glDeleteShader(fragShader); +// +// gl.glLinkProgram(program); +// +// +// +// int size = vertices.length*Vertex.SIZEOF; +// ByteBuffer buff = ByteBuffer.allocate(size); +// +// gl.glBindBuffer(GL2VK.GL_VERTEX_BUFFER, vertexBuffer); +// +// // Setup up attribs +// int position = gl.glGetAttribLocation(program, "inPosition"); +// int color = gl.glGetAttribLocation(program, "inColor"); +// gl.glVertexAttribPointer(position, 2, GL2VK.GL_FLOAT, false, 5*4, 0); +// gl.glVertexAttribPointer(color, 3, GL2VK.GL_FLOAT, false, 5*4, 2*4); +// +// gl.glUseProgram(program); +// +// +// boolean multithreaded = false; +// int threadIndex = 0; +// +// while (!gl.shouldClose()) { +// // Buffer data +// createVertices(vertices); +// buff.rewind(); +// memcpy(buff, vertices); +// gl.glBindBuffer(GL2VK.GL_VERTEX_BUFFER, vertexBuffer); +// gl.glBufferData(GL2VK.GL_VERTEX_BUFFER, size, buff, 0); +// +// gl.beginRecord(); +// +// if (multithreaded) gl.selectNode((threadIndex++)%gl.getNodesCount()); +// else gl.selectNode(0); +// +// gl.glDrawArrays(0, 0, vertices.length); +// gl.endRecord(); +// +// frameWait(); +// } +// gl.close(); +// } +// +// +// +// public static void trianglesSeparate(GL2VK gl) { +// Vertex[] vertices = new Vertex[1000]; +// createVertices(vertices); +// +// // Gen buffers +// IntBuffer out = IntBuffer.allocate(2); +// gl.glGenBuffers(2, out); +// int vertexBuffer = out.get(0); +// int colorBuffer = out.get(1); +// +// // Create our gpu program +// int program = gl.glCreateProgram(); +// int vertShader = gl.glCreateShader(GL2VK.GL_VERTEX_SHADER); +// int fragShader = gl.glCreateShader(GL2VK.GL_FRAGMENT_SHADER); +// +// // Shader source +// gl.glShaderSource(vertShader, Util.readFile("resources/shaders/shader.vert")); +// gl.glShaderSource(fragShader, Util.readFile("resources/shaders/shader.frag")); +// // Compile the shaders +// gl.glCompileVKShader(vertShader); +// gl.glCompileVKShader(fragShader); +// // Attach the shaders +// gl.glAttachShader(program, vertShader); +// gl.glAttachShader(program, fragShader); +// // Don't need em anymore +// gl.glDeleteShader(vertShader); +// gl.glDeleteShader(fragShader); +// +// gl.glLinkProgram(program); +// +// +// +// ByteBuffer vertexBuff = ByteBuffer.allocate(2 * Float.BYTES * vertices.length); +// ByteBuffer colorBuff = ByteBuffer.allocate(3 * Float.BYTES * vertices.length); +// +// +// // Setup up attribs +// int position = gl.glGetAttribLocation(program, "inPosition"); +// int color = gl.glGetAttribLocation(program, "inColor"); +// +// gl.glBindBuffer(GL2VK.GL_VERTEX_BUFFER, vertexBuffer); +// gl.glVertexAttribPointer(position, 2, GL2VK.GL_FLOAT, false, 2*4, 0); +// gl.glBindBuffer(GL2VK.GL_VERTEX_BUFFER, colorBuffer); +// gl.glVertexAttribPointer(color, 3, GL2VK.GL_FLOAT, false, 3*4, 0); +// +// gl.glUseProgram(program); +// +// +// boolean multithreaded = false; +// int threadIndex = 0; +// +// while (!gl.shouldClose()) { +// // Buffer vertices +// createVertices(vertices); +// vertexBuff.rewind(); +// copyVertex(vertexBuff, vertices); +// gl.glBindBuffer(GL2VK.GL_VERTEX_BUFFER, vertexBuffer); +// gl.glBufferData(GL2VK.GL_VERTEX_BUFFER, 2 * Float.BYTES * vertices.length, vertexBuff, 0); +// +// colorBuff.rewind(); +// copyColor(colorBuff, vertices); +// gl.glBindBuffer(GL2VK.GL_VERTEX_BUFFER, colorBuffer); +// gl.glBufferData(GL2VK.GL_VERTEX_BUFFER, 3 * Float.BYTES * vertices.length, colorBuff, 0); +//// +//// vertexBuff.rewind(); +//// +//// while (vertexBuff.hasRemaining()) { +//// System.out.println(vertexBuff.getFloat()); +//// } +// +// gl.beginRecord(); +// +// if (multithreaded) gl.selectNode((threadIndex++)%gl.getNodesCount()); +// else gl.selectNode(0); +// +// gl.glDrawArrays(0, 0, vertices.length); +// gl.endRecord(); +// +// frameWait(); +// } +// gl.close(); +// } +// +// +// +// private static void frameWait() { +// try { +// Thread.sleep(16); +// } catch (InterruptedException e) { +// } +// } +// +// +// public static void throttleTest(GL2VK gl) { +// final int PARTS = 5; +// Vertex[] vertices = new Vertex[3]; +// int[] vertexBuffer = new int[PARTS]; +// createVertices(vertices); +// +// // Gen buffers +// IntBuffer out = IntBuffer.allocate(PARTS); +// gl.glGenBuffers(PARTS, out); +// vertexBuffer = out.array(); +// +// int buffindex = 0; +// +// int size = vertices.length*Vertex.SIZEOF; +// ByteBuffer buff = ByteBuffer.allocate(size); +// +// for (int i = 0; i < PARTS; i++) { +// // Buffer data +// createVertices(vertices); +// buff.rewind(); +// memcpy(buff, vertices); +// +// gl.glBindBuffer(GL2VK.GL_VERTEX_BUFFER, vertexBuffer[i]); +// gl.glBufferData(GL2VK.GL_VERTEX_BUFFER, size, buff, 0); +// } +// +// boolean multithreaded = true; +// +// while (!gl.shouldClose()) { +// +// gl.beginRecord(); +// +// // Throttle +// for (int i = 0; i < 10000; i++) { +// if (multithreaded) gl.selectNode((i/10)%gl.getNodesCount()); +// else gl.selectNode(0); +// +// gl.glBindBuffer(GL2VK.GL_VERTEX_BUFFER, vertexBuffer[buffindex]); +// gl.glDrawArrays(0, 0, vertices.length); +// } +// +// buffindex++; +// if (buffindex >= PARTS) buffindex = 0; +// +// +// gl.endRecord(); +//// frameWait(); +// } +// gl.close(); +// } + +} diff --git a/core/src/processing/GL2VK/GLShader.java b/core/src/processing/GL2VK/GLShader.java new file mode 100644 index 0000000000..c644cc2559 --- /dev/null +++ b/core/src/processing/GL2VK/GLShader.java @@ -0,0 +1,49 @@ +package processing.GL2VK; + +import java.util.ArrayList; + +import processing.GL2VK.ShaderSPIRVUtils.SPIRV; + +//Shaders aren't actually anything significant, they're really temporary data structures +// to create a vulkan pipeline. +public class GLShader { + private String source = ""; + public boolean successfulCompile = false; + public int type; + public SPIRV spirv = null; + public String log = ""; + + // This constructor is mostly for convenient testing. + public GLShader(String source) { + setSource(source); + } + + // Use for vertex shaders only. See notes in glCompileShader + // for why we're oddly putting this here. + public ShaderAttribInfo attribInfo = null; + + // Once the vert and frag shaders are linked it will + // be combined into one ArrayList + public ArrayList uniforms = new ArrayList<>(); + + public GLShader(int type) { + this.type = type; + } + + public void setUniforms(ArrayList uniforms) { + this.uniforms = uniforms; + for (GLUniform u : uniforms) { + // I could and should do it the proper way to + // ensure GL_VERTEX_SHADER is the same meaning for GLUniform + // class but let's be real; it's an int with 2 different values. + u.vertexFragment = type; + } + } + + public String getSource() { + return source; + } + public void setSource(String source) { + this.source = source; + } +} \ No newline at end of file diff --git a/core/src/processing/GL2VK/GLUniform.java b/core/src/processing/GL2VK/GLUniform.java new file mode 100644 index 0000000000..30f1cf6a03 --- /dev/null +++ b/core/src/processing/GL2VK/GLUniform.java @@ -0,0 +1,42 @@ +package processing.GL2VK; + +import static org.lwjgl.vulkan.VK10.VK_SHADER_STAGE_FRAGMENT_BIT; +import static org.lwjgl.vulkan.VK10.VK_SHADER_STAGE_VERTEX_BIT; + +public class GLUniform { + + public static final int VERTEX = 1; + public static final int FRAGMENT = 2; + + public String name; + public int size = -1; + public int offset = -1; + // Vertex or fragment + public int vertexFragment = 0; + + public boolean isSampler = false; + + public GLUniform(String name, int size, int offset) { + this.name = name; + this.size = size; + this.offset = offset; + } + + // Pain + public GLUniform(String name, int size) { + this.name = name; + this.size = size; + } + + public int getVkType() { + if (vertexFragment == VERTEX) { + return VK_SHADER_STAGE_VERTEX_BIT; + } + else if (vertexFragment == FRAGMENT) { + return VK_SHADER_STAGE_FRAGMENT_BIT; + } + else { + return VK_SHADER_STAGE_VERTEX_BIT; + } + } +} \ No newline at end of file diff --git a/core/src/processing/GL2VK/GraphicsBuffer.java b/core/src/processing/GL2VK/GraphicsBuffer.java new file mode 100644 index 0000000000..97a9d33ef3 --- /dev/null +++ b/core/src/processing/GL2VK/GraphicsBuffer.java @@ -0,0 +1,749 @@ +package processing.GL2VK; + +import static org.lwjgl.system.MemoryStack.stackPush; +import static org.lwjgl.vulkan.VK10.VK_BUFFER_USAGE_INDEX_BUFFER_BIT; +import static org.lwjgl.vulkan.VK10.VK_BUFFER_USAGE_TRANSFER_DST_BIT; +import static org.lwjgl.vulkan.VK10.VK_BUFFER_USAGE_TRANSFER_SRC_BIT; +import static org.lwjgl.vulkan.VK10.VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; +import static org.lwjgl.vulkan.VK10.VK_MEMORY_HEAP_DEVICE_LOCAL_BIT; +import static org.lwjgl.vulkan.VK10.VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; +import static org.lwjgl.vulkan.VK10.VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; +import static org.lwjgl.vulkan.VK10.vkDestroyBuffer; +import static org.lwjgl.vulkan.VK10.vkFreeMemory; +import static org.lwjgl.vulkan.VK10.vkMapMemory; +import static org.lwjgl.vulkan.VK10.vkUnmapMemory; +import static org.lwjgl.vulkan.VK10.VK_NULL_HANDLE; +import static org.lwjgl.vulkan.VK10.vkCmdCopyBuffer; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.LongBuffer; +import java.nio.ShortBuffer; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.nio.Buffer; +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.nio.IntBuffer; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkBufferCopy; + + +// ChatGPT response to an optimised buffering approach: +//Ah, I see what you're asking now! In Vulkan, you cannot directly "buffer" data during rendering within a VkRenderPass instance in the same way you might in some higher-level graphics APIs. However, you can achieve similar functionality through the use of dynamic buffers and descriptor sets. Here’s how that works: +// +//Dynamic Buffers in Vulkan +//Dynamic Vertex Buffers: +// +//You can create a vertex buffer that allows for dynamic updates. +//Use buffer updates (e.g., vkMapMemory followed by writing data) to change the contents of the buffer. +//Using Descriptor Sets: +// +//Create a descriptor set that points to the buffer. +//Update the descriptor set as needed during rendering. +//Steps to Buffer Data Dynamically +//Create Dynamic Buffers: +// +//When creating your vertex buffer, allocate enough space to allow for dynamic updates. +//Use VK_BUFFER_USAGE_VERTEX_BUFFER_BIT and VK_BUFFER_USAGE_TRANSFER_DST_BIT when creating the buffer. +//Map Buffer Memory: +// +//Use vkMapMemory to access the buffer memory and write your vertex data into it. +//void* data; +//vkMapMemory(device, vertexBufferMemory, 0, bufferSize, 0, &data); +//memcpy(data, newVertexData, sizeof(newVertexData)); +//vkUnmapMemory(device, vertexBufferMemory); +//Update Descriptor Sets: +// +//If using descriptor sets, update them to point to the new data. +//Call vkUpdateDescriptorSets to update the descriptor set with new buffer information. +//Bind Buffers and Draw: +// +//In your command buffer, bind the vertex buffer and draw as usual. +//vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexBuffer, offsets); +//vkCmdDraw(commandBuffer, vertexCount, 1, 0, 0); +//Considerations +//Performance: Dynamic updates to buffers during rendering can impact performance. It's usually better to batch updates and minimize the frequency of changes if possible. +//Synchronization: Be cautious of synchronization issues when updating buffers while rendering. Use fences and semaphores appropriately. +//Conclusion +//While you cannot directly buffer data inside a VkRenderPass instance, you can achieve dynamic updates to your vertex data by using dynamic buffers and updating descriptor sets appropriately. This allows for flexible rendering scenarios where vertex data can change during the rendering process. +// +//If you have any more questions or need clarification on specific parts, feel free to ask! + + + +public class GraphicsBuffer { + + + private int globalInstance = 0; + + // Is 0 or 1 + private static int frame = 0; + private final static int MAX_INSTANCES = 128; + + private long[] buffers = new long[MAX_INSTANCES*2]; + private volatile long[] bufferMemory = new long[MAX_INSTANCES*2]; + // the bufferMemory list is at risk; if we createBufferAuto while a bufferData process is happening, + // this could cause bad things. + // So let's have a buncha booleans to specify if it's safe to use + private AtomicBoolean[] safeToUpdateBuffer = new AtomicBoolean[MAX_INSTANCES*2]; + + private long[] stagingBuffers = new long[MAX_INSTANCES*2]; + private long[] stagingBufferMemory = new long[MAX_INSTANCES*2]; + + private int[] bufferSize = new int[MAX_INSTANCES*2]; + + private boolean retainedMode = false; + private boolean mapped = false; + public boolean indexBuffer = false; + + private class DeleteEntry { + public long buffer = 0L; + public long mem = 0L; + public int tmr = -1; + + public DeleteEntry(long b, long m) { + buffer = b; + mem = m; + tmr = 6; + } + } + + // Buffers may be in use by GPU mid-frame. + // queue for deletion next frame. + private ArrayList deleteQueue = new ArrayList<>(); + + public ShortBuffer indexInstantAccessBuffer = null; + + private static VulkanSystem system; + private static VKSetup vkbase; + + public GraphicsBuffer(VulkanSystem s) { + system = s; + vkbase = s.vkbase; + } + + // Debug mode constructor + public GraphicsBuffer() { + } + + { + for (int i = 0; i < buffers.length; i++) { + buffers[i] = -1; + bufferMemory[i] = -1; + stagingBuffers[i] = -1; + stagingBufferMemory[i] = -1; + safeToUpdateBuffer[i] = new AtomicBoolean(true); + } + } + + public static void setFrame(int f) { + frame = f; + } + + + // Releases any previous buffers and creates a buffer IF + // - There's no previous buffer + // - Buffer size != new size. + // NOT THREAD SAFE HERE + public void createBufferAuto(int size, int vertexIndexUsage, boolean retainedMode) { + + // TO BE SAFE: + // bufferData is done in separate threads (for immediate mode). + // Specifically, bufferMemory is at risk here. + // Let's NOT do any operations until we know it's safe to do so. + // While this looks bad, this is only used in very very rare cases, and + // even when it does happen, it should only happen once per vkbuffer. + int count = 0; + while (safeToUpdateBuffer[actualInst(globalInstance)].get() == false) { + // busy wait + if (count > 9999999) { + System.err.println("BUG WARNING createBufferAuto: looplock'd waiting for buffer instance "+globalInstance+" to be safe."); + System.exit(1); + } + count++; + } + if (count > 0) { + System.out.println("createBufferAuto: Stall for "+count); + } + + if (vertexIndexUsage == VK_BUFFER_USAGE_INDEX_BUFFER_BIT) { + indexBuffer = true; + } + + // Delete old buffers +// destroy(globalInstance); + + // Create new one + if (retainedMode) { + if (buffers[globalInstance] == -1 || size > bufferSize[globalInstance]) { + scheduleDestroy(globalInstance); + createBufferRetainedMode(size, vertexIndexUsage, globalInstance); + this.retainedMode = true; + } + } + else { + // One for each frame + if (buffers[globalInstance] == -1 || size > bufferSize[globalInstance] + || buffers[globalInstance+MAX_INSTANCES] == -1 || size > bufferSize[globalInstance+MAX_INSTANCES]) { + scheduleDestroy(globalInstance); + createBufferImmediateMode(size, vertexIndexUsage, globalInstance); + createBufferImmediateMode(size, vertexIndexUsage, globalInstance+MAX_INSTANCES); + this.retainedMode = false; + } + } + } + + private int actualInst(int instance) { + if (this.retainedMode) { + return instance; + } + else { + return instance + (frame*MAX_INSTANCES); + } + } + + private void destroyScheduledBuffers() { + for (DeleteEntry e : deleteQueue) { + e.tmr--; + if (e.tmr == 0) { + if (e.buffer != -1) { + vkDestroyBuffer(system.device, e.buffer, null); + e.buffer = -1; + } + if (e.mem != -1) { + vkFreeMemory(system.device, e.mem, null); + e.mem = -1; + } + } + } + } + + public static int bufferCount = 0; + + + // Creates a buffer without allocating any data + private void createBufferImmediateMode(int size, int usage, int inst) { + // If in debug mode, just assign a dummy value + if (system == null) { + this.buffers[inst] = (long)(Math.random()*100000.); + + return; + } + + try(MemoryStack stack = stackPush()) { + + // Not from anywhere, just alloc pointers we can use + // to get back from createbuffer method + LongBuffer pBuffer = stack.mallocLong(1); + LongBuffer pBufferMemory = stack.mallocLong(1); + + + // Buffer which is usable by both CPU and GPU + vkbase.createBuffer(size, + usage, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + pBuffer, + pBufferMemory); + + // Pointer variables now populated + + // GraphicsBuffedr object, set with our new pointer variables. + buffers[inst] = pBuffer.get(0); + bufferMemory[inst] = pBufferMemory.get(0); + bufferSize[inst] = size; + + } + bufferCount++; + } + + + // Needs to be called when it + public long getCurrBuffer() { + if (globalInstance == 0) globalInstance = 1; + + // Problem: we need to know whether we're doing retained or immediate. + // We can quickly check stagingBuffer == -1 for immediate, != -1 for retained + if (stagingBuffers[actualInst(globalInstance-1)] != -1) { + // Retained + return stagingBuffers[actualInst(globalInstance-1)]; + } + else { + // Immediate + return buffers[actualInst(globalInstance-1)]; + } + } + + + public void reset() { + globalInstance = 0; + + // Clear scheduled-for-deletion buffers. + destroyScheduledBuffers(); + } + + // NOT THREAD SAFE + private void createBufferRetainedMode(int size, int usage, int instance) { + // If in debug mode, just assign a dummy value + if (system == null) { + this.buffers[(instance)] = (long)(Math.random()*100000.); + + return; + } + + try(MemoryStack stack = stackPush()) { + + // Not from anywhere, just alloc pointers we can use + // to get back from createbuffer method + LongBuffer pBuffer = stack.mallocLong(1); + LongBuffer pBufferMemory = stack.mallocLong(1); + + + // Buffer which is usable by both CPU and GPU + vkbase.createBuffer(size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | usage, + VK_MEMORY_HEAP_DEVICE_LOCAL_BIT, + pBuffer, + pBufferMemory); + + // Pointer variables now populated + + // GraphicsBuffedr object, set with our new pointer variables. + buffers[(instance)] = pBuffer.get(0); + bufferMemory[(instance)] = pBufferMemory.get(0); + bufferSize[(instance)] = size; + } + + + // STAGING BUFFER + try(MemoryStack stack = stackPush()) { + LongBuffer pBuffer = stack.mallocLong(1); + LongBuffer pBufferMemory = stack.mallocLong(1); + vkbase.createBuffer(size, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | usage, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + pBuffer, + pBufferMemory); + + this.stagingBuffers[(instance)] = pBuffer.get(0); + this.stagingBufferMemory[(instance)] = pBufferMemory.get(0); + } + bufferCount++; + } + + + + + + + public void destroy(int instance) { + // If debug mode enabled + if (system == null) return; + + if (buffers[(instance)] != -1 && bufferMemory[(instance)] != -1) { + vkDestroyBuffer(system.device, buffers[(instance)], null); + vkFreeMemory(system.device, bufferMemory[(instance)], null); + } +// bufferCount--; + } + + public void destroy() { + for (int i = 0; i < buffers.length; i++) { + if (buffers[i] != -1 && bufferMemory[i] != -1) { + vkDestroyBuffer(system.device, buffers[i], null); + vkFreeMemory(system.device, bufferMemory[i], null); + } + } + } + + public void scheduleDestroy(int instance) { + deleteQueue.add(new DeleteEntry(buffers[(instance)], bufferMemory[(instance)])); + deleteQueue.add(new DeleteEntry(buffers[(instance+MAX_INSTANCES)], bufferMemory[(instance+MAX_INSTANCES)])); + } + + public boolean mapped() { + return mapped; + } + + public ByteBuffer map(int instance) { + mapped = true; + + + + // Problem: we need to know whether we're doing retained or immediate. + // We can quickly check stagingBuffer == -1 for immediate, != -1 for retained + if (stagingBufferMemory[actualInst(instance)] != -1) { + // Retained + if (indexBuffer) { + indexInstantAccessBuffer = mapShort(bufferSize[actualInst(instance)], stagingBufferMemory[actualInst(instance)]); + unmap(stagingBufferMemory[actualInst(instance)]); + } + return mapByte(bufferSize[actualInst(instance)], stagingBufferMemory[actualInst(instance)]); + } + else { + // Immediate + if (indexBuffer) { + indexInstantAccessBuffer = mapShort(bufferSize[actualInst(instance)], bufferMemory[actualInst(instance)]); + unmap(bufferMemory[actualInst(instance)]); + } + return mapByte(bufferSize[actualInst(instance)], bufferMemory[actualInst(instance)]); + } + } + + // For a very specific purpose + // Just alt+shift+h it, i cant be bothered explaining. + public ShortBuffer mapShort(int instance) { + mapped = true; + if (stagingBufferMemory[actualInst(instance)] != -1) { + // Retained + return mapShort(bufferSize[actualInst(instance)], stagingBufferMemory[actualInst(instance)]); + } + else { + // Immediate + return mapShort(bufferSize[actualInst(instance)], bufferMemory[actualInst(instance)]); + } + } + + // TODO: Make multithreaded + public static ByteBuffer mapByte(int size, long mem) { + try(MemoryStack stack = stackPush()) { + + + // alloc pointer for our data + PointerBuffer pointer = stack.mallocPointer(1); + vkMapMemory(system.device, mem, 0, size, 0, pointer); + + // Here instead of some mem copy function we can just + // copy each and every byte of buffer. + ByteBuffer datato = pointer.getByteBuffer(0, size); + + return datato; + } + } + + public static FloatBuffer mapFloat(int size, long mem) { + try(MemoryStack stack = stackPush()) { + // alloc pointer for our data + PointerBuffer pointer = stack.mallocPointer(1); + vkMapMemory(system.device, mem, 0, size, 0, pointer); + + // Here instead of some mem copy function we can just + // copy each and every byte of buffer. + FloatBuffer datato = pointer.getFloatBuffer(0, size/Float.BYTES); + + return datato; + } + } + + public static ShortBuffer mapShort(int size, long mem) { + try(MemoryStack stack = stackPush()) { + // alloc pointer for our data + PointerBuffer pointer = stack.mallocPointer(1); + vkMapMemory(system.device, mem, 0, size, 0, pointer); + + // Here instead of some mem copy function we can just + // copy each and every byte of buffer. + ShortBuffer datato = pointer.getShortBuffer(0, size/Short.BYTES); + + return datato; + } + } + + public static IntBuffer mapInt(int size, long mem) { + try(MemoryStack stack = stackPush()) { + // alloc pointer for our data + PointerBuffer pointer = stack.mallocPointer(1); + vkMapMemory(system.device, mem, 0, size, 0, pointer); + + // Here instead of some mem copy function we can just + // copy each and every byte of buffer. + IntBuffer datato = pointer.getIntBuffer(0, size/Integer.BYTES); + + return datato; + } + } + + public static LongBuffer mapLong(int size, long mem) { + try(MemoryStack stack = stackPush()) { + // alloc pointer for our data + PointerBuffer pointer = stack.mallocPointer(1); + vkMapMemory(system.device, mem, 0, size, 0, pointer); + + // Here instead of some mem copy function we can just + // copy each and every byte of buffer. + LongBuffer datato = pointer.getLongBuffer(0, size/Integer.BYTES); + + return datato; + } + } + + public static void unmap(long mem) { + vkUnmapMemory(system.device, mem); + } + + public void increaseInstance() { + globalInstance++; + } + + public int getInstance() { + return globalInstance; + } + + public void unmap(int instance) { + // Problem: we need to know whether we're doing retained or immediate. + // We can quickly check stagingBuffer == -1 for immediate, != -1 for retained + if (stagingBufferMemory[actualInst(instance)] != -1) { + // Retained + unmap(stagingBufferMemory[actualInst(instance)]); + mapped = false; + } + else { + // Immediate + unmap(bufferMemory[actualInst(instance)]); + mapped = false; + } + } + + ///////////////////////////////////////////////////// + // TODO: version where memory is constantly unmapped + + // IMMEDIATE MODE METHODS TO BE USED IN MULTITHREADING + public void bufferDataImmediate(ByteBuffer data, int size, int instance) { + // If debug mode enabled + if (system == null) return; + + safeToUpdateBuffer[actualInst(instance)].set(false); + + long mem = bufferMemory[actualInst(instance)]; + + ByteBuffer datato = mapByte(size, mem); + + try { + datato.rewind(); + data.rewind(); + while (datato.hasRemaining()) { + datato.put(data.get()); + } + datato.rewind(); + } + catch (BufferOverflowException e) { + // Ignore and continue. + } + catch (BufferUnderflowException e) { + // Ignore and continue. + } + + unmap(mem); + safeToUpdateBuffer[actualInst(instance)].set(true); + } + + public void bufferDataImmediate(FloatBuffer data, int size, int instance) { + // If debug mode enabled + if (system == null) return; + + safeToUpdateBuffer[actualInst(instance)].set(false); + + long mem = bufferMemory[actualInst(instance)]; + + FloatBuffer datato = mapFloat(size, mem); + + try { + datato.rewind(); + data.rewind(); + while (datato.hasRemaining()) { + datato.put(data.get()); + } + datato.rewind(); + } + catch (BufferOverflowException e) { + // Ignore and continue. + } + catch (BufferUnderflowException e) { + // Ignore and continue. + } + + unmap(mem); + safeToUpdateBuffer[actualInst(instance)].set(true); + } + + public void bufferDataImmediate(ShortBuffer data, int size, int instance) { + // If debug mode enabled + if (system == null) return; + + safeToUpdateBuffer[actualInst(instance)].set(false); + + long mem = bufferMemory[actualInst(instance)]; + + ShortBuffer datato = mapShort(size, mem); + + + try { + datato.rewind(); + data.rewind(); + while (datato.hasRemaining()) { + datato.put(data.get()); + } + datato.rewind(); + } + catch (BufferOverflowException e) { + // Ignore and continue. + } + catch (BufferUnderflowException e) { + // Ignore and continue. + } + + if (indexBuffer) { + indexInstantAccessBuffer = datato; + } + + + unmap(mem); + safeToUpdateBuffer[actualInst(instance)].set(true); + } + + public void bufferDataImmediate(IntBuffer data, int size, int instance) { + // If debug mode enabled + if (system == null) return; + + safeToUpdateBuffer[actualInst(instance)].set(false); + + long mem = bufferMemory[actualInst(instance)]; + + IntBuffer datato = mapInt(size, mem); + + try { + datato.rewind(); + data.rewind(); + while (datato.hasRemaining()) { + datato.put(data.get()); + } + datato.rewind(); + } + catch (BufferOverflowException e) { + // Ignore and continue. + } + catch (BufferUnderflowException e) { + // Ignore and continue. + } + + unmap(mem); + safeToUpdateBuffer[actualInst(instance)].set(true); + } + + public void bufferDataImmediate(LongBuffer data, int size, int instance) { + // If debug mode enabled + if (system == null) return; + + safeToUpdateBuffer[actualInst(instance)].set(false); + + long mem = bufferMemory[actualInst(instance)]; + + LongBuffer datato = mapLong(size, mem); + + try { + datato.rewind(); + data.rewind(); + while (datato.hasRemaining()) { + datato.put(data.get()); + } + datato.rewind(); + } + catch (BufferOverflowException e) { + // Ignore and continue. + } + catch (BufferUnderflowException e) { + // Ignore and continue. + } + + + unmap(mem); + safeToUpdateBuffer[actualInst(instance)].set(true); + } + + + + + + + + + + //////////////////////////////////////////////////////////////// + // Retained mode + // NOT TO BE USED IN MULTITHREADING + + public void bufferDataRetained(ByteBuffer data, int size, int instance) { + // If debug mode enabled + if (system == null) return; + + ByteBuffer datato = mapByte(size, stagingBuffers[actualInst(instance)]); + datato.rewind(); + data.rewind(); + while (datato.hasRemaining()) { + datato.put(data.get()); + } + datato.rewind(); + unmap(stagingBuffers[actualInst(instance)]); + + vkbase.copyBufferAndWait(stagingBuffers[actualInst(instance)], buffers[actualInst(instance)], size); + } + + public void bufferDataRetained(FloatBuffer data, int size, int instance) { + // If debug mode enabled + if (system == null) return; + + FloatBuffer datato = mapFloat(size, stagingBuffers[actualInst(instance)]); + + datato.rewind(); + data.rewind(); + while (datato.hasRemaining()) { + datato.put(data.get()); + } + datato.rewind(); + + unmap(stagingBuffers[actualInst(instance)]); + + vkbase.copyBufferAndWait(stagingBuffers[actualInst(instance)], buffers[actualInst(instance)], size); + } + + public void bufferDataRetained(ShortBuffer data, int size, int instance) { + // If debug mode enabled + if (system == null) return; + + ShortBuffer datato = mapShort(size, stagingBuffers[actualInst(instance)]); + datato.rewind(); + data.rewind(); + while (datato.hasRemaining()) { + datato.put(data.get()); + } + datato.rewind(); + + if (indexBuffer) { + indexInstantAccessBuffer = datato; + } + + unmap(stagingBuffers[actualInst(instance)]); + + vkbase.copyBufferAndWait(stagingBuffers[actualInst(instance)], buffers[actualInst(instance)], size); + } + + public void bufferDataRetained(IntBuffer data, int size, int instance) { + // If debug mode enabled + if (system == null) return; + + IntBuffer datato = mapInt(size, stagingBuffers[actualInst(instance)]); + datato.rewind(); + data.rewind(); + while (datato.hasRemaining()) { + datato.put(data.get()); + } + datato.rewind(); + unmap(stagingBuffers[actualInst(instance)]); + + vkbase.copyBufferAndWait(stagingBuffers[actualInst(instance)], buffers[actualInst(instance)], size); + } +} \ No newline at end of file diff --git a/core/src/processing/GL2VK/ShaderAttribInfo.java b/core/src/processing/GL2VK/ShaderAttribInfo.java new file mode 100644 index 0000000000..eba10cbb02 --- /dev/null +++ b/core/src/processing/GL2VK/ShaderAttribInfo.java @@ -0,0 +1,186 @@ +package processing.GL2VK; + +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32B32A32_SFLOAT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32B32A32_SINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32B32A32_UINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32B32_SFLOAT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32B32_SINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32B32_UINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32_SFLOAT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32_SINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32_UINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32_SFLOAT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32_SINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32_UINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8G8B8A8_UINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8G8B8_UINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8G8_UINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8_UINT; + +import java.util.ArrayList; +import java.util.HashMap; + + +public class ShaderAttribInfo { + + // Each attrib has a type, size, and location + public class AttribInfo { + public int location = 0; + public int format = 0; + public int size = 0; + public int offset = 0; + + public AttribInfo(int l, int f, int s, int off) { + location = l; + format = f; + size = s; + offset = off; + } + } + + public ShaderAttribInfo(String source) { + loadShaderAttribs(source); + } + + private void newAttribInfo(int l, int f, int s, int off) { + locationToAttrib[l] = new AttribInfo(l,f,s,off); + } + + // Stupid redundant info for gl backward compat + public int bindingSize = 0; + public AttribInfo[] locationToAttrib = new AttribInfo[1024]; + public HashMap nameToLocation = new HashMap(); + + + private void loadShaderAttribs(String shader) { + + // Split into array of lines + String[] lines = shader.split("\n"); + int currAttribOffset = 0; + + + int bracketDepth = 0; + for (String s : lines) { + + // Bracket depth so we only get attribs from outside bodies. + // You'll never see attribs/uniforms in brackets for example. + bracketDepth += countChars(s, "{"); + bracketDepth -= countChars(s, "}"); + + + // Make sure we're not in any methods (like main) + // and search for attribs (keyword "in") + if ( + bracketDepth == 0 && + s.contains(" in ") + ) { + // Lil filtering + // Elements makes it easier to get each individual token. + String[] elements = s.split(" "); + // No spaces allowed. + String line = s.replaceAll(" ", ""); + + // Get location + int index = line.indexOf("layout(location="); + if (index == -1) continue; + index += "layout(location=".length(); + int endIndex = line.indexOf(")", index); + + int location = Integer.parseInt(line.substring(index, endIndex)); +// System.out.println("Location: "+location); + + // Get type + String type = ""; + String attribName = ""; + try { + // Search for the element "in". + // It is equivalant to "attrib" in OpenGL. + for (int i = 0; i < elements.length; i++) { + if (elements[i].equals("in")) { + // Once we find in, remember what follows: + // e.g. in vec3 variableName; + type = elements[i+1]; + attribName = elements[i+2]; + // Remove ; at the end + // And get the attribname + if (attribName.charAt(attribName.length()-1) == ';') attribName = attribName.substring(0, attribName.length()-1); +// System.out.println("type: "+type+" attribName: "+attribName); + break; + } + } + } + catch (IndexOutOfBoundsException e) { +// System.err.println("Woops"); + } + + int size = typeToSize(type); + int format = typeToFormat(type); + + newAttribInfo(location, format, size, currAttribOffset); + + bindingSize += size; + currAttribOffset += size; + + nameToLocation.put(attribName, location); + + // Shouldn't be anything else for us to scan at this point. + continue; + } + } + } + + + private static int countChars(String line, String c) { + return line.length() - line.replace(c, "").length(); + } + + private static int typeToFormat(String val) { + if (val.equals("float")) return VK_FORMAT_R32_SFLOAT; + else if (val.equals("vec2")) return VK_FORMAT_R32G32_SFLOAT; + else if (val.equals("vec3")) return VK_FORMAT_R32G32B32_SFLOAT; + else if (val.equals("vec4")) return VK_FORMAT_R32G32B32A32_SFLOAT; + else if (val.equals("int")) return VK_FORMAT_R32_SINT; + else if (val.equals("ivec2")) return VK_FORMAT_R32G32_SINT; + else if (val.equals("ivec3")) return VK_FORMAT_R32G32B32_SINT; + else if (val.equals("ivec4")) return VK_FORMAT_R32G32B32A32_SINT; + else if (val.equals("uint")) return VK_FORMAT_R32_UINT; + else if (val.equals("uvec2")) return VK_FORMAT_R32G32_UINT; + else if (val.equals("uvec3")) return VK_FORMAT_R32G32B32_UINT; + else if (val.equals("uvec4")) return VK_FORMAT_R32G32B32A32_UINT; + else if (val.equals("bool")) return VK_FORMAT_R8_UINT; + else if (val.equals("bvec2")) return VK_FORMAT_R8G8_UINT; + else if (val.equals("bvec3")) return VK_FORMAT_R8G8B8_UINT; + else if (val.equals("bvec4")) return VK_FORMAT_R8G8B8A8_UINT; + else if (val.equals("mat2")) return VK_FORMAT_R32G32_SFLOAT; + else if (val.equals("mat3")) return VK_FORMAT_R32G32B32_SFLOAT; + else if (val.equals("mat4")) return VK_FORMAT_R32G32B32A32_SFLOAT; + else return -1; + } + + + + private static int typeToSize(String val) { + if (val.equals("float")) return 1 * Float.BYTES; + else if (val.equals("vec2")) return 2 * Float.BYTES; + else if (val.equals("vec3")) return 3 * Float.BYTES; + else if (val.equals("vec4")) return 4 * Float.BYTES; + else if (val.equals("int")) return 1 * Integer.BYTES; + else if (val.equals("ivec2")) return 2 * Integer.BYTES; + else if (val.equals("ivec3")) return 3 * Integer.BYTES; + else if (val.equals("ivec4")) return 4 * Integer.BYTES; + else if (val.equals("uint")) return 1 * Integer.BYTES; + else if (val.equals("uvec2")) return 2 * Integer.BYTES; + else if (val.equals("uvec3")) return 3 * Integer.BYTES; + else if (val.equals("uvec4")) return 4 * Integer.BYTES; + else if (val.equals("bool")) return 1; + else if (val.equals("bvec2")) return 2; + else if (val.equals("bvec3")) return 3; + else if (val.equals("bvec4")) return 4; + else if (val.equals("mat2")) return 2 * 2 * Float.BYTES; + else if (val.equals("mat3")) return 3 * 3 * Float.BYTES; + else if (val.equals("mat4")) return 4 * 4 * Float.BYTES; + else return -1; + } + + +} \ No newline at end of file diff --git a/core/src/processing/GL2VK/ShaderSPIRVUtils.java b/core/src/processing/GL2VK/ShaderSPIRVUtils.java new file mode 100644 index 0000000000..8e27496c6d --- /dev/null +++ b/core/src/processing/GL2VK/ShaderSPIRVUtils.java @@ -0,0 +1,132 @@ +package processing.GL2VK; + +import org.lwjgl.system.NativeResource; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static org.lwjgl.system.MemoryUtil.NULL; +import static org.lwjgl.util.shaderc.Shaderc.*; + +public class ShaderSPIRVUtils { + + public static SPIRV compileShaderFile(String shaderFile, ShaderKind shaderKind) { + File f = new File("."); + String path = f.getAbsolutePath().replaceAll("\\\\", "/"); + path = path.substring(0, path.length()-1); + System.out.println(path+shaderFile); + return compileShaderAbsoluteFile(path+shaderFile, shaderKind); + } + +// public static VertexAttribsBinding getVertexAttribPointers(String vertexShader, int binding) { +// File f = new File("."); +// String path = f.getAbsolutePath().replaceAll("\\\\", "/"); +// path = path.substring(0, path.length()-1); +// System.out.println(path+vertexShader); +// try { +// String source = new String(Files.readAllBytes(Paths.get(vertexShader))); +// return new VertexAttribsBinding(binding, source); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// return null; +// } + + public static SPIRV compileShaderAbsoluteFile(String shaderFile, ShaderKind shaderKind) { + try { + String source = new String(Files.readAllBytes(Paths.get(shaderFile))); + return compileShader(shaderFile, source, shaderKind); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public static SPIRV compileShader(String filename, String source, ShaderKind shaderKind) { + + long compiler = shaderc_compiler_initialize(); + + if(compiler == NULL) { + throw new RuntimeException("Failed to create shader compiler"); + } + + long result = shaderc_compile_into_spv(compiler, source, shaderKind.kind, filename, "main", NULL); + + if(result == NULL) { + throw new RuntimeException("Failed to compile shader " + filename + " into SPIR-V"); + } + + if(shaderc_result_get_compilation_status(result) != shaderc_compilation_status_success) { + throw new RuntimeException(shaderc_result_get_error_message(result)); + } + + shaderc_compiler_release(compiler); + + return new SPIRV(result, shaderc_result_get_bytes(result)); + } + + + public static SPIRV compileShader(String source, ShaderKind shaderKind) { + + long compiler = shaderc_compiler_initialize(); + + if(compiler == NULL) { + throw new RuntimeException("Failed to create shader compiler"); + } + + long result = shaderc_compile_into_spv(compiler, source, shaderKind.kind, "shader", "main", NULL); + + if(result == NULL) { + throw new RuntimeException("No error information provided by compiler"); + } + + if(shaderc_result_get_compilation_status(result) != shaderc_compilation_status_success) { + throw new RuntimeException(shaderc_result_get_error_message(result)); + } + + shaderc_compiler_release(compiler); + + return new SPIRV(result, shaderc_result_get_bytes(result)); + } + + + public enum ShaderKind { + + VERTEX_SHADER(shaderc_glsl_vertex_shader), + GEOMETRY_SHADER(shaderc_glsl_geometry_shader), + FRAGMENT_SHADER(shaderc_glsl_fragment_shader); + + private final int kind; + + ShaderKind(int kind) { + this.kind = kind; + } + } + + public static final class SPIRV implements NativeResource { + + private final long handle; + private ByteBuffer bytecode; + + public SPIRV(long handle, ByteBuffer bytecode) { + this.handle = handle; + this.bytecode = bytecode; + } + + public ByteBuffer bytecode() { + return bytecode; + } + + @Override + public void free() { + shaderc_result_release(handle); + bytecode = null; // Help the GC + } + } + +} \ No newline at end of file diff --git a/core/src/processing/GL2VK/TextureBuffer.java b/core/src/processing/GL2VK/TextureBuffer.java new file mode 100644 index 0000000000..805773d049 --- /dev/null +++ b/core/src/processing/GL2VK/TextureBuffer.java @@ -0,0 +1,327 @@ +package processing.GL2VK; + +import static org.lwjgl.system.MemoryStack.stackPush; +import static org.lwjgl.vulkan.VK10.vkCreateImage; +import static org.lwjgl.vulkan.VK10.vkGetImageMemoryRequirements; +import static org.lwjgl.vulkan.VK10.vkMapMemory; +import static org.lwjgl.vulkan.VK10.vkUnmapMemory; +import static org.lwjgl.vulkan.VK10.vkAllocateMemory; +import static org.lwjgl.vulkan.VK10.vkBindImageMemory; +import static org.lwjgl.vulkan.VK10.vkDestroyImageView; +import static org.lwjgl.vulkan.VK10.vkDestroyImage; +import static org.lwjgl.vulkan.VK10.vkFreeMemory; +import static org.lwjgl.vulkan.VK10.vkCreateSampler; +import static org.lwjgl.vulkan.VK10.vkDestroySampler; +import static org.lwjgl.vulkan.VK10.VK_BUFFER_USAGE_TRANSFER_DST_BIT; +import static org.lwjgl.vulkan.VK10.VK_BUFFER_USAGE_TRANSFER_SRC_BIT; +import static org.lwjgl.vulkan.VK10.VK_MEMORY_HEAP_DEVICE_LOCAL_BIT; +import static org.lwjgl.vulkan.VK10.VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; +import static org.lwjgl.vulkan.VK10.VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_IMAGE_TYPE_2D; +import static org.lwjgl.vulkan.VK10.VK_IMAGE_LAYOUT_UNDEFINED; +import static org.lwjgl.vulkan.VK10.VK_IMAGE_TILING_OPTIMAL; +import static org.lwjgl.vulkan.VK10.VK_SAMPLE_COUNT_1_BIT; +import static org.lwjgl.vulkan.VK10.VK_SHARING_MODE_EXCLUSIVE; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_SUCCESS; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8G8B8A8_UNORM; +import static org.lwjgl.vulkan.VK10.VK_IMAGE_USAGE_TRANSFER_DST_BIT; +import static org.lwjgl.vulkan.VK10.VK_IMAGE_USAGE_SAMPLED_BIT; +import static org.lwjgl.vulkan.VK10.VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; +import static org.lwjgl.vulkan.VK10.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; +import static org.lwjgl.vulkan.VK10.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_FILTER_LINEAR; +import static org.lwjgl.vulkan.VK10.VK_SAMPLER_ADDRESS_MODE_REPEAT; +import static org.lwjgl.vulkan.VK10.VK_BORDER_COLOR_INT_OPAQUE_BLACK; +import static org.lwjgl.vulkan.VK10.VK_COMPARE_OP_ALWAYS; +import static org.lwjgl.vulkan.VK10.VK_SAMPLER_MIPMAP_MODE_LINEAR; + +import java.nio.IntBuffer; +import java.nio.LongBuffer; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkImageCreateInfo; +import org.lwjgl.vulkan.VkMemoryAllocateInfo; +import org.lwjgl.vulkan.VkMemoryRequirements; +import org.lwjgl.vulkan.VkSamplerCreateInfo; + +public class TextureBuffer { + + private static VulkanSystem system; + private static VKSetup vkbase; + + private int bufferCount = 0; + + private int[][] data; + + private long texture = -1; + private volatile long textureMemory = -1; + + private long stagingBuffer = -1; + private long stagingBufferMemory = -1; + + public long imageView = -1; + public long sampler = -1; + + boolean initialized = false; + + private int width = 0; + private int height = 0; + + public static int textureCount = 1; + + public int myTextureID = 0; + + + + + public TextureBuffer(VulkanSystem s) { + system = s; + vkbase = s.vkbase; + } + + // Debug mode constructor + public TextureBuffer() { + + } + + { + myTextureID = textureCount++; + } + + public static IntBuffer mapInt(int size, long mem) { + try(MemoryStack stack = stackPush()) { + // alloc pointer for our data + PointerBuffer pointer = stack.mallocPointer(1); + vkMapMemory(system.device, mem, 0, size, 0, pointer); + + // Here instead of some mem copy function we can just + // copy each and every byte of buffer. + IntBuffer datato = pointer.getIntBuffer(0, size/Integer.BYTES); + + return datato; + } + } + + public static void unmap(long mem) { + vkUnmapMemory(system.device, mem); + } + + private void writeData(IntBuffer pdata, int xOffset, int yOffset, int width, int height) { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + data[y+yOffset][x+xOffset] = pdata.get(y*width+x); + } + } + } + + public void bufferData(IntBuffer data, int xOffset, int yOffset, int width, int height) { +// int offset = yOffset*height + xOffset; +// int newsize = width*height; +// +// int currentSize = this.width*this.height; + writeData(data, xOffset, yOffset, width, height); + updateBuffer(); + +// if (!initialized) { +// createTextureBuffer(width, height); +// bufferData(data, newsize, offset); +// } +// else { +// // Just do a warning for now. +// if (newsize > currentSize) { +// System.err.println("bufferDataAuto: newsize ("+newsize+") > currentSize ("+currentSize+"); can't buffer bigger size when buffer has already been created."); +// return; +// } +// bufferData(data, newsize, offset); +// } + } + + public void createBuffer(int width, int height) { + if (initialized) { + return; + } + + createTextureBuffer(width, height); + } + + + + public void createTextureBuffer(int width, int height) { + try(MemoryStack stack = stackPush()) { + // Info + VkImageCreateInfo imageInfo = VkImageCreateInfo.calloc(stack); + imageInfo.sType(VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO); + imageInfo.imageType(VK_IMAGE_TYPE_2D); + imageInfo.extent().width(width); + imageInfo.extent().height(height); + imageInfo.extent().depth(1); + imageInfo.mipLevels(1); + imageInfo.arrayLayers(1); + imageInfo.format(VK_FORMAT_R8G8B8A8_UNORM ); + imageInfo.tiling(VK_IMAGE_TILING_OPTIMAL); + imageInfo.initialLayout(VK_IMAGE_LAYOUT_UNDEFINED); + imageInfo.usage(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + imageInfo.samples(VK_SAMPLE_COUNT_1_BIT); + imageInfo.sharingMode(VK_SHARING_MODE_EXCLUSIVE); + + // Actually creating the texture here. + LongBuffer pTextureImage = stack.callocLong(1); + if(vkCreateImage(system.device, imageInfo, null, pTextureImage) != VK_SUCCESS) { + throw new RuntimeException("Failed to create image"); + } + // Texture now set + texture = pTextureImage.get(0); + + // Time to get some mem requirements for our texture + VkMemoryRequirements memRequirements = VkMemoryRequirements.malloc(stack); + vkGetImageMemoryRequirements(system.device, texture, memRequirements); + + + VkMemoryAllocateInfo allocInfo = VkMemoryAllocateInfo.calloc(stack); + allocInfo.sType(VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO); + allocInfo.allocationSize(memRequirements.size()); + allocInfo.memoryTypeIndex(vkbase.findMemoryType(stack, memRequirements.memoryTypeBits(), VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); + + LongBuffer pTextureImageMemory = stack.callocLong(1); + System.out.println("TextureBuffer Allocation"); + if(vkAllocateMemory(system.device, allocInfo, null, pTextureImageMemory) != VK_SUCCESS) { + throw new RuntimeException("Failed to allocate image memory"); + } + textureMemory = pTextureImageMemory.get(0); + + vkBindImageMemory(system.device, texture, textureMemory, 0); + } + + + // STAGING BUFFER + try(MemoryStack stack = stackPush()) { + LongBuffer pBuffer = stack.mallocLong(1); + LongBuffer pBufferMemory = stack.mallocLong(1); + int size = width*height*4; + + vkbase.createBuffer(size, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + pBuffer, + pBufferMemory); + + this.stagingBuffer = pBuffer.get(0); + this.stagingBufferMemory = pBufferMemory.get(0); + } + bufferCount++; + initialized = true; + this.width = width; + this.height = height; + data = new int[height][width]; + imageView = vkbase.createImageView(texture, VK_FORMAT_R8G8B8A8_UNORM ); + createTextureSampler(); + } + + + private void createTextureSampler() { + + try(MemoryStack stack = stackPush()) { + + VkSamplerCreateInfo samplerInfo = VkSamplerCreateInfo.calloc(stack); + samplerInfo.sType(VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO); + samplerInfo.magFilter(VK_FILTER_LINEAR); + samplerInfo.minFilter(VK_FILTER_LINEAR); + samplerInfo.addressModeU(VK_SAMPLER_ADDRESS_MODE_REPEAT); + samplerInfo.addressModeV(VK_SAMPLER_ADDRESS_MODE_REPEAT); + samplerInfo.addressModeW(VK_SAMPLER_ADDRESS_MODE_REPEAT); + samplerInfo.anisotropyEnable(true); + samplerInfo.maxAnisotropy(16.0f); + samplerInfo.borderColor(VK_BORDER_COLOR_INT_OPAQUE_BLACK); + samplerInfo.unnormalizedCoordinates(false); + samplerInfo.compareEnable(false); + samplerInfo.compareOp(VK_COMPARE_OP_ALWAYS); + samplerInfo.mipmapMode(VK_SAMPLER_MIPMAP_MODE_LINEAR); + + LongBuffer pTextureSampler = stack.mallocLong(1); + + if(vkCreateSampler(system.device, samplerInfo, null, pTextureSampler) != VK_SUCCESS) { + throw new RuntimeException("Failed to create texture sampler"); + } + + sampler = pTextureSampler.get(0); + } + } + + private boolean undefinedLayout = true; + + // Buffer the whole data + public void updateBuffer() { + // Null data? Skip buffering. Null means "just create the buffer". + if (data == null) return; + + int size = width*height*Integer.BYTES; + + if (system == null) return; + + IntBuffer datato = mapInt(size, stagingBufferMemory); + datato.rewind(); + + int x = 0; + int y = 0; + +// try { + while (datato.hasRemaining()) { +// System.out.println(data[y][x]); + datato.put(data[y][x]); + x++; + if (x >= width) { + x = 0; + y++; + } + } +// } +// catch (IndexOutOfBoundsException e) { +// +// } + + datato.rewind(); + unmap(stagingBufferMemory); + + if (undefinedLayout) { + vkbase.transitionImageLayout(texture, + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + undefinedLayout = false; + } + else { + vkbase.transitionImageLayout(texture, + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + } + + vkbase.copyTextureAndWait(stagingBuffer, texture, width, height); + + vkbase.transitionImageLayout(texture, + VK_FORMAT_R8G8B8A8_UNORM , + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + } + + public void clean() { + vkDestroyImageView(system.device, imageView, null); + vkDestroyImage(system.device, texture, null); + vkDestroySampler(system.device, sampler, null); + vkFreeMemory(system.device, textureMemory, null); + + texture = -1; + textureMemory = -1; + stagingBuffer = -1; + stagingBufferMemory = -1; + imageView = -1; + sampler = -1; + initialized = false; + } +} + diff --git a/core/src/processing/GL2VK/ThreadNode.java b/core/src/processing/GL2VK/ThreadNode.java new file mode 100644 index 0000000000..66dbde67a1 --- /dev/null +++ b/core/src/processing/GL2VK/ThreadNode.java @@ -0,0 +1,1341 @@ +package processing.GL2VK; + +import static org.lwjgl.system.MemoryStack.stackPush; +import static org.lwjgl.vulkan.VK10.VK_COMMAND_BUFFER_LEVEL_SECONDARY; +import static org.lwjgl.vulkan.VK10.VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; +import static org.lwjgl.vulkan.VK10.VK_PIPELINE_BIND_POINT_GRAPHICS; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS; +import static org.lwjgl.vulkan.VK10.VK_SUCCESS; +import static org.lwjgl.vulkan.VK10.VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; +import static org.lwjgl.vulkan.VK10.VK_INDEX_TYPE_UINT16; +import static org.lwjgl.vulkan.VK10.VK_INDEX_TYPE_UINT32; +import static org.lwjgl.vulkan.VK10.VK_SHADER_STAGE_VERTEX_BIT; +import static org.lwjgl.vulkan.VK10.VK_SHADER_STAGE_FRAGMENT_BIT; +import static org.lwjgl.vulkan.VK10.VK_IMAGE_ASPECT_COLOR_BIT; +import static org.lwjgl.vulkan.VkPhysicalDeviceIndexTypeUint8FeaturesKHR.INDEXTYPEUINT8; +import static org.lwjgl.vulkan.VK10.vkCmdBindIndexBuffer; +import static org.lwjgl.vulkan.VK10.vkAllocateCommandBuffers; +import static org.lwjgl.vulkan.VK10.vkBeginCommandBuffer; +import static org.lwjgl.vulkan.VK10.vkCmdBeginRenderPass; +import static org.lwjgl.vulkan.VK10.vkCmdBindPipeline; +import static org.lwjgl.vulkan.VK10.vkCmdBindVertexBuffers; +import static org.lwjgl.vulkan.VK10.vkCmdDraw; +import static org.lwjgl.vulkan.VK10.vkCmdDrawIndexed; +import static org.lwjgl.vulkan.VK10.vkCreateCommandPool; +import static org.lwjgl.vulkan.VK10.vkEndCommandBuffer; +import static org.lwjgl.vulkan.VK10.vkFreeCommandBuffers; +import static org.lwjgl.vulkan.VK10.vkResetCommandBuffer; +import static org.lwjgl.vulkan.VK10.vkCmdPushConstants; +import static org.lwjgl.vulkan.VK10.vkDestroyCommandPool; +import static org.lwjgl.vulkan.VK10.vkCmdClearAttachments; +import static org.lwjgl.vulkan.VK10.vkCmdBindDescriptorSets; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; +import java.nio.LongBuffer; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerArray; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongArray; + +import org.lwjgl.BufferUtils; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkCommandBuffer; +import org.lwjgl.vulkan.VkCommandBufferAllocateInfo; +import org.lwjgl.vulkan.VkCommandBufferBeginInfo; +import org.lwjgl.vulkan.VkCommandBufferInheritanceInfo; +import org.lwjgl.vulkan.VkCommandPoolCreateInfo; +import org.lwjgl.vulkan.VkOffset2D; +import org.lwjgl.vulkan.VkPipelineColorBlendAttachmentState; +import org.lwjgl.vulkan.VkRect2D; +import org.lwjgl.vulkan.VkClearAttachment; +import org.lwjgl.vulkan.VkClearValue; +import org.lwjgl.vulkan.VkClearRect; + +//import helloVulkan.VKSetup.QueueFamilyIndices; + +// TODO: time active vs. time idle query information +// TODO: average % of cmdQueue used in a frame. + +public class ThreadNode { + final static boolean DEBUG = false; + + // ThreadNode commands + public final static int NO_CMD = 0; + public final static int CMD_DRAW_ARRAYS = 1; + public final static int CMD_DRAW_INDEXED = 2; + public final static int CMD_BEGIN_RECORD = 3; + public final static int CMD_END_RECORD = 4; + public final static int CMD_KILL = 5; + public final static int CMD_BUFFER_DATA = 6; + public final static int CMD_BIND_PIPELINE = 7; + public final static int CMD_PUSH_CONSTANT = 8; + public final static int CMD_BUFFER_FLOAT_DATA = 9; + public final static int CMD_BUFFER_BYTE_DATA = 10; + public final static int CMD_BUFFER_SHORT_DATA = 11; + public final static int CMD_BUFFER_LONG_DATA = 12; + public final static int CMD_BUFFER_INT_DATA = 13; + public final static int CMD_CLEAR = 14; + public final static int CMD_BIND_DESCRIPTOR = 15; + + // ThreadNode state statuses + public final static int STATE_INACTIVE = 0; + public final static int STATE_SLEEPING = 1; + public final static int STATE_RUNNING = 2; + public final static int STATE_ENTERING_SLEEP = 3; + public final static int STATE_KILLED = 4; + public final static int STATE_WAKING = 5; + public final static int STATE_SLEEPING_INTERRUPTED = 6; + public final static int STATE_NEXT_CMD = 7; + public final static int STATE_LINGERING = 8; + public final static int STATE_LINGERING_LAST_CHANCE = 9; + + + // CURRENT BUGS: + // BUG WARNING signalled out of sleep with no work available. + // Let's say we're calling endCommands: + // - (1) executing cmd + // - (0) Set cmd id + // - (1) Oh look! A new command to execute. + // - (1) Done, let's go to sleep + // - (0) wakeThread (we're under the assumption that the thread hasn't done the work yet) + // - (1) Woke up, but wait! There isn't any work for me to do! + // Solution: check cmd is set to 0 or not + + // Other bug: + // looplock'd waiting for a thread that won't respond (state 1) + // + + // To avoid clashing from the main thread accessing the front of the queue while the + // other thread is accessing the end of the queue, best solution is to make this big + // enough lol. + protected final static int MAX_QUEUE_LENGTH = 100000; + + // In order to avoid expensive interrupt calls waking a thread up from sleep, we can + // busy loop for a little while after we have no more tasks to do. If we get another task + // during this lingering period, great! We avoided an expensive interrupt() call. If not, + // then we go to sleep, and eventually get woken up by interrupt(). + // How long does this linger time last? + // Well it's up to you, this value is in microseconds. + protected final static long LINGER_TIME = 500L; + + protected VulkanSystem system; + protected VKSetup vkbase; + protected int myID = 0; + + protected VkCommandBuffer[] cmdbuffers; + + // NOT to be set by main thread + protected AtomicInteger currentFrame = new AtomicInteger(0); + protected AtomicInteger currentImage = new AtomicInteger(0); + protected long commandPool; + + protected AtomicInteger threadState = new AtomicInteger(STATE_INACTIVE); + protected AtomicBoolean openCmdBuffer = new AtomicBoolean(false); + + // Read-only begin info for beginning our recording of commands + // (what am i even typing i need sleep) + // One for each frames in flight + protected VkCommandBufferBeginInfo[] beginInfos; + // Just to keep it from being garbage collected or something + private VkCommandBufferInheritanceInfo[] inheritanceInfos; + + + // There are two seperate indexes, one for our thread (main thread) and one for this thread. + // We add item to queue (cmdindex = 0 -> 1) and then eventually thread updates its own index + // as it works on cmd (myIndex = 0 -> 1) + protected int cmdindex = 0; // Start at one because thread will have already consumed index 0 + protected Thread thread; + + // Accessed by 2 threads so volatile (i was told that volatile avoids outdated caching issues) + // (source: the internet, the most truthful place, i think) +// private volatile CMD[] cmdqueue; + + + // INNER COMMAND TYPES + // Originally was gonna create some classes which extend this class, + // but this would mean garbage collection for each command we call. + // The other option is to put all arguments from every command into + // the one cmd class, which isn't the most readable or memory efficient, + // but we care about going FAST. + protected AtomicIntegerArray cmdID = new AtomicIntegerArray(MAX_QUEUE_LENGTH); + protected AtomicLongArray[] cmdLongArgs = new AtomicLongArray[128]; + protected AtomicIntegerArray[] cmdIntArgs = new AtomicIntegerArray[128]; + public long currentPipeline = 0L; + + private static final int MAX_BUFFERS = 4096; + + protected int floatBuffersIndex = 0; + protected int shortBuffersIndex = 0; + protected int byteBuffersIndex = 0; + protected int longBuffersIndex = 0; + protected int intBuffersIndex = 0; + protected int graphicsBufferIndex = 0; + protected GraphicsBuffer[] graphicsBuffers = new GraphicsBuffer[MAX_BUFFERS]; + protected FloatBuffer[] floatBuffers = new FloatBuffer[MAX_BUFFERS]; + protected ShortBuffer[] shortBuffers = new ShortBuffer[MAX_BUFFERS]; + protected IntBuffer[] intBuffers = new IntBuffer[MAX_BUFFERS]; + protected ByteBuffer[] byteBuffers = new ByteBuffer[MAX_BUFFERS]; + protected LongBuffer[] longBuffers = new LongBuffer[MAX_BUFFERS]; + + // Some benchmarking variables. + private static int interruptsCalled = 0; + private static long interruptPenalty = 0L; + private static long oddStateStallPenalty = 0L; + private static long threadFinishWait = 0L; + private static long fullQueueStallPenalty = 0L; + + + + public ThreadNode(VulkanSystem vk, int id) { + // Variables setup + system = vk; + vkbase = vk.vkbase; + myID = id; + + // Initialise cmdqueue and all its objects + for (int i = 0; i < MAX_QUEUE_LENGTH; i++) { + cmdID.set(i, 0); + } + + createObjects(); + startThread(); + } + + + protected void println(String message) { + if (DEBUG) { + System.out.println("("+myID+") "+message); + } + } + + private void createObjects() { + // Create command pool + try(MemoryStack stack = stackPush()) { + VkCommandPoolCreateInfo poolInfo = VkCommandPoolCreateInfo.calloc(stack); + poolInfo.sType(VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO); + poolInfo.flags(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); + poolInfo.queueFamilyIndex(vkbase.queueIndicies.graphicsFamily); + + // create our command pool vk + LongBuffer pCommandPool = stack.mallocLong(1); + if (vkCreateCommandPool(vkbase.device, poolInfo, null, pCommandPool) != VK_SUCCESS) { + throw new RuntimeException("Failed to create command pool"); + }; + commandPool = pCommandPool.get(0); + } + + final int commandBuffersCount = VulkanSystem.MAX_FRAMES_IN_FLIGHT; + + // Create secondary command buffer + try(MemoryStack stack = stackPush()) { + + VkCommandBufferAllocateInfo allocInfo = VkCommandBufferAllocateInfo.calloc(stack); + allocInfo.sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO); + allocInfo.commandPool(commandPool); + allocInfo.level(VK_COMMAND_BUFFER_LEVEL_SECONDARY); + allocInfo.commandBufferCount(commandBuffersCount); + + PointerBuffer pCommandBuffers = stack.mallocPointer(commandBuffersCount); + + if(vkAllocateCommandBuffers(vkbase.device, allocInfo, pCommandBuffers) != VK_SUCCESS) { + throw new RuntimeException("Failed to allocate command buffers"); + } + + cmdbuffers = new VkCommandBuffer[commandBuffersCount]; + for(int i = 0; i < commandBuffersCount; i++) { + cmdbuffers[i] = new VkCommandBuffer(pCommandBuffers.get(i), vkbase.device); + } + } + + int imagesSize = system.swapChainFramebuffers.size(); + // Create readonly beginInfo structs. + beginInfos = new VkCommandBufferBeginInfo[imagesSize]; + inheritanceInfos = new VkCommandBufferInheritanceInfo[imagesSize]; + for(int i = 0; i < imagesSize; i++) { +// Inheritance because for some reason we need that + inheritanceInfos[i] = VkCommandBufferInheritanceInfo.create(); + inheritanceInfos[i].sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO); + inheritanceInfos[i].renderPass(system.renderPass); + // Secondary command buffer also use the currently active framebuffer + inheritanceInfos[i].framebuffer(system.swapChainFramebuffers.get(i)); + + beginInfos[i] = VkCommandBufferBeginInfo.create(); + beginInfos[i].sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO); + beginInfos[i].flags(VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT); + beginInfos[i].pInheritanceInfo(inheritanceInfos[i]); + } + } + + protected void startThread() { + // Inside of thread, we run logic which checks for items in the queue + // and then executes the vk commands that correspond to the int + thread = new Thread(new Runnable() { + public void run() { + int myIndex = 0; + + long sleepTime = 0L; + long runTime = 0L; + + boolean pipelineBound = false; + + long lingerTimer = 0; + long lingerTimestampBefore = 0L; + + ByteBuffer pushConstantBuffer = null; + + // Loop until receive KILL_THREAD cmd + while (true) { + long runbefore = System.nanoTime(); + boolean goToSleepMode = false; + boolean kill = false; + + int index = (myIndex++)%MAX_QUEUE_LENGTH; + + VkCommandBuffer cmdbuffer = cmdbuffers[currentFrame.get()]; + + +// if (threadState.get() == STATE_WAKING) { +// if (id == NO_CMD) System.err.println(myID+" NO_CMD warning"); +// threadState.set(STATE_RUNNING); +// } +// Util.beginTmr(); + + + // ====================== + // CMD EXECUTOR + // ====================== + switch (cmdID.getAndSet(index, 0)) { + case NO_CMD: + // Only go to sleep if we've been lingering for long enough. + if ((lingerTimer/1000L) < LINGER_TIME) { + // Keep running + threadState.set(STATE_LINGERING); + lingerTimer += System.nanoTime()-lingerTimestampBefore; + lingerTimestampBefore = System.nanoTime(); + + if ((lingerTimer/1000L) >= LINGER_TIME) { + threadState.set(STATE_LINGERING_LAST_CHANCE); + } + } + else { +// System.out.println("GOING TO SLEEP"); + threadState.set(STATE_ENTERING_SLEEP); + goToSleepMode = true; + } + + myIndex--; + break; + case CMD_DRAW_ARRAYS: { + threadState.set(STATE_RUNNING); + lingerTimer = 0L; + lingerTimestampBefore = System.nanoTime(); + + println("CMD_DRAW_ARRAYS (index "+index+")"); + int size = cmdIntArgs[0].get(index); + int first = cmdIntArgs[1].get(index); + int numBuffers = cmdIntArgs[2].get(index); + + try(MemoryStack stack = stackPush()) { + LongBuffer vertexBuffers = stack.callocLong(numBuffers); + LongBuffer offsets = stack.callocLong(numBuffers); + + // Longargs 1-x are buffers. + for (int i = 0; i < numBuffers; i++) { + vertexBuffers.put(i, cmdLongArgs[i].get(index)); + offsets.put(i, 0); + } + vkCmdBindVertexBuffers(cmdbuffer, 0, vertexBuffers, offsets); + + vkCmdDraw(cmdbuffer, size, 1, first, 0); + } + break; + } + // Probably the most important command + case CMD_BEGIN_RECORD: + threadState.set(STATE_RUNNING); + lingerTimer = 0L; + lingerTimestampBefore = System.nanoTime(); + + sleepTime = 0; + runTime = 0; + println("CMD_BEGIN_RECORD"); + + currentImage.set(cmdIntArgs[0].get(index)); + currentFrame.set(cmdIntArgs[1].get(index)); + cmdbuffer = cmdbuffers[currentFrame.get()]; + + if (openCmdBuffer.get() == false) { + vkResetCommandBuffer(cmdbuffer, 0); + // Begin recording + + // In case you're wondering, beginInfo index is currentImage, not + // the currentFrame, because it holds which framebuffer (image) we're + // using (confusing i know) + if(vkBeginCommandBuffer(cmdbuffer, beginInfos[currentImage.get()]) != VK_SUCCESS) { + throw new RuntimeException("Failed to begin recording command buffer"); + } + } + // Bug detected + else System.err.println("("+myID+") Attempt to begin an already open command buffer."); + + openCmdBuffer.set(true); +// vkCmdBindPipeline(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, system.graphicsPipeline); + pipelineBound = false; + break; + case CMD_END_RECORD: + threadState.set(STATE_RUNNING); + lingerTimer = 0L; + lingerTimestampBefore = System.nanoTime(); + + println("CMD_END_RECORD (index "+index+")"); + + if (openCmdBuffer.get() == true) { + if(vkEndCommandBuffer(cmdbuffer) != VK_SUCCESS) { + throw new RuntimeException("Failed to record command buffer"); + } + } + else System.err.println("("+myID+") Attempt to close an already closed command buffer."); + + openCmdBuffer.set(false); + // We should also really go into sleep mode now + threadState.set(STATE_ENTERING_SLEEP); + goToSleepMode = true; + + // RATIO +// System.out.println("("+myID+") Sleep time "+(sleepTime/1000L)+"us Run time "+(runTime/1000L)+"us"); + +// System.out.println("("+myID+") Active-to-sleep ratio "+(int)((((double)runTime)/((double)(runTime+sleepTime)))*100d)+"%"); + + break; + case CMD_KILL: + threadState.set(STATE_RUNNING); + lingerTimer = 0L; + lingerTimestampBefore = System.nanoTime(); + + goToSleepMode = false; + kill = true; + break; + + // This goes pretty much unused. + case CMD_BUFFER_DATA: + threadState.set(STATE_RUNNING); + lingerTimer = 0L; + println("CMD_BUFFER_DATA (index "+index+")"); + +// vkCmdEndRenderPass(system.currentCommandBuffer); + system.copyBufferFast(cmdbuffer, cmdLongArgs[0].get(index), cmdLongArgs[1].get(index), cmdIntArgs[0].get(index)); +// vkCmdBeginRenderPass(system.currentCommandBuffer, system.renderPassInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); + break; + + case CMD_BIND_PIPELINE: { + threadState.set(STATE_RUNNING); + lingerTimer = 0L; + lingerTimestampBefore = System.nanoTime(); + + println("CMD_BIND_PIPELINE (index "+index+")"); + + long pipeline = cmdLongArgs[0].get(index); + + vkCmdBindPipeline(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + break; + } + + case CMD_BIND_DESCRIPTOR: { + threadState.set(STATE_RUNNING); + lingerTimer = 0L; + lingerTimestampBefore = System.nanoTime(); + + println("CMD_BIND_DESCRIPTOR (index "+index+")"); + + long pipelineLayout = cmdLongArgs[0].get(index); + long descriptorSet = cmdLongArgs[1].get(index); + + try(MemoryStack stack = stackPush()) { + vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + pipelineLayout, 0, stack.longs(descriptorSet), null); + } + break; + } + + + case CMD_DRAW_INDEXED: { + threadState.set(STATE_RUNNING); + lingerTimer = 0L; + lingerTimestampBefore = System.nanoTime(); + + println("CMD_DRAW_INDEXED (index "+index+")"); + // Int0: indiciesSize + // Long0: indiciesBuffer + // Int1: numBuffers + // LongX: vertexBuffers + // Int2: offset + + int indiciesSize = cmdIntArgs[0].get(index); + int numBuffers = cmdIntArgs[1].get(index); + long indicesBuffer = cmdLongArgs[0].get(index); + + int type = cmdIntArgs[2].get(index); + int offset = cmdIntArgs[3].get(index); + int vertexOffset =cmdIntArgs[4].get(index); + + + +// System.out.println("expected type: "+VK_INDEX_TYPE_UINT16+" type: "+vkType+" offset: "+offset+" indicesSize: "+indiciesSize+" numBuffers: "+numBuffers+" indicesBuffer: "+indicesBuffer); + + +// long before = System.nanoTime(); + try(MemoryStack stack = stackPush()) { + LongBuffer vertexBuffers = stack.callocLong(numBuffers); + LongBuffer offsets = stack.callocLong(numBuffers); + + + // Longargs 1-x are buffers. + for (int i = 0; i < numBuffers; i++) { + vertexBuffers.put(i, cmdLongArgs[i+1].get(index)); + offsets.put(i, 0); + } + vertexBuffers.rewind(); + offsets.rewind(); + + vkCmdBindVertexBuffers(cmdbuffer, 0, vertexBuffers, offsets); + +// vkCmdBindIndexBuffer(cmdbuffer, indicesBuffer, offset, type); +// vkCmdDrawIndexed(cmdbuffer, indiciesSize, 1, 0, 0, 0); + vkCmdBindIndexBuffer(cmdbuffer, indicesBuffer, offset, type); + vkCmdDrawIndexed(cmdbuffer, indiciesSize, 1, 0, vertexOffset, 0); + } +// System.out.println("drawindexed elapsed time "+(System.nanoTime()-before)+"ns"); + + break; + } + case CMD_PUSH_CONSTANT: { + threadState.set(STATE_RUNNING); + lingerTimer = 0L; + lingerTimestampBefore = System.nanoTime(); + + println("CMD_PUSH_CONSTANT (index "+index+")"); + // Long0: pipelineLayout + // Int0: Size/offset/vertexOrFragment + // Long1-X: bufferData (needs to be reconstructed + + long pipelineLayout = cmdLongArgs[0].get(index); + + // Layout: ssssssssoooooooooooooooovvvvvvvv + int arg0 = cmdIntArgs[0].get(index); + int size = ((arg0 >> 24) & 0x000000FF); + int offset = ((arg0 >> 8) & 0x0000FFFF); + int vertexOrFragment = (arg0 & 0x000000FF); + + if (size <= 0) { + System.err.println("CONCURRENCY ISSUE"); + System.err.println("arg0 "+Integer.toHexString(arg0)); + System.err.println("size "+size); + System.err.println("offset "+offset); + System.err.println("vertexOrFragment "+vertexOrFragment); + } + + // Needs to be in multiples of 8 for long buffer. + // TODO: actually sort out. + // buffer size = 12, we need to expand to 16. + // This equasion would set it to 8. +// size = ((size/8))*8; + + // Create buffer + // It's a gobal variable so we don't have to recreate the variable each + // time + if (pushConstantBuffer == null || pushConstantBuffer.capacity() != size) { + pushConstantBuffer = BufferUtils.createByteBuffer(size); + pushConstantBuffer.order(ByteOrder.LITTLE_ENDIAN); + } + + // Now we have to reconstruct the buffer from the longs + pushConstantBuffer.rewind(); + int arg = 1; + + // Long story short, we might store 8 bytes in a 4-byte buffer, + // which would cause an exception. + // We have a special case to write the bytes in multiples of 8, + // then write the remaining 4 bytes + int ssize = size; + if (size % 8 == 4) ssize -= 4; + for (int i = 0; i < ssize; i += Long.BYTES) { + pushConstantBuffer.putLong(cmdLongArgs[arg++].get(index)); + } + if (size % 8 == 4) { + int val = (int)cmdLongArgs[arg++].get(index); + pushConstantBuffer.putInt(val); + } + pushConstantBuffer.rewind(); + + + // Get the vk type + int vkType = 0; + if (vertexOrFragment == GLUniform.VERTEX) { + vkType = VK_SHADER_STAGE_VERTEX_BIT; + } + else if (vertexOrFragment == GLUniform.FRAGMENT) { + vkType = VK_SHADER_STAGE_FRAGMENT_BIT; + } + else { + vkType = VK_SHADER_STAGE_VERTEX_BIT; + } + + vkCmdPushConstants(cmdbuffer, pipelineLayout, vkType, offset, pushConstantBuffer); + } + break; + case CMD_BUFFER_FLOAT_DATA: { + threadState.set(STATE_RUNNING); + lingerTimer = 0L; + lingerTimestampBefore = System.nanoTime(); + + + int size = cmdIntArgs[0].get(index); + int bufferIndex = cmdIntArgs[1].get(index); + int instance = cmdIntArgs[2].get(index); + int graphicsIndex = cmdIntArgs[3].get(index); + + graphicsBuffers[graphicsIndex].bufferDataImmediate(floatBuffers[bufferIndex], size, instance); + break; + } + case CMD_BUFFER_BYTE_DATA: { + threadState.set(STATE_RUNNING); + lingerTimer = 0L; + lingerTimestampBefore = System.nanoTime(); + + int size = cmdIntArgs[0].get(index); + int bufferIndex = cmdIntArgs[1].get(index); + int instance = cmdIntArgs[2].get(index); + int graphicsIndex = cmdIntArgs[3].get(index); + + graphicsBuffers[graphicsIndex].bufferDataImmediate(byteBuffers[bufferIndex], size, instance); + break; + } + case CMD_BUFFER_SHORT_DATA: { + threadState.set(STATE_RUNNING); + lingerTimer = 0L; + lingerTimestampBefore = System.nanoTime(); + + int size = cmdIntArgs[0].get(index); + int bufferIndex = cmdIntArgs[1].get(index); + int instance = cmdIntArgs[2].get(index); + int graphicsIndex = cmdIntArgs[3].get(index); + + graphicsBuffers[graphicsIndex].bufferDataImmediate(shortBuffers[bufferIndex], size, instance); + break; + } + case CMD_BUFFER_INT_DATA: { + threadState.set(STATE_RUNNING); + lingerTimer = 0L; + lingerTimestampBefore = System.nanoTime(); + + int size = cmdIntArgs[0].get(index); + int bufferIndex = cmdIntArgs[1].get(index); + int instance = cmdIntArgs[2].get(index); + int graphicsIndex = cmdIntArgs[3].get(index); + + graphicsBuffers[graphicsIndex].bufferDataImmediate(intBuffers[bufferIndex], size, instance); + break; + } + case CMD_BUFFER_LONG_DATA: { + threadState.set(STATE_RUNNING); + lingerTimer = 0L; + lingerTimestampBefore = System.nanoTime(); + + int size = cmdIntArgs[0].get(index); + int bufferIndex = cmdIntArgs[1].get(index); + int instance = cmdIntArgs[2].get(index); + int graphicsIndex = cmdIntArgs[3].get(index); + + graphicsBuffers[graphicsIndex].bufferDataImmediate(longBuffers[bufferIndex], size, instance); + break; + } + case CMD_CLEAR: { + threadState.set(STATE_RUNNING); + lingerTimer = 0L; + lingerTimestampBefore = System.nanoTime(); + + try(MemoryStack stack = stackPush()) { + VkClearAttachment.Buffer attachments = VkClearAttachment.calloc(1, stack); + attachments.aspectMask(VK_IMAGE_ASPECT_COLOR_BIT); + + float r = Float.intBitsToFloat(cmdIntArgs[0].get(index)); + float g = Float.intBitsToFloat(cmdIntArgs[1].get(index)); + float b = Float.intBitsToFloat(cmdIntArgs[2].get(index)); + float a = Float.intBitsToFloat(cmdIntArgs[3].get(index)); + + VkClearValue clearValues = VkClearValue.calloc(stack); + clearValues.color().float32(stack.floats(r,g,b,a)); + attachments.clearValue(clearValues); + + VkRect2D rect = VkRect2D.calloc(stack); + rect.offset(VkOffset2D.calloc(stack).set(0, 0)); + rect.extent(vkbase.swapChainExtent); + + VkClearRect.Buffer clearRect = VkClearRect.calloc(1, stack); + clearRect.rect(rect); // Size of the area to clear + clearRect.baseArrayLayer(0); // For 2D images + clearRect.layerCount(1); // Clear one layer + + vkCmdClearAttachments(cmdbuffer, attachments, clearRect); + } + } + + + } + + // ====================== + + + if (kill) { + threadState.set(STATE_KILLED); + // Kills the thread + break; + } + + runTime += System.nanoTime()-runbefore; + // No more tasks to do? Take a lil nap. + if (goToSleepMode) { + println("NOW SLEEPING"); + long before = System.nanoTime(); + try { + // Sleep for an indefinite amount of time + // (we gonna interrupt the thread later) + threadState.set(STATE_SLEEPING); + Thread.sleep(999999); + } + catch (InterruptedException e) { + threadState.set(STATE_WAKING); + lingerTimer = 0; + println("WAKEUP"); + } + sleepTime += System.nanoTime()-before; + } + } + threadState.set(STATE_KILLED); + + } + } + ); + thread.start(); + } + int stalltime = 0; + + private int getNextCMDIndex() { + int ret = cmdindex; + + long before = System.nanoTime(); + int stallloopcounts = 0; + while (cmdID.get(ret) != NO_CMD) { + // We're forced to wait until the thread has caught up with some of the queue +// try { +// Thread.sleep(0); +// } catch (InterruptedException e) { +// } +// println("WARNING queue clash, cmdid is "+ret); + stallloopcounts++; + } + long stall = System.nanoTime()-before; + if (stall > 100) fullQueueStallPenalty += stall; + cmdindex++; + if (cmdindex >= MAX_QUEUE_LENGTH) { + cmdindex = 0; + } + return ret; + } + + private void setIntArg(int argIndex, int queueIndex, int value) { + if (cmdIntArgs[argIndex] == null) { + cmdIntArgs[argIndex] = new AtomicIntegerArray(MAX_QUEUE_LENGTH); + } + cmdIntArgs[argIndex].set(queueIndex, value); + } + + private void setLongArg(int argIndex, int queueIndex, long value) { + if (cmdLongArgs[argIndex] == null) { + cmdLongArgs[argIndex] = new AtomicLongArray(MAX_QUEUE_LENGTH); + } + cmdLongArgs[argIndex].set(queueIndex, value); + } + + + public static void getTimedReport() { + System.out.println("------------------------"); + + System.out.println("Interrupts called "+interruptsCalled); + System.out.println("Interrupt penalty "+(interruptPenalty/1000L)+"us"); + System.out.println("Unlucky stall penalty "+(oddStateStallPenalty/1000L)+"us"); + System.out.println("Full queue stall penalty "+(fullQueueStallPenalty/1000L)+"us"); + System.out.println("Finish up wait "+(threadFinishWait/1000L)+"us"); + + // You should only call this method once lol. + interruptsCalled = 0; + interruptPenalty = 0L; + oddStateStallPenalty = 0L; + threadFinishWait = 0L; + fullQueueStallPenalty = 0L; + } + + + protected void wakeThread(int cmdindex) { + + // Thread's already awake if we're lingering. Skip the interrupt() call! + if (threadState.get() == STATE_LINGERING) { + return; + } + + // There's a bug if we just call wakethread after setting cmdIndex. + // I'm going to copy+paste it here: + // Let's say we're calling endCommands: + // - (1) executing cmd + // - (0) Set cmd id + // - (1) Oh look! A new command to execute. + // - (1) Done, let's go to sleep + // - (0) wakeThread (we're under the assumption that the thread hasn't done the work yet) + // - (1) Woke up, but wait! There isn't any work for me to do! + // Solution: Check cmdid is non-zero, because thread sets it to 0 as soon as it executes + // the command + if (cmdID.get(cmdindex) == NO_CMD) { + return; + } + + + + // Only need to interrupt if sleeping. + // We call it here because if wakeThread is called, then a command was called, and + // when a command was called, that means we should definitely not be asleep + // (avoids concurrency issues with await() + // If it's on STATE_NEXT_CMD, it means that it might have an outdated cmdid, which we can fix + // by simply interrupting it as soon as it eventually goes into sleep mode + + // NOTE: With the new lingering mode, it means that STATE_NEXT_CMD is deprecated. + + // Here's the new solution: + // If we're on the verge of going from lingering to entering state, we must wait for either 1 of 2 outcomes: + // - worker thread has read the updated cmdid and will update its state to STATE_RUNNING, hence we can continue + // without calling interrupt() + // OR + // - worker thread has read an outdated state (0 NO_CMD) and is now going to sleep, so we need to + // call interrupt() (bummer). + + + if (threadState.get() == STATE_ENTERING_SLEEP || threadState.get() == STATE_LINGERING_LAST_CHANCE) { // || threadState.get() == STATE_NEXT_CMD + + // Uhoh, unlucky. This means we just gotta wait until we're entering sleep state then wake up. + long before = System.nanoTime(); + while (threadState.get() != STATE_SLEEPING && threadState.get() != STATE_RUNNING) { + // Busy loop + } + oddStateStallPenalty += System.nanoTime()-before; + + // Running? We can happily continue and skip the interrupt() :D + if (threadState.get() == STATE_RUNNING) { + return; + } + + println("INTERRUPT"); + threadState.set(STATE_SLEEPING_INTERRUPTED); + + // Before we call interrupt let's do a lil benchmarking + before = System.nanoTime(); + + // Actually call interrupt + thread.interrupt(); + + interruptPenalty += System.nanoTime()-before; + interruptsCalled++; + } + + if (threadState.get() == STATE_SLEEPING) { + println("INTERRUPT"); + + // We need to set status for only one interrupt otherwise we will keep calling + // interrupt interrupt interrupt interrupt interrupt interrupt interrupt interrupt + // and it seems to be stored in some sort of queue. That means, when the thread tries + // to go back to sleep, it immediately wakes up because those interrupts are still in + // the queue. We tell it "it's been interrupted once, don't bother it any further." + threadState.set(STATE_SLEEPING_INTERRUPTED); + + // Before we call interrupt let's do a lil benchmarking + long before = System.nanoTime(); + + // Actually call interrupt + thread.interrupt(); + + interruptPenalty += System.nanoTime()-before;; + interruptsCalled++; + } + + // We also need to consider the case for when a thread is ABOUT to enter sleep mode. + // Cus we can call interrupt() all we want, it's not going to stop the thread from + // entering sleep mode. + +// sleeping.set(false); + } + + + public void drawArrays(ArrayList buffers, int size, int first) { + int index = getNextCMDIndex(); + println("call CMD_DRAW_ARRAYS (index "+index+")"); + + for (int i = 0; i < buffers.size(); i++) { + setLongArg(i, index, buffers.get(i)); + } + + setIntArg(0, index, size); + setIntArg(1, index, first); + setIntArg(2, index, buffers.size()); + // Remember, last thing we should set is cmdID, set it before and + // our thread may begin executing drawArrays without all the commands + // being properly set. + cmdID.set(index, CMD_DRAW_ARRAYS); + wakeThread(index); + } + + + public void drawIndexed(int indiciesSize, long indiciesBuffer, ArrayList vertexBuffers, int offset, int vertexOffset, int type) { +// long before = System.nanoTime(); + int index = getNextCMDIndex(); + println("call CMD_DRAW_INDEXED (index "+index+")"); + + + // Int0: indiciesSize + // Long0: indiciesBuffer + // Int1: numBuffers + // LongX: vertexBuffers + // Int2: offset/type (higher bits type, lower bits offset) + + setIntArg(0, index, indiciesSize); + setLongArg(0, index, indiciesBuffer); + setIntArg(1, index, vertexBuffers.size()); + + + // Replace last 2 bits with type + switch (type) { + case GL2VK.GL_UNSIGNED_BYTE: + // 1 + setIntArg(2, index, INDEXTYPEUINT8); + break; + case GL2VK.GL_UNSIGNED_INT: + // 2 + setIntArg(2, index, VK_INDEX_TYPE_UINT32); + break; + case GL2VK.GL_UNSIGNED_SHORT: + // 3 + setIntArg(2, index, VK_INDEX_TYPE_UINT16); + break; + } + + + setIntArg(3, index, offset); + setIntArg(4, index, vertexOffset); + for (int i = 0; i < vertexBuffers.size(); i++) { + setLongArg(i+1, index, vertexBuffers.get(i)); + } + cmdID.set(index, CMD_DRAW_INDEXED); + wakeThread(index); +// System.out.println("issue drawindexed elapsed time "+(System.nanoTime()-before)+"ns"); + } + + + // TODO: what would be much more efficient and perhaps easier would be to pass + // the literal uniform arguments e.g. + // mat4, vec2, another vec2 + // We would need a class that contains the args we wanna pass tho. + public void pushConstant(long pipelineLayout, int vertexOrFragment, int offset, ByteBuffer buffer) { + int index = getNextCMDIndex(); + println("call CMD_PUSH_CONSTANT (index "+index+")"); + // Long0: pipelineLayout + // Int0: Size/offset/vertexOrFragment + // Long1-X: bufferData (needs to be reconstructed + setLongArg(0, index, pipelineLayout); + + int size = buffer.capacity(); + + // Let's combine it into a single int argument to reduce memory usage + // Layout: ssssssssoooooooooooooooovvvvvvvv + // Remember that none of these should ever be bigger than their limits + int arg0 = 0; + arg0 |= size << 24; + arg0 |= ((offset << 8) & 0x00FFFF00); + arg0 |= (vertexOrFragment & 0x000000FF); + setIntArg(0, index, arg0); + + // Now, we need to do the unhinged: + // Stuff an entire buffer into the long args. + // If we use the entire 256 bytes of pushConstant space, + // we will need 32 long args altogether so it's not too bad I guess?? + int arg = 1; + for (int i = 0; i < size; i += Long.BYTES) { + setLongArg(arg++, index, buffer.getLong()); + } + + cmdID.set(index, CMD_PUSH_CONSTANT); + wakeThread(index); + } + + public void pushConstant(long pipelineLayout, int vertexOrFragment, int offset, FloatBuffer buffer) { + + int index = getNextCMDIndex(); + println("call CMD_PUSH_CONSTANT (index "+index+")"); + // Long0: pipelineLayout + // Int0: Size/offset/vertexOrFragment + // Long1-X: bufferData (needs to be reconstructed + setLongArg(0, index, pipelineLayout); + + int size = buffer.capacity()*4; + + // Let's combine it into a single int argument to reduce memory usage + // Layout: ssssssssoooooooooooooooovvvvvvvv + // Remember that none of these should ever be bigger than their limits + int arg0 = 0; + arg0 |= size << 24; + arg0 |= ((offset << 8) & 0x00FFFF00); + arg0 |= (vertexOrFragment & 0x000000FF); + setIntArg(0, index, arg0); + + + // Now, we need to do the unhinged: + // Stuff an entire buffer into the long args. + // If we use the entire 256 bytes of pushConstant space, + // we will need 32 long args altogether so it's not too bad I guess?? + buffer.rewind(); + int arg = 1; + for (int i = 0; i < size; i += Long.BYTES) { + float val1 = buffer.get(); + float val2 = buffer.get(); +// if (i+4 < size) val2 = buffer.get(); + + setLongArg(arg++, index, (Float.floatToIntBits(val1) & 0xFFFFFFFFL) | ((Float.floatToIntBits(val2) & 0xFFFFFFFFL) << 32)); + } + + cmdID.set(index, CMD_PUSH_CONSTANT); + wakeThread(index); + } + + private int getNextIndexForPushConstant(long pipelineLayout, int vertexOrFragment, int offset, int size) { + int index = getNextCMDIndex(); + + println("call CMD_PUSH_CONSTANT (index "+index+")"); + // Long0: pipelineLayout + // Int0: Size/offset/vertexOrFragment + // Long1-X: bufferData (needs to be reconstructed + setLongArg(0, index, pipelineLayout); + + // Let's combine it into a single int argument to reduce memory usage + // Layout: ssssssssoooooooooooooooovvvvvvvv + // Remember that none of these should ever be bigger than their limits + int arg0 = 0; + arg0 |= size << 24; + arg0 |= ((offset << 8) & 0x00FFFF00); + arg0 |= (vertexOrFragment & 0x000000FF); + setIntArg(0, index, arg0); + + return index; + } + + public void pushConstant(long pipelineLayout, int vertexOrFragment, int offset, float val) { + int index = getNextIndexForPushConstant(pipelineLayout, vertexOrFragment, offset, 4); + + setLongArg(1, index, Float.floatToIntBits(val) & 0xFFFFFFFFL); + + cmdID.set(index, CMD_PUSH_CONSTANT); + wakeThread(index); + } + + public void pushConstant(long pipelineLayout, int vertexOrFragment, int offset, float val0, float val1) { + int index = getNextIndexForPushConstant(pipelineLayout, vertexOrFragment, offset, 8); + + setLongArg(1, index, (Float.floatToIntBits(val0) & 0xFFFFFFFFL) | ((Float.floatToIntBits(val1) & 0xFFFFFFFFL) << 32)); + + cmdID.set(index, CMD_PUSH_CONSTANT); + wakeThread(index); + } + + public void pushConstant(long pipelineLayout, int vertexOrFragment, int offset, float val0, float val1, float val2) { + int index = getNextIndexForPushConstant(pipelineLayout, vertexOrFragment, offset, 12); + + setLongArg(1, index, (Float.floatToIntBits(val0) & 0xFFFFFFFFL) | ((Float.floatToIntBits(val1) & 0xFFFFFFFFL) << 32)); + setLongArg(2, index, (Float.floatToIntBits(val2) & 0xFFFFFFFFL)); + + cmdID.set(index, CMD_PUSH_CONSTANT); + wakeThread(index); + } + + public void pushConstant(long pipelineLayout, int vertexOrFragment, int offset, float val0, float val1, float val2, float val3) { + int index = getNextIndexForPushConstant(pipelineLayout, vertexOrFragment, offset, 16); + + setLongArg(1, index, (Float.floatToIntBits(val0) & 0xFFFFFFFFL) | ((Float.floatToIntBits(val1) & 0xFFFFFFFFL) << 32)); + setLongArg(2, index, (Float.floatToIntBits(val2) & 0xFFFFFFFFL) | ((Float.floatToIntBits(val3) & 0xFFFFFFFFL) << 32)); + + cmdID.set(index, CMD_PUSH_CONSTANT); + wakeThread(index); + } + + public void pushConstant(long pipelineLayout, int vertexOrFragment, int offset, int val) { + int index = getNextIndexForPushConstant(pipelineLayout, vertexOrFragment, offset, 4); + + setLongArg(1, index, val & 0xFFFFFFFFL); + + cmdID.set(index, CMD_PUSH_CONSTANT); + wakeThread(index); + } + + public void pushConstant(long pipelineLayout, int vertexOrFragment, int offset, int val0, int val1) { + int index = getNextIndexForPushConstant(pipelineLayout, vertexOrFragment, offset, 8); + + setLongArg(1, index, (val0 & 0xFFFFFFFFL) | ((val1 & 0xFFFFFFFFL) << 32)); + + cmdID.set(index, CMD_PUSH_CONSTANT); + wakeThread(index); + } + + public void pushConstant(long pipelineLayout, int vertexOrFragment, int offset, int val0, int val1, int val2) { + int index = getNextIndexForPushConstant(pipelineLayout, vertexOrFragment, offset, 12); + + setLongArg(1, index, (val0 & 0xFFFFFFFFL) | ((val1 & 0xFFFFFFFFL) << 32)); + setLongArg(2, index, (val2 & 0xFFFFFFFFL)); + + cmdID.set(index, CMD_PUSH_CONSTANT); + wakeThread(index); + } + + public void pushConstant(long pipelineLayout, int vertexOrFragment, int offset, int val0, int val1, int val2, int val3) { + int index = getNextIndexForPushConstant(pipelineLayout, vertexOrFragment, offset, 16); + + setLongArg(1, index, (val0 & 0xFFFFFFFFL) | ((val1 & 0xFFFFFFFFL) << 32)); + setLongArg(2, index, (val2 & 0xFFFFFFFFL) | ((val3 & 0xFFFFFFFFL) << 32)); + + cmdID.set(index, CMD_PUSH_CONSTANT); + wakeThread(index); + } + + +// public void bufferData(long srcBuffer, long dstBuffer, int size) { +// int index = getNextCMDIndex(); +// setLongArg(0, index, srcBuffer); +// setLongArg(1, index, dstBuffer); +// setIntArg(0, index, size); +// // Remember, last thing we should set is cmdID, set it before and +// // our thread may begin executing drawArrays without all the commands +// // being properly set. +// cmdID.set(index, CMD_BUFFER_DATA); +// wakeThread(index); +// } + + public void bindPipeline(long pipeline) { + if (currentPipeline != pipeline) { + int index = getNextCMDIndex(); + println("call CMD_BIND_PIPELINE (index "+index+")"); + currentPipeline = pipeline; + setLongArg(0, index, pipeline); + cmdID.set(index, CMD_BIND_PIPELINE); + wakeThread(index); + } + } + + public void bindDescriptorSet(long pipelineLayout, long descriptorSet) { + int index = getNextCMDIndex(); + println("call CMD_BIND_PIPELINE (index "+index+")"); + setLongArg(0, index, pipelineLayout); + setLongArg(1, index, descriptorSet); + cmdID.set(index, CMD_BIND_DESCRIPTOR); + wakeThread(index); + } + + + public void bufferData(GraphicsBuffer graphicsBuffer, int size, ByteBuffer buffer, int instance) { + int index = getNextCMDIndex(); + setIntArg(0, index, size); + byteBuffers[byteBuffersIndex] = buffer; + graphicsBuffers[graphicsBufferIndex] = graphicsBuffer; + setIntArg(1, index, byteBuffersIndex++); + setIntArg(2, index, instance); + setIntArg(3, index, graphicsBufferIndex++); + + cmdID.set(index, CMD_BUFFER_BYTE_DATA); + wakeThread(index); + } + + + public void bufferData(GraphicsBuffer graphicsBuffer, int size, FloatBuffer buffer, int instance) { + + int index = getNextCMDIndex(); + setIntArg(0, index, size); + floatBuffers[floatBuffersIndex] = buffer; + graphicsBuffers[graphicsBufferIndex] = graphicsBuffer; + setIntArg(1, index, floatBuffersIndex++); + setIntArg(2, index, instance); + setIntArg(3, index, graphicsBufferIndex++); + + cmdID.set(index, CMD_BUFFER_FLOAT_DATA); + wakeThread(index); + } + + + public void bufferData(GraphicsBuffer graphicsBuffer, int size, ShortBuffer buffer, int instance) { + int index = getNextCMDIndex(); + setIntArg(0, index, size); + shortBuffers[shortBuffersIndex] = buffer; + graphicsBuffers[graphicsBufferIndex] = graphicsBuffer; + setIntArg(1, index, shortBuffersIndex++); + setIntArg(2, index, instance); + setIntArg(3, index, graphicsBufferIndex++); + + cmdID.set(index, CMD_BUFFER_SHORT_DATA); + wakeThread(index); + } + + + public void bufferData(GraphicsBuffer graphicsBuffer, int size, LongBuffer buffer, int instance) { + int index = getNextCMDIndex(); + setIntArg(0, index, size); + longBuffers[longBuffersIndex] = buffer; + graphicsBuffers[graphicsBufferIndex] = graphicsBuffer; + setIntArg(1, index, longBuffersIndex++); + setIntArg(2, index, instance); + setIntArg(3, index, graphicsBufferIndex++); + + cmdID.set(index, CMD_BUFFER_LONG_DATA); + wakeThread(index); + } + + + public void bufferData(GraphicsBuffer graphicsBuffer, int size, IntBuffer buffer, int instance) { + int index = getNextCMDIndex(); + setIntArg(0, index, size); + intBuffers[intBuffersIndex] = buffer; + graphicsBuffers[graphicsBufferIndex] = graphicsBuffer; + setIntArg(1, index, intBuffersIndex++); + setIntArg(2, index, instance); + setIntArg(3, index, graphicsBufferIndex++); + + cmdID.set(index, CMD_BUFFER_INT_DATA); + wakeThread(index); + } + + + public void clearColor(float r, float g, float b, float a) { + int index = getNextCMDIndex(); + + setIntArg(0, index, Float.floatToIntBits(r)); + setIntArg(1, index, Float.floatToIntBits(g)); + setIntArg(2, index, Float.floatToIntBits(b)); + setIntArg(3, index, Float.floatToIntBits(a)); + + cmdID.set(index, CMD_CLEAR); + wakeThread(index); + } + + + public void beginRecord(int currentFrame, int currentImage) { + // Reset it here idk + byteBuffersIndex = 0; + shortBuffersIndex = 0; + longBuffersIndex = 0; + floatBuffersIndex = 0; + intBuffersIndex = 0; + graphicsBufferIndex = 0; + + + println("call begin record"); + int index = getNextCMDIndex(); + setIntArg(0, index, currentImage); + setIntArg(1, index, currentFrame); + cmdID.set(index, CMD_BEGIN_RECORD); + + wakeThread(index); + } + + + public void endRecord() { + int index = getNextCMDIndex(); + cmdID.set(index, CMD_END_RECORD); + println("call CMD_END_RECORD (index "+index+")"); + // No arguments + wakeThread(index); + currentPipeline = 0; + } + + public void kill() { + println("kill thread"); + int index = getNextCMDIndex(); + cmdID.set(index, CMD_KILL); + // No arguments + wakeThread(index); + } + + public void killAndCleanup() { + kill(); + // Wait for thread to end + while (threadState.get() != STATE_KILLED) { + // Do the classic ol "wait 1ms each loop" + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + // Now clean up our mess + + if (openCmdBuffer.get() == true) { + vkEndCommandBuffer(cmdbuffers[currentFrame.get()]); + } + + try(MemoryStack stack = stackPush()) { + ArrayList deleteList = new ArrayList<>(); + for (int i = 0; i < cmdbuffers.length; i++) { + deleteList.add(cmdbuffers[i]); + } + vkFreeCommandBuffers(system.device, commandPool, Util.asPointerBuffer(stack, deleteList)); + } + vkDestroyCommandPool(system.device, commandPool, null); + } + + public VkCommandBuffer getBuffer() { + return cmdbuffers[currentFrame.get()]; + } + + public void await() { + int count = 0; + // We wait until it has finished its commands + + // In order for the thread to be properly done its work it must: + // - be in sleep mode + // - its cmd buffer must be closed + + // Lets do accurate scientific measuring instead of just using count to get the + // benchmarks for threadFinishWait. + long before = System.nanoTime(); + while ( + !(threadState.get() == STATE_SLEEPING && + openCmdBuffer.get() == false) + ) { + +// try { +// Thread.sleep(1); +// } catch (InterruptedException e) { +// } +// if (count == 500) { +// System.err.println("("+this.myID+") BUG WARNING looplock'd waiting for a thread that won't respond (state "+threadState.get()+")"); +// System.exit(1); +// } +// count++; + if (System.nanoTime()-before > 500000000L) { + System.err.println("("+this.myID+") BUG WARNING looplock'd waiting for a thread that won't respond (state "+threadState.get()+")"); + System.exit(1); + } + + } + threadFinishWait += System.nanoTime()-before; + } +} \ No newline at end of file diff --git a/core/src/processing/GL2VK/UniformParser.java b/core/src/processing/GL2VK/UniformParser.java new file mode 100644 index 0000000000..3b15fdcf4c --- /dev/null +++ b/core/src/processing/GL2VK/UniformParser.java @@ -0,0 +1,332 @@ +package processing.GL2VK; + +import java.util.ArrayList; + +public class UniformParser { + + + public static ArrayList parseUniforms(String shaderSource) { + + // Split into array of lines + String[] lines = shaderSource.split("\n"); + + ArrayList uniforms = new ArrayList<>(); + + // Set to true while we're inside of a uniform struct. + // While in this state, any variables (i.e. items that start with + // vec3, float, mat4 etc) will be added as uniforms with their names. + boolean uniformStruct = false; + + int bracketDepth = 0; + for (String s : lines) { + + s = filterComments(s); + + // Here we're going to do a comparison to see if we're exiting struct + int beforeBracketDepth = bracketDepth; + + // Bracket depth so we only get attribs from outside bodies. + // You'll never see attribs/uniforms in brackets for example. + bracketDepth += countChars(s, "{"); + bracketDepth -= countChars(s, "}"); + + + // If there's a change in bracketDepth to 0 and we're in uniformStruct + // state, it means we're at the end of the struct block. + if ( + bracketDepth != beforeBracketDepth && + bracketDepth == 0 && + uniformStruct + ) { + uniformStruct = false; + } + + // If we're in the state of looking for uniforms + if (uniformStruct) { + // If it's got a variable type in it, that's a uniform. + if (hasType(s)) { + // Try/catch block because there's probably cases where it will crash + // but it seriously doesn't matter if it breaks. + try { + + // Lil filtering + // Elements makes it easier to get each individual token. + String[] elements = s.replaceAll("\t", "").trim().split(" "); + String line = s.replaceAll(" ", ""); + int offset = -1; + + // layout(offset ... ) + int startIndex = line.indexOf("layout(offset="); + if (startIndex != -1) { + int endBracked = line.indexOf(')'); + + startIndex += "layout(offset=".length(); + + // Remember if something odd happens here, the try/catch block will catch it. + offset = Integer.parseInt(line.substring(startIndex, endBracked)); + } + + // Look for the element which contains the size. + // Remember, if we have "layout( offset = ..." + // we're going to have a list of + // ["layout(", "offset" "=" ... ] + int size = -1; + for (int i = 0; i < elements.length; i++) { +// System.out.println(elements[i]); + size = typeToSize(elements[i]); + // Size found? Element that follows? + // Time to assign our variable. + if (size != -1 && i+1 < elements.length) { + // Next element should be name + String uniformName = removeSemicolon(elements[i+1]); + + // Remove ';' at the end + + // Unfortunately, we can't assign a uniform yet since the vertex and fragment + // uniforms need to be combined into one, and here if we try to assign an offset, + // currUniformOffset starts at 0 for fragment uniforms, which is incorrect. + // We will assign the offset later. + + // HOWEVER, + // If we have a layout(offset=...) then we know the offset and can + // set it directly. Later code will see that offset has already been + // assigned and won't assign it automatically. + if (offset != -1) { + uniforms.add(new GLUniform(uniformName, size, offset)); + } + else { + uniforms.add(new GLUniform(uniformName, size)); + } + break; + } + } + continue; + } + catch (RuntimeException e) { + System.out.println(e.getMessage()); + } + } + } + + + // Uniforms +// System.out.println((bracketDepth == 0) + " " + s.contains(" uniform ")); + if ( +// bracketDepth == 0 && // Not sure if this will break anything but I don't think it should...? + s.contains(" uniform ") + ) { + // Lil filtering + // Elements makes it easier to get each individual token. + String[] elements = s.split(" "); + // No spaces allowed. + String line = s.replaceAll(" ", ""); + + + // Get the type of uniform (push constant or descriptor) + int pushConstantIndex = line.indexOf("push_constant"); + + // TODO: Descriptor layouts. + // Impossible (always false) condition here. + int descriptorIndex = line.indexOf("(([amongus]))"); + + // PUSH CONSTANT PATH + if (pushConstantIndex != -1) { + + // Prolly don't need that. + String structName = ""; + + // Scroll until we find (uniform) + try { + // Search for the element "in". + for (int i = 0; i < elements.length; i++) { + if (elements[i].equals("uniform")) { + // The element after that should be the struct name. + structName = elements[i+1]; + + // We shall start to traverse the struct adding any variable we come + // across as uniforms + uniformStruct = true; + break; + } + } + } + catch (IndexOutOfBoundsException e) { +// System.err.println("Woops"); + } + continue; + } + // Descriptor path + else if (descriptorIndex != -1) { + continue; + } + // Unknown uniform type. + else { + // No continue because the line might have something else for us to scan. + } + } + } + + // One more thing before we return the uniforms; + // We are going to do something absolutely unhinged. + // We need the samplers as GLUniforms too. + // So let's call our other function and parse the whole thing again just to get samplers. + // Yeah, not the best approach, but easiest to implement for now. + parseSamplers(shaderSource); + + for (String name : tempSamplerNames) { + GLUniform u = new GLUniform(name, 0, 0); + u.isSampler = true; + uniforms.add(u); + } + + return uniforms; + } + + // Unhinged code + private static ArrayList tempSamplerNames = new ArrayList<>(); + + // Returns bindings + public static ArrayList parseSamplers(String shaderSource) { + ArrayList list = new ArrayList<>(); + String[] lines = shaderSource.split("\n"); + tempSamplerNames.clear(); + + for (String line : lines) { + line = filterComments(line); + + // Find a line that contains "uniform". + // Of course, will either be the push_constants block or will be + // a sampler variable or maybe later TODO will be a descriptorset. + if ( + line.contains(" uniform ") + ) { + // Lil filtering + // Elements makes it easier to get each individual token. + String[] elements = line.split(" "); + // No spaces allowed. + line = line.replaceAll(" ", ""); + + // find "layout(binding=x)" + int bindingIndex = line.indexOf("binding="); + + + if (bindingIndex != -1) { + + bindingIndex += "binding=".length(); + + // get the binding + int bindingEndIndex = line.indexOf(")", bindingIndex); + + int binding = -1; + if (bindingEndIndex != -1) { + binding = Integer.parseInt(line.substring(bindingIndex, bindingEndIndex)); + } + else { + // Some error line, shouldn't happen, but if it does, + // just ignore the line + continue; + } + + // We got the binding. + // But we don't stop there yet; the question is, is this a legit sampler uniform? + // If so, we can add the binding to the list. + + // Prolly don't need that. + String name = ""; + + // Might be a descriptor block, might be a sampler, + // we want it to be a sampler + try { + for (int i = 0; i < elements.length; i++) { + if (elements[i].equals("uniform") && (elements[i+1].equals("sampler1D") || elements[i+1].equals("sampler2D") || elements[i+1].equals("sampler3D"))) { + // The element after that should be the struct name. + name = removeSemicolon(elements[i+2]); + list.add(binding); + tempSamplerNames.add(name); + + break; + } + } + } + catch (IndexOutOfBoundsException e) { +// System.err.println("Woops"); + } + continue; + } + // Unknown uniform type. + else { + // No continue because the line might have something else for us to scan. + } + } + } + + return list; + } + + public static int countChars(String line, String c) { + return line.length() - line.replace(c, "").length(); + } + + public static String removeSemicolon(String line) { + if (line.charAt(line.length()-1) == ';') line = line.substring(0, line.length()-1); + return line; + } + + public static int typeToSize(String val) { + if (val.equals("float")) return 1 * Float.BYTES; + else if (val.equals("vec2")) return 2 * Float.BYTES; + else if (val.equals("vec3")) return 3 * Float.BYTES; + else if (val.equals("vec4")) return 4 * Float.BYTES; + else if (val.equals("int")) return 4 * Integer.BYTES; // TODO: Int has a size of 16 bytes??? + else if (val.equals("ivec2")) return 2 * Integer.BYTES; + else if (val.equals("ivec3")) return 3 * Integer.BYTES; + else if (val.equals("ivec4")) return 4 * Integer.BYTES; + else if (val.equals("uint")) return 1 * Integer.BYTES; + else if (val.equals("uvec2")) return 2 * Integer.BYTES; + else if (val.equals("uvec3")) return 3 * Integer.BYTES; + else if (val.equals("uvec4")) return 4 * Integer.BYTES; + else if (val.equals("bool")) return 1; + else if (val.equals("bvec2")) return 2; + else if (val.equals("bvec3")) return 3; + else if (val.equals("bvec4")) return 4; + else if (val.equals("mat2")) return 2 * 2 * Float.BYTES; + else if (val.equals("mat3")) return 3 * 3 * Float.BYTES; + else if (val.equals("mat4")) return 4 * 4 * Float.BYTES; + else return -1; + } + + public static boolean hasType(String val) { + if (val.contains("float")) return true; + else if (val.contains("vec2")) return true; + else if (val.contains("vec3")) return true; + else if (val.contains("vec4")) return true; + else if (val.contains("int")) return true; + else if (val.contains("ivec2")) return true; + else if (val.contains("ivec3")) return true; + else if (val.contains("ivec4")) return true; + else if (val.contains("uint")) return true; + else if (val.contains("uvec2")) return true; + else if (val.contains("uvec3")) return true; + else if (val.contains("uvec4")) return true; + else if (val.contains("bool")) return true; + else if (val.contains("bvec2")) return true; + else if (val.contains("bvec3")) return true; + else if (val.contains("bvec4")) return true; + else if (val.contains("mat2")) return true; + else if (val.contains("mat3")) return true; + else if (val.contains("mat4")) return true; + else if (val.contains("sampler1D")) return true; + else if (val.contains("sampler2D")) return true; + else if (val.contains("sampler3D")) return true; + else return false; + } + + // TODO: We also need for the /* */ comments + private static String filterComments(String line) { + int index = line.indexOf("//"); + if (index != -1) { + return line.substring(index); + } + return line; + } +} diff --git a/core/src/processing/GL2VK/Util.java b/core/src/processing/GL2VK/Util.java new file mode 100644 index 0000000000..0f4b071231 --- /dev/null +++ b/core/src/processing/GL2VK/Util.java @@ -0,0 +1,179 @@ +package processing.GL2VK; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.BufferUnderflowException; +import java.nio.IntBuffer; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +import javax.imageio.ImageIO; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.Pointer; + +public class Util { + public static final int UINT32_MAX = 0xFFFFFFFF; + public static final long UINT64_MAX = 0xFFFFFFFFFFFFFFFFL; + + + public static PointerBuffer asPointerBuffer(MemoryStack stack, Collection collection) { + + PointerBuffer buffer = stack.mallocPointer(collection.size()); + + collection.stream() + .map(stack::UTF8) + .forEach(buffer::put); + + return buffer.rewind(); + } + + public static PointerBuffer asPointerBuffer(MemoryStack stack, List list) { + + PointerBuffer buffer = stack.mallocPointer(list.size()); + + list.forEach(buffer::put); + + return buffer.rewind(); + } + + public static int clamp(int min, int max, int value) { + return Math.max(min, Math.min(max, value)); + } + + + public static String readFile(String relativepath) { + File f = new File("."); + String path = f.getAbsolutePath().replaceAll("\\\\", "/"); + path = path.substring(0, path.length()-1); + try { + return new String(Files.readAllBytes(Paths.get(path+relativepath))); + } catch (IOException e) { + System.err.println("ERROR IOException "+e.getMessage()); + return ""; + } + } + + + + public static HashSet disableList = new HashSet<>(); + + static { + disableList.add("useProgram"); + disableList.add("uniformMatrix4fv"); + disableList.add("bindBuffer"); + disableList.add("glVertexAttribPointer"); + disableList.add("vertexAttribPointer"); + disableList.add("drawElementsImpl"); + disableList.add("drawElements"); + + disableList.add("submitAndPresent"); + disableList.add("endrecords"); + disableList.add("awaits"); + disableList.add("executeCommands"); + disableList.add("endRenderpass"); + + } + + // Lil debugging tools here + private static long tmrnbefore = 0L; + public static void beginTmr() { + tmrnbefore = System.nanoTime(); + } + public static long endTmr(String name) { + long ns = ((System.nanoTime()-tmrnbefore)); + System.out.println( + name+": "+ns+"ns"+ + (ns > 1000 ? " ("+(ns/1000L)+"us)" : "") + + (ns > 1000000 ? " ("+(ns/1000000L)+"ms)" : "") + + (ns > 1000000000 ? " ("+(ns/1000000000L)+"s)" : "") + ); + return ns; + } + + public static long endTmrWarn(String name) { + long ns = ((System.nanoTime()-tmrnbefore)); + + String msg = + name+": "+ns+"ns"+ + (ns > 1000 ? " ("+(ns/1000L)+"us)" : "") + + (ns > 1000000 ? " ("+(ns/1000000L)+"ms)" : "") + + (ns > 1000000000 ? " ("+(ns/1000000000L)+"s)" : ""); + + if (ns > 1000000L) { + System.err.println(msg); + } + else { + System.out.println(msg); + } + + return ns; + } + + public static void saveArrayAsPNG(IntBuffer buffer, int wi, int hi, String filePath) { + buffer.rewind(); + int[][] array = new int[hi][wi]; + try { + for (int y = 0; y < hi; y++) { + for (int x = 0; x < wi; x++) { + array[y][x] = buffer.get(); + } + } + } + catch (BufferUnderflowException e) { + + } + buffer.rewind(); + + saveArrayAsPNG(array, filePath); + } + + // Generated by chatgpt for debugging purposes + public static void saveArrayAsPNG(int[][] array, String filePath) { + // Get dimensions from the array + int height = array.length; + int width = array[0].length; + + // Create a BufferedImage + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + + // Populate the BufferedImage with the pixel data from the array + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + // Set pixel value + image.setRGB(x, y, array[y][x]); + } + } + + // Write the BufferedImage to a file + try { + File outputFile = new File(filePath); + ImageIO.write(image, "png", outputFile); + System.out.println("Image saved successfully at: " + filePath); + } catch (Exception e) { + System.err.println("Error saving the image: " + e.getMessage()); + } +} + + public static int roundToMultiple8(int input) { + // let's say we have 22 + // return 24 + int m = 8; + int mod = (input%m); + if (mod == 0) return input; + return input+(m-mod); + } + + public static void emergencyExit(String... mssg) { + System.err.println("GL2VK EMERGENCY EXIT"); + for (String s : mssg) { + System.err.println(s); + } + System.exit(1); + } +} diff --git a/core/src/processing/GL2VK/VKSetup.java b/core/src/processing/GL2VK/VKSetup.java new file mode 100644 index 0000000000..6263f80bca --- /dev/null +++ b/core/src/processing/GL2VK/VKSetup.java @@ -0,0 +1,1209 @@ +package processing.GL2VK; + +import static java.util.stream.Collectors.toSet; +import static org.lwjgl.glfw.GLFW.glfwGetFramebufferSize; +import static org.lwjgl.glfw.GLFW.glfwWaitEvents; +import static org.lwjgl.glfw.GLFWVulkan.glfwCreateWindowSurface; +import static org.lwjgl.glfw.GLFWVulkan.glfwGetRequiredInstanceExtensions; +import static org.lwjgl.system.MemoryStack.stackGet; +import static org.lwjgl.system.MemoryStack.stackPush; +import static org.lwjgl.system.MemoryUtil.NULL; +import static org.lwjgl.vulkan.EXTDebugUtils.VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; +import static org.lwjgl.vulkan.EXTDebugUtils.VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; +import static org.lwjgl.vulkan.EXTDebugUtils.VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; +import static org.lwjgl.vulkan.EXTDebugUtils.VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT; +import static org.lwjgl.vulkan.EXTDebugUtils.VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; +import static org.lwjgl.vulkan.EXTDebugUtils.VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; +import static org.lwjgl.vulkan.EXTDebugUtils.VK_EXT_DEBUG_UTILS_EXTENSION_NAME; +import static org.lwjgl.vulkan.EXTDebugUtils.VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; +import static org.lwjgl.vulkan.EXTDebugUtils.vkCreateDebugUtilsMessengerEXT; +import static org.lwjgl.vulkan.EXTDebugUtils.vkDestroyDebugUtilsMessengerEXT; +import static org.lwjgl.vulkan.KHRSurface.VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; +import static org.lwjgl.vulkan.KHRSurface.VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; +import static org.lwjgl.vulkan.KHRSurface.VK_PRESENT_MODE_FIFO_KHR; +import static org.lwjgl.vulkan.KHRSurface.VK_PRESENT_MODE_MAILBOX_KHR; +import static org.lwjgl.vulkan.KHRSurface.vkGetPhysicalDeviceSurfaceCapabilitiesKHR; +import static org.lwjgl.vulkan.KHRSurface.vkGetPhysicalDeviceSurfaceFormatsKHR; +import static org.lwjgl.vulkan.KHRSurface.vkGetPhysicalDeviceSurfacePresentModesKHR; +import static org.lwjgl.vulkan.KHRSurface.vkGetPhysicalDeviceSurfaceSupportKHR; +import static org.lwjgl.vulkan.KHRSwapchain.VK_KHR_SWAPCHAIN_EXTENSION_NAME; +import static org.lwjgl.vulkan.KHRSwapchain.VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; +import static org.lwjgl.vulkan.KHRSwapchain.vkCreateSwapchainKHR; +import static org.lwjgl.vulkan.KHRSwapchain.vkDestroySwapchainKHR; +import static org.lwjgl.vulkan.KHRSwapchain.vkGetSwapchainImagesKHR; +import static org.lwjgl.vulkan.VK10.*; + + +import java.nio.IntBuffer; +import java.nio.LongBuffer; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkAllocationCallbacks; +import org.lwjgl.vulkan.VkApplicationInfo; +import org.lwjgl.vulkan.VkBufferCopy; +import org.lwjgl.vulkan.VkBufferCreateInfo; +import org.lwjgl.vulkan.VkBufferImageCopy; +import org.lwjgl.vulkan.VkCommandBufferBeginInfo; +import org.lwjgl.vulkan.VkCommandPoolCreateInfo; +import org.lwjgl.vulkan.VkDebugUtilsMessengerCallbackDataEXT; +import org.lwjgl.vulkan.VkDebugUtilsMessengerCreateInfoEXT; +import org.lwjgl.vulkan.VkDevice; +import org.lwjgl.vulkan.VkDeviceCreateInfo; +import org.lwjgl.vulkan.VkDeviceQueueCreateInfo; +import org.lwjgl.vulkan.VkExtensionProperties; +import org.lwjgl.vulkan.VkExtent2D; +import org.lwjgl.vulkan.VkExtent3D; +import org.lwjgl.vulkan.VkFormatProperties; +import org.lwjgl.vulkan.VkImageCreateInfo; +import org.lwjgl.vulkan.VkImageMemoryBarrier; +import org.lwjgl.vulkan.VkImageViewCreateInfo; +import org.lwjgl.vulkan.VkInstance; +import org.lwjgl.vulkan.VkInstanceCreateInfo; +import org.lwjgl.vulkan.VkLayerProperties; +import org.lwjgl.vulkan.VkPhysicalDevice; +import org.lwjgl.vulkan.VkPhysicalDeviceFeatures; +import org.lwjgl.vulkan.VkPhysicalDeviceMemoryProperties; +import org.lwjgl.vulkan.VkPhysicalDeviceProperties; +import org.lwjgl.vulkan.VkQueue; +import org.lwjgl.vulkan.VkQueueFamilyProperties; +import org.lwjgl.vulkan.VkSubmitInfo; +import org.lwjgl.vulkan.VkSurfaceCapabilitiesKHR; +import org.lwjgl.vulkan.VkSurfaceFormatKHR; +import org.lwjgl.vulkan.VkSwapchainCreateInfoKHR; + +import processing.vulkan.PSurfaceVK; + +import org.lwjgl.vulkan.VkCommandBuffer; +import org.lwjgl.vulkan.VkMemoryRequirements; +import org.lwjgl.vulkan.VkMemoryAllocateInfo; +import org.lwjgl.vulkan.VkCommandBufferAllocateInfo; + + +// Vulkan is highly customisable. +// VR, 3D screens, offscreen buffer, validation layers, rendering straight to +// the display etc etc. +// Let's use the default setup and dump it all into this one file. + +public class VKSetup { + + public static final boolean ENABLE_VALIDATION_LAYERS = false; //DEBUG.get(true); + + private static final Set VALIDATION_LAYERS; + static { + if(ENABLE_VALIDATION_LAYERS) { + VALIDATION_LAYERS = new HashSet<>(); + VALIDATION_LAYERS.add("VK_LAYER_KHRONOS_validation"); + } else { + // We are not going to use it, so we don't create it + VALIDATION_LAYERS = null; + } + } + + private static final int OPERATION_BUFFER = 1; + private static final int OPERATION_TEXTURE = 2; + + + public VkInstance instance; + public long debugMessenger; + + public VkQueue graphicsQueue; + public VkQueue presentQueue; + + public long swapChain; + public List swapChainImages; + public int swapChainImageFormat; + public VkExtent2D swapChainExtent; + public List swapChainImageViews; + + public VkPhysicalDevice physicalDevice; + public VkDevice device; + public PSurfaceVK vkwindow; + public long window; + private VkCommandBuffer transferCommandBuffer; + public VkQueue transferQueue; + public long commandPool; + public long transferCommandPool; + public QueueFamilyIndices queueIndicies; + public int pushConstantsSizeLimit = 0; + + public boolean useTransferQueue = true; + + public void initBase(PSurfaceVK surface) { + vkwindow = surface; + window = vkwindow.glfwwindow; + createInstance(); + setupDebugMessenger(); + vkwindow.createGLFWSurface(instance); + pickPhysicalDevice(); + createLogicalDevice(); + createSwapChain(); + createImageViews(); + } + + + + private static final Set DEVICE_EXTENSIONS = Stream.of(VK_KHR_SWAPCHAIN_EXTENSION_NAME) + .collect(toSet()); + + + + private static int debugCallback(int messageSeverity, int messageType, long pCallbackData, long pUserData) { + + VkDebugUtilsMessengerCallbackDataEXT callbackData = VkDebugUtilsMessengerCallbackDataEXT.create(pCallbackData); + + System.err.println("Validation layer: " + callbackData.pMessageString()); + + return VK_FALSE; + } + + private static int createDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerCreateInfoEXT createInfo, + VkAllocationCallbacks allocationCallbacks, LongBuffer pDebugMessenger) { + + if(vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT") != NULL) { + return vkCreateDebugUtilsMessengerEXT(instance, createInfo, allocationCallbacks, pDebugMessenger); + } + + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + public static void destroyDebugUtilsMessengerEXT(VkInstance instance, long debugMessenger, VkAllocationCallbacks allocationCallbacks) { + + if(vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT") != NULL) { + vkDestroyDebugUtilsMessengerEXT(instance, debugMessenger, allocationCallbacks); + } + + } + + public void destroyOtherThings() { + // Only need to destroy these "other things" (transfer stuff) + // if we're using the transfer queue. + if (useTransferQueue) { + try(MemoryStack stack = stackPush()) { + ArrayList deleteList = new ArrayList<>(); + deleteList.add(transferCommandBuffer); + vkFreeCommandBuffers(device, transferCommandPool, Util.asPointerBuffer(stack, deleteList)); + } + vkDestroyCommandPool(device, transferCommandPool, null); + } + } + + public class QueueFamilyIndices { + + // We use Integer to use null as the empty value + public Integer graphicsFamily; + public Integer presentFamily; + public Integer transferFamily; + + private boolean isComplete() { + if (useTransferQueue) + return graphicsFamily != null && presentFamily != null && transferFamily != null; + else + return graphicsFamily != null && presentFamily != null; + } + + public int[] unique() { + if (useTransferQueue) + return IntStream.of(graphicsFamily, presentFamily, transferFamily).distinct().toArray(); + else + return IntStream.of(graphicsFamily, presentFamily).distinct().toArray(); + } + + public int[] array() { + if (useTransferQueue) + return new int[] {graphicsFamily, presentFamily, transferFamily}; + else + return new int[] {graphicsFamily, presentFamily}; + } + } + + public class SwapChainSupportDetails { + + private VkSurfaceCapabilitiesKHR capabilities; + private VkSurfaceFormatKHR.Buffer formats; + private IntBuffer presentModes; + + } + + public void createInstance() { + + if(ENABLE_VALIDATION_LAYERS && !checkValidationLayerSupport()) { + throw new RuntimeException("Validation requested but not supported"); + } + + try(MemoryStack stack = stackPush()) { + + // Use calloc to initialize the structs with 0s. Otherwise, the program can crash due to random values + + VkApplicationInfo appInfo = VkApplicationInfo.calloc(stack); + + appInfo.sType(VK_STRUCTURE_TYPE_APPLICATION_INFO); + appInfo.pApplicationName(stack.UTF8Safe("Hello Triangle")); + appInfo.applicationVersion(VK_MAKE_VERSION(1, 0, 0)); + appInfo.pEngineName(stack.UTF8Safe("No Engine")); + appInfo.engineVersion(VK_MAKE_VERSION(1, 0, 0)); + appInfo.apiVersion(VK_API_VERSION_1_0); + + VkInstanceCreateInfo createInfo = VkInstanceCreateInfo.calloc(stack); + + createInfo.sType(VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO); + createInfo.pApplicationInfo(appInfo); + // enabledExtensionCount is implicitly set when you call ppEnabledExtensionNames + createInfo.ppEnabledExtensionNames(getRequiredExtensions(stack)); + + if(ENABLE_VALIDATION_LAYERS) { + + createInfo.ppEnabledLayerNames(Util.asPointerBuffer(stack, VALIDATION_LAYERS)); + + VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo = VkDebugUtilsMessengerCreateInfoEXT.calloc(stack); + populateDebugMessengerCreateInfo(debugCreateInfo); + createInfo.pNext(debugCreateInfo.address()); + } + + // We need to retrieve the pointer of the created instance + PointerBuffer instancePtr = stack.mallocPointer(1); + + if(vkCreateInstance(createInfo, null, instancePtr) != VK_SUCCESS) { + throw new RuntimeException("Failed to create instance"); + } + + instance = new VkInstance(instancePtr.get(0), createInfo); + } + } + + + + private void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo) { + debugCreateInfo.sType(VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT); + debugCreateInfo.messageSeverity(VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT); + debugCreateInfo.messageType(VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT); + debugCreateInfo.pfnUserCallback(VKSetup::debugCallback); + } + + private void setupDebugMessenger() { + + if(!ENABLE_VALIDATION_LAYERS) { + return; + } + + try(MemoryStack stack = stackPush()) { + + VkDebugUtilsMessengerCreateInfoEXT createInfo = VkDebugUtilsMessengerCreateInfoEXT.calloc(stack); + + populateDebugMessengerCreateInfo(createInfo); + + LongBuffer pDebugMessenger = stack.longs(VK_NULL_HANDLE); + + if(createDebugUtilsMessengerEXT(instance, createInfo, null, pDebugMessenger) != VK_SUCCESS) { + throw new RuntimeException("Failed to set up debug messenger"); + } + + debugMessenger = pDebugMessenger.get(0); + } + } + + + + public void createCommandPool() { + + try(MemoryStack stack = stackPush()) { + + // Part 1: pool for graphics queue + + // God i wish this shiz was easier to read. + VkCommandPoolCreateInfo poolInfo = VkCommandPoolCreateInfo.calloc(stack); + poolInfo.sType(VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO); + // Important: VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT (i had bugs lol) + poolInfo.flags(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); + // graphics family + poolInfo.queueFamilyIndex(queueIndicies.graphicsFamily); + + // create our command pool vk + LongBuffer pCommandPool = stack.mallocLong(1); + if (vkCreateCommandPool(device, poolInfo, null, pCommandPool) != VK_SUCCESS) { + throw new RuntimeException("Failed to create command pool"); + } + // Boom. Assign'd + commandPool = pCommandPool.get(0); + + // ===> Part 2: Create the transfer command pool <=== + // BUT ONLY IF WE'RE ALLOWED!!! + if (useTransferQueue) { + poolInfo.queueFamilyIndex(queueIndicies.transferFamily); + // poolInfo.flags(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); + + // Tell Vulkan that the buffers of this pool will be constantly rerecorded + // poolInfo.flags(VK_COMMAND_POOL_CREATE_TRANSIENT_BIT); + poolInfo.flags(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); + + // Create our transfer command pool + if (vkCreateCommandPool(device, poolInfo, null, pCommandPool) != VK_SUCCESS) { + throw new RuntimeException("Failed to create command pool"); + } + // Boom. Assign'd again. + transferCommandPool = pCommandPool.get(0); + + allocateTransferCommandBuffer(); + } + } + } + + private void allocateTransferCommandBuffer() { + + try(MemoryStack stack = stackPush()) { + + VkCommandBufferAllocateInfo allocInfo = VkCommandBufferAllocateInfo.calloc(stack); + allocInfo.sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO); + allocInfo.level(VK_COMMAND_BUFFER_LEVEL_PRIMARY); + allocInfo.commandPool(transferCommandPool); + allocInfo.commandBufferCount(1); + + PointerBuffer pCommandBuffer = stack.mallocPointer(1); + vkAllocateCommandBuffers(device, allocInfo, pCommandBuffer); + transferCommandBuffer = new VkCommandBuffer(pCommandBuffer.get(0), device); + } + } + + private void createLogicalDevice() { + + try(MemoryStack stack = stackPush()) { + + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + queueIndicies = indices; + + int[] uniqueQueueFamilies = indices.unique(); + + VkDeviceQueueCreateInfo.Buffer queueCreateInfos = VkDeviceQueueCreateInfo.calloc(uniqueQueueFamilies.length, stack); + + for(int i = 0;i < uniqueQueueFamilies.length;i++) { + VkDeviceQueueCreateInfo queueCreateInfo = queueCreateInfos.get(i); + queueCreateInfo.sType(VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO); + queueCreateInfo.queueFamilyIndex(uniqueQueueFamilies[i]); + queueCreateInfo.pQueuePriorities(stack.floats(1.0f)); + } + + VkPhysicalDeviceFeatures deviceFeatures = VkPhysicalDeviceFeatures.calloc(stack); + deviceFeatures.samplerAnisotropy(true); + + VkDeviceCreateInfo createInfo = VkDeviceCreateInfo.calloc(stack); + + createInfo.sType(VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO); + createInfo.pQueueCreateInfos(queueCreateInfos); + // queueCreateInfoCount is automatically set + + createInfo.pEnabledFeatures(deviceFeatures); + + createInfo.ppEnabledExtensionNames(Util.asPointerBuffer(stack, DEVICE_EXTENSIONS)); + + if(ENABLE_VALIDATION_LAYERS) { + createInfo.ppEnabledLayerNames(Util.asPointerBuffer(stack, VALIDATION_LAYERS)); + } + + PointerBuffer pDevice = stack.pointers(VK_NULL_HANDLE); + + if(vkCreateDevice(physicalDevice, createInfo, null, pDevice) != VK_SUCCESS) { + throw new RuntimeException("Failed to create logical device"); + } + + device = new VkDevice(pDevice.get(0), physicalDevice, createInfo); + + PointerBuffer pQueue = stack.pointers(VK_NULL_HANDLE); + + vkGetDeviceQueue(device, indices.graphicsFamily, 0, pQueue); + graphicsQueue = new VkQueue(pQueue.get(0), device); + + vkGetDeviceQueue(device, indices.presentFamily, 0, pQueue); + presentQueue = new VkQueue(pQueue.get(0), device); + + if (useTransferQueue) { + vkGetDeviceQueue(device, indices.transferFamily, 0, pQueue); + transferQueue = new VkQueue(pQueue.get(0), device); + } + } + } + + + private VkSurfaceFormatKHR chooseSwapSurfaceFormat(VkSurfaceFormatKHR.Buffer availableFormats) { +// return availableFormats.stream() +// .filter(availableFormat -> availableFormat.format() == VK_FORMAT_B8G8R8_UNORM) +// .filter(availableFormat -> availableFormat.colorSpace() == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) +// .findAny() +// .orElse(availableFormats.get(0)); + + // We want one-to-one with the Processing program so no nonlinear "corrected" colors. + + return availableFormats.stream() + .filter(availableFormat -> availableFormat.format() == VK_FORMAT_B8G8R8_UNORM) + .findAny() + .orElse(availableFormats.get(0)); + } + + private int chooseSwapPresentMode(IntBuffer availablePresentModes) { + + for(int i = 0;i < availablePresentModes.capacity();i++) { + if(availablePresentModes.get(i) == VK_PRESENT_MODE_MAILBOX_KHR) { + return availablePresentModes.get(i); + } + } + + return VK_PRESENT_MODE_FIFO_KHR; + } + + private VkExtent2D chooseSwapExtent(MemoryStack stack, VkSurfaceCapabilitiesKHR capabilities) { + + if(capabilities.currentExtent().width() != Util.UINT32_MAX) { + return capabilities.currentExtent(); + } + + IntBuffer width = stackGet().ints(0); + IntBuffer height = stackGet().ints(0); + + glfwGetFramebufferSize(window, width, height); + + VkExtent2D actualExtent = VkExtent2D.malloc(stack).set(width.get(0), height.get(0)); + + VkExtent2D minExtent = capabilities.minImageExtent(); + VkExtent2D maxExtent = capabilities.maxImageExtent(); + + actualExtent.width(Util.clamp(minExtent.width(), maxExtent.width(), actualExtent.width())); + actualExtent.height(Util.clamp(minExtent.height(), maxExtent.height(), actualExtent.height())); + + + return actualExtent; + } + + + + + public void createSwapChain() { + + try(MemoryStack stack = stackPush()) { + + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice, stack); + + VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); + int presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); + VkExtent2D extent = chooseSwapExtent(stack, swapChainSupport.capabilities); + + IntBuffer imageCount = stack.ints(swapChainSupport.capabilities.minImageCount() + 1); + + if(swapChainSupport.capabilities.maxImageCount() > 0 && imageCount.get(0) > swapChainSupport.capabilities.maxImageCount()) { + imageCount.put(0, swapChainSupport.capabilities.maxImageCount()); + } + + VkSwapchainCreateInfoKHR createInfo = VkSwapchainCreateInfoKHR.calloc(stack); + + createInfo.sType(VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR); + createInfo.surface(vkwindow.glfwsurface); + + // Image settings + createInfo.minImageCount(imageCount.get(0)); + createInfo.imageFormat(surfaceFormat.format()); + createInfo.imageColorSpace(surfaceFormat.colorSpace()); + createInfo.imageExtent(extent); + createInfo.imageArrayLayers(1); + createInfo.imageUsage(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); + + + if(!queueIndicies.graphicsFamily.equals(queueIndicies.presentFamily)) { + createInfo.imageSharingMode(VK_SHARING_MODE_CONCURRENT); + createInfo.pQueueFamilyIndices(stack.ints(queueIndicies.graphicsFamily, queueIndicies.presentFamily)); + } else { + createInfo.imageSharingMode(VK_SHARING_MODE_EXCLUSIVE); + } + + createInfo.preTransform(swapChainSupport.capabilities.currentTransform()); + createInfo.compositeAlpha(VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR); + createInfo.presentMode(presentMode); + createInfo.clipped(true); + + createInfo.oldSwapchain(VK_NULL_HANDLE); + + LongBuffer pSwapChain = stack.longs(VK_NULL_HANDLE); + + if(vkCreateSwapchainKHR(device, createInfo, null, pSwapChain) != VK_SUCCESS) { + throw new RuntimeException("Failed to create swap chain"); + } + + swapChain = pSwapChain.get(0); + + vkGetSwapchainImagesKHR(device, swapChain, imageCount, null); + + LongBuffer pSwapchainImages = stack.mallocLong(imageCount.get(0)); + + vkGetSwapchainImagesKHR(device, swapChain, imageCount, pSwapchainImages); + + swapChainImages = new ArrayList<>(imageCount.get(0)); + + for(int i = 0;i < pSwapchainImages.capacity();i++) { + swapChainImages.add(pSwapchainImages.get(i)); + } + + swapChainImageFormat = surfaceFormat.format(); + swapChainExtent = VkExtent2D.create().set(extent); + } + } + + + private void pickPhysicalDevice() { + + try(MemoryStack stack = stackPush()) { + + IntBuffer deviceCount = stack.ints(0); + + vkEnumeratePhysicalDevices(instance, deviceCount, null); + + if(deviceCount.get(0) == 0) { + throw new RuntimeException("Failed to find GPUs with Vulkan support"); + } + + PointerBuffer ppPhysicalDevices = stack.mallocPointer(deviceCount.get(0)); + + vkEnumeratePhysicalDevices(instance, deviceCount, ppPhysicalDevices); + + for(int i = 0;i < ppPhysicalDevices.capacity();i++) { + + VkPhysicalDevice device = new VkPhysicalDevice(ppPhysicalDevices.get(i), instance); + + if(isDeviceSuitable(device)) { + physicalDevice = device; + return; + } + } + + throw new RuntimeException("Failed to find a suitable GPU"); + } + } + + + private boolean isDeviceSuitable(VkPhysicalDevice device) { + + boolean extensionsSupported = checkDeviceExtensionSupport(device); + boolean swapChainAdequate = false; + boolean integrated = false; + boolean discrete = false; + int pushConstantsSize = 0; + boolean anisotropySupported = false; + + if(extensionsSupported) { + try(MemoryStack stack = stackPush()) { + + VkPhysicalDeviceProperties deviceProperties = VkPhysicalDeviceProperties.malloc(stack); + VkPhysicalDeviceFeatures deviceFeatures = VkPhysicalDeviceFeatures.malloc(stack); + vkGetPhysicalDeviceProperties(device, deviceProperties); + vkGetPhysicalDeviceFeatures(device, deviceFeatures); + + pushConstantsSize = deviceProperties.limits().maxPushConstantsSize(); + + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device, stack); + swapChainAdequate = swapChainSupport.formats.hasRemaining() && swapChainSupport.presentModes.hasRemaining(); + integrated = deviceProperties.deviceType() == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; + discrete = deviceProperties.deviceType() == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU; + + VkPhysicalDeviceFeatures supportedFeatures = VkPhysicalDeviceFeatures.malloc(stack); + vkGetPhysicalDeviceFeatures(device, supportedFeatures); + anisotropySupported = supportedFeatures.samplerAnisotropy(); + + } + } + + boolean suitable = findQueueFamilies(device).isComplete() && extensionsSupported && swapChainAdequate && integrated && anisotropySupported; + // Set values here cus I'm too lazy to do it properly + if (suitable) { + pushConstantsSizeLimit = pushConstantsSize; + } + return suitable; + } + + + public QueueFamilyIndices findQueueFamilies() { + return findQueueFamilies(physicalDevice); + } + + + private QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { + + QueueFamilyIndices indices = new QueueFamilyIndices(); + + try(MemoryStack stack = stackPush()) { + + IntBuffer queueFamilyCount = stack.ints(0); + + vkGetPhysicalDeviceQueueFamilyProperties(device, queueFamilyCount, null); + + VkQueueFamilyProperties.Buffer queueFamilies = VkQueueFamilyProperties.malloc(queueFamilyCount.get(0), stack); + + vkGetPhysicalDeviceQueueFamilyProperties(device, queueFamilyCount, queueFamilies); + + IntBuffer presentSupport = stack.ints(VK_FALSE); + + + for(int i = 0; i < queueFamilies.capacity() || !indices.isComplete(); i++) { + + if((queueFamilies.get(i).queueFlags() & VK_QUEUE_GRAPHICS_BIT) != 0) { + indices.graphicsFamily = i; + } + // Only if transferQueue is enabled. + else if (useTransferQueue && ((queueFamilies.get(i).queueFlags() & VK_QUEUE_TRANSFER_BIT) != 0)) { + indices.transferFamily = i; + } + // Crash fix + // If the transferFamily has not been assigned yet and we're at the end of the + // list, give up with the whole queueFamily thing. + else if (useTransferQueue && i == queueFamilies.capacity()-1 && indices.transferFamily == null) { + useTransferQueue = false; + } + + // In case of having only 1 queueFamily, it means we're gonna get a validation error if we + // try using a transfer queue so let's not use a transfer queue. + // Only if transferQueue is enabled. + if(useTransferQueue && queueFamilies.capacity() == 1) { +// indices.transferFamily = i; + useTransferQueue = false; + } + + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, vkwindow.glfwsurface, presentSupport); + + if(presentSupport.get(0) == VK_TRUE) { + indices.presentFamily = i; + } + + } + + return indices; + } + } + + + + private SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device, MemoryStack stack) { + + SwapChainSupportDetails details = new SwapChainSupportDetails(); + + details.capabilities = VkSurfaceCapabilitiesKHR.malloc(stack); + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, vkwindow.glfwsurface, details.capabilities); + + IntBuffer count = stack.ints(0); + + vkGetPhysicalDeviceSurfaceFormatsKHR(device, vkwindow.glfwsurface, count, null); + + if(count.get(0) != 0) { + details.formats = VkSurfaceFormatKHR.malloc(count.get(0), stack); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, vkwindow.glfwsurface, count, details.formats); + } + + vkGetPhysicalDeviceSurfacePresentModesKHR(device,vkwindow.glfwsurface, count, null); + + if(count.get(0) != 0) { + details.presentModes = stack.mallocInt(count.get(0)); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, vkwindow.glfwsurface, count, details.presentModes); + } + + return details; + } + + boolean checkDeviceExtensionSupport(VkPhysicalDevice device) { + + try(MemoryStack stack = stackPush()) { + + IntBuffer extensionCount = stack.ints(0); + + vkEnumerateDeviceExtensionProperties(device, (String)null, extensionCount, null); + + VkExtensionProperties.Buffer availableExtensions = VkExtensionProperties.malloc(extensionCount.get(0), stack); + + vkEnumerateDeviceExtensionProperties(device, (String)null, extensionCount, availableExtensions); + + return availableExtensions.stream() + .map(VkExtensionProperties::extensionNameString) + .collect(toSet()) + .containsAll(DEVICE_EXTENSIONS); + } + } + + + + private boolean checkValidationLayerSupport() { + + try(MemoryStack stack = stackPush()) { + + IntBuffer layerCount = stack.ints(0); + + vkEnumerateInstanceLayerProperties(layerCount, null); + + VkLayerProperties.Buffer availableLayers = VkLayerProperties.malloc(layerCount.get(0), stack); + + vkEnumerateInstanceLayerProperties(layerCount, availableLayers); + + Set availableLayerNames = availableLayers.stream() + .map(VkLayerProperties::layerNameString) + .collect(toSet()); + + return availableLayerNames.containsAll(VALIDATION_LAYERS); + } + } + + + public void createImageViews() { + + swapChainImageViews = new ArrayList<>(swapChainImages.size()); + + for(long swapChainImage : swapChainImages) { + swapChainImageViews.add(createImageView(swapChainImage, swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT)); + } + } + + + public long createImageView(long image, int format) { + return createImageView(image, format, VK_IMAGE_ASPECT_COLOR_BIT); + } + + public void createImage(int width, int height, int format, int tiling, int usage, int memProperties, + LongBuffer pTextureImage, LongBuffer pTextureImageMemory) { + + try(MemoryStack stack = stackPush()) { + + VkImageCreateInfo imageInfo = VkImageCreateInfo.calloc(stack); + imageInfo.sType(VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO); + imageInfo.imageType(VK_IMAGE_TYPE_2D); + imageInfo.extent().width(width); + imageInfo.extent().height(height); + imageInfo.extent().depth(1); + imageInfo.mipLevels(1); + imageInfo.arrayLayers(1); + imageInfo.format(format); + imageInfo.tiling(tiling); + imageInfo.initialLayout(VK_IMAGE_LAYOUT_UNDEFINED); + imageInfo.usage(usage); + imageInfo.samples(VK_SAMPLE_COUNT_1_BIT); + imageInfo.sharingMode(VK_SHARING_MODE_EXCLUSIVE); + + if(vkCreateImage(device, imageInfo, null, pTextureImage) != VK_SUCCESS) { + throw new RuntimeException("Failed to create image"); + } + + VkMemoryRequirements memRequirements = VkMemoryRequirements.malloc(stack); + vkGetImageMemoryRequirements(device, pTextureImage.get(0), memRequirements); + + VkMemoryAllocateInfo allocInfo = VkMemoryAllocateInfo.calloc(stack); + allocInfo.sType(VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO); + allocInfo.allocationSize(memRequirements.size()); + allocInfo.memoryTypeIndex(findMemoryType(stack, memRequirements.memoryTypeBits(), memProperties)); + + if(vkAllocateMemory(device, allocInfo, null, pTextureImageMemory) != VK_SUCCESS) { + throw new RuntimeException("Failed to allocate image memory"); + } + + vkBindImageMemory(device, pTextureImage.get(0), pTextureImageMemory.get(0), 0); + } + } + + + public long createImageView(long image, int format, int aspectFlags) { + try(MemoryStack stack = stackPush()) { + + VkImageViewCreateInfo viewInfo = VkImageViewCreateInfo.calloc(stack); + viewInfo.sType(VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO); + viewInfo.image(image); + viewInfo.viewType(VK_IMAGE_VIEW_TYPE_2D); + viewInfo.format(format); + + viewInfo.components().r(VK_COMPONENT_SWIZZLE_IDENTITY); + viewInfo.components().g(VK_COMPONENT_SWIZZLE_IDENTITY); + viewInfo.components().b(VK_COMPONENT_SWIZZLE_IDENTITY); + viewInfo.components().a(VK_COMPONENT_SWIZZLE_IDENTITY); + + viewInfo.subresourceRange().aspectMask(aspectFlags); + viewInfo.subresourceRange().baseMipLevel(0); + viewInfo.subresourceRange().levelCount(1); + viewInfo.subresourceRange().baseArrayLayer(0); + viewInfo.subresourceRange().layerCount(1); + + LongBuffer pImageView = stack.mallocLong(1); + + if(vkCreateImageView(device, viewInfo, null, pImageView) != VK_SUCCESS) { + throw new RuntimeException("Failed to create texture image view"); + } + + return pImageView.get(0); + } + } + + public void createBuffer(long size, int usage, int properties, LongBuffer pBuffer, LongBuffer pBufferMemory) { + + try(MemoryStack stack = stackPush()) { + + VkBufferCreateInfo bufferInfo = VkBufferCreateInfo.calloc(stack); + bufferInfo.sType(VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO); + bufferInfo.size(size); + bufferInfo.usage(usage); + + // Change the sharing mode to concurrent (it will be shared between graphics and transfer queues) + // but only if useTransferQueue enabled + if (useTransferQueue) { + QueueFamilyIndices queueFamilies = findQueueFamilies(physicalDevice); + bufferInfo.pQueueFamilyIndices(stack.ints(queueFamilies.graphicsFamily, queueFamilies.transferFamily)); + bufferInfo.sharingMode(VK_SHARING_MODE_CONCURRENT); + } + else { + bufferInfo.sharingMode(VK_SHARING_MODE_EXCLUSIVE); + } + + if(vkCreateBuffer(device, bufferInfo, null, pBuffer) != VK_SUCCESS) { + throw new RuntimeException("Failed to create vertex buffer"); + } + + VkMemoryRequirements memRequirements = VkMemoryRequirements.malloc(stack); + vkGetBufferMemoryRequirements(device, pBuffer.get(0), memRequirements); + + VkMemoryAllocateInfo allocInfo = VkMemoryAllocateInfo.calloc(stack); + allocInfo.sType(VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO); + allocInfo.allocationSize(memRequirements.size()); + allocInfo.memoryTypeIndex(findMemoryType(stack, memRequirements.memoryTypeBits(), properties)); + + + if(vkAllocateMemory(device, allocInfo, null, pBufferMemory) != VK_SUCCESS) { + throw new RuntimeException("Failed to allocate vertex buffer memory"); + } + + vkBindBufferMemory(device, pBuffer.get(0), pBufferMemory.get(0), 0); + } + } + + + + + private void copyBufferDefault(long srcBuffer, long dstBuffer, int widthsize, int height, int operation) { + + try(MemoryStack stack = stackPush()) { + + VkCommandBufferAllocateInfo allocInfo = VkCommandBufferAllocateInfo.calloc(stack); + allocInfo.sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO); + allocInfo.level(VK_COMMAND_BUFFER_LEVEL_PRIMARY); + allocInfo.commandPool(commandPool); + allocInfo.commandBufferCount(1); + + PointerBuffer pCommandBuffer = stack.mallocPointer(1); + vkAllocateCommandBuffers(device, allocInfo, pCommandBuffer); + VkCommandBuffer commandBuffer = new VkCommandBuffer(pCommandBuffer.get(0), device); + + VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.calloc(stack); + beginInfo.sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO); + beginInfo.flags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + + vkBeginCommandBuffer(commandBuffer, beginInfo); + copyCommandOperation(srcBuffer, dstBuffer, widthsize, height, commandBuffer, operation); + vkEndCommandBuffer(commandBuffer); + + VkSubmitInfo submitInfo = VkSubmitInfo.calloc(stack); + submitInfo.sType(VK_STRUCTURE_TYPE_SUBMIT_INFO); + submitInfo.pCommandBuffers(pCommandBuffer); + + if(vkQueueSubmit(graphicsQueue, submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) { + throw new RuntimeException("Failed to submit copy command buffer"); + } + + vkQueueWaitIdle(graphicsQueue); + + vkFreeCommandBuffers(device, commandPool, pCommandBuffer); + } + } + + public void transitionImageLayout(long image, int format, int oldLayout, int newLayout) { + + try(MemoryStack stack = stackPush()) { + + VkImageMemoryBarrier.Buffer barrier = VkImageMemoryBarrier.calloc(1, stack); + barrier.sType(VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER); + barrier.oldLayout(oldLayout); + barrier.newLayout(newLayout); + barrier.srcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED); + barrier.dstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED); + barrier.image(image); + barrier.subresourceRange().aspectMask(VK_IMAGE_ASPECT_COLOR_BIT); + barrier.subresourceRange().baseMipLevel(0); + barrier.subresourceRange().levelCount(1); + barrier.subresourceRange().baseArrayLayer(0); + barrier.subresourceRange().layerCount(1); + + if(newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + + barrier.subresourceRange().aspectMask(VK_IMAGE_ASPECT_DEPTH_BIT); + + if(hasStencilComponent(format)) { + barrier.subresourceRange().aspectMask( + barrier.subresourceRange().aspectMask() | VK_IMAGE_ASPECT_STENCIL_BIT); + } + + } else { + barrier.subresourceRange().aspectMask(VK_IMAGE_ASPECT_COLOR_BIT); + } + + int sourceStage; + int destinationStage; + + if(oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + + barrier.srcAccessMask(0); + barrier.dstAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT); + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + + } else if(oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + + barrier.srcAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT); + barrier.dstAccessMask(VK_ACCESS_SHADER_READ_BIT); + + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + + } else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + barrier.srcAccessMask(0); + barrier.dstAccessMask(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + + } else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask(VK_ACCESS_SHADER_READ_BIT); + barrier.dstAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT); + + sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + }else { + throw new IllegalArgumentException("Unsupported layout transition"); + } + + + if (useTransferQueue) { + VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.calloc(stack); + beginInfo.sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO); + beginInfo.flags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + + vkResetCommandBuffer(transferCommandBuffer, 0); + // Transfer command buffer implicitly reset + vkBeginCommandBuffer(transferCommandBuffer, beginInfo); + + vkCmdPipelineBarrier(transferCommandBuffer, + sourceStage, destinationStage, + 0, + null, + null, + barrier); + + vkEndCommandBuffer(transferCommandBuffer); + + VkSubmitInfo submitInfo = VkSubmitInfo.calloc(stack); + submitInfo.sType(VK_STRUCTURE_TYPE_SUBMIT_INFO); + submitInfo.pCommandBuffers(stack.pointers(transferCommandBuffer)); + if(vkQueueSubmit(transferQueue, submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) { + throw new RuntimeException("Failed to submit copy command buffer"); + } + vkQueueWaitIdle(transferQueue); + } + else { + VkCommandBufferAllocateInfo allocInfo = VkCommandBufferAllocateInfo.calloc(stack); + allocInfo.sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO); + allocInfo.level(VK_COMMAND_BUFFER_LEVEL_PRIMARY); + allocInfo.commandPool(commandPool); + allocInfo.commandBufferCount(1); + + PointerBuffer pCommandBuffer = stack.mallocPointer(1); + vkAllocateCommandBuffers(device, allocInfo, pCommandBuffer); + VkCommandBuffer commandBuffer = new VkCommandBuffer(pCommandBuffer.get(0), device); + + VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.calloc(stack); + beginInfo.sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO); + beginInfo.flags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + + vkBeginCommandBuffer(commandBuffer, beginInfo); + + vkCmdPipelineBarrier(commandBuffer, + sourceStage, destinationStage, + 0, + null, + null, + barrier); + + vkEndCommandBuffer(commandBuffer); + + VkSubmitInfo submitInfo = VkSubmitInfo.calloc(stack); + submitInfo.sType(VK_STRUCTURE_TYPE_SUBMIT_INFO); + submitInfo.pCommandBuffers(pCommandBuffer); + + if(vkQueueSubmit(graphicsQueue, submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) { + throw new RuntimeException("Failed to submit copy command buffer"); + } + + vkQueueWaitIdle(graphicsQueue); + + vkFreeCommandBuffers(device, commandPool, pCommandBuffer); + } + + } + } + + + + public void copyBufferTransfer(long srcBuffer, long dstBuffer, int widthsize, int height, int operation) { +// + try(MemoryStack stack = stackPush()) { + + VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.calloc(stack); + beginInfo.sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO); + beginInfo.flags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + + vkResetCommandBuffer(transferCommandBuffer, 0); + // Transfer command buffer implicitly reset + vkBeginCommandBuffer(transferCommandBuffer, beginInfo); + copyCommandOperation(srcBuffer, dstBuffer, widthsize, height, transferCommandBuffer, operation); + vkEndCommandBuffer(transferCommandBuffer); + + VkSubmitInfo submitInfo = VkSubmitInfo.calloc(stack); + submitInfo.sType(VK_STRUCTURE_TYPE_SUBMIT_INFO); + submitInfo.pCommandBuffers(stack.pointers(transferCommandBuffer)); + + if(vkQueueSubmit(transferQueue, submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) { + throw new RuntimeException("Failed to submit copy command buffer"); + } + vkQueueWaitIdle(transferQueue); + + } + } + + + public void copyBufferAndWait(long srcBuffer, long dstBuffer, int size) { + // Too lazy to combine it into one function + if (useTransferQueue) { + copyBufferTransfer(srcBuffer, dstBuffer, size, 0, OPERATION_BUFFER); + } + else { + copyBufferDefault(srcBuffer, dstBuffer, size, 0, OPERATION_BUFFER); + } + // The 0 here in both these functions is for height for textures. + // But obviously, we're buffering buffers, not textures. + } + + + public void copyTextureAndWait(long srcTexture, long dstTexture, int width, int height) { + if (useTransferQueue) { + copyBufferTransfer(srcTexture, dstTexture, width, height, OPERATION_TEXTURE); + } + else { + copyBufferDefault(srcTexture, dstTexture, width, height, OPERATION_TEXTURE); + } + } + + + + // To be used after starting a singletime command buffer. + private void copyCommandOperation(long srcBuffer, long dstBuffer, + int widthsize, int height, + VkCommandBuffer cmdBuffer, + int operation) { + try(MemoryStack stack = stackPush()) { + switch (operation) { + case OPERATION_BUFFER: + { + VkBufferCopy.Buffer copyRegion = VkBufferCopy.calloc(1, stack); + copyRegion.size(widthsize); + vkCmdCopyBuffer(cmdBuffer, srcBuffer, dstBuffer, copyRegion); + } + break; + case OPERATION_TEXTURE: + VkBufferImageCopy.Buffer region = VkBufferImageCopy.calloc(1, stack); + region.bufferOffset(0); + region.bufferRowLength(0); // Tightly packed + region.bufferImageHeight(0); // Tightly packed + region.imageSubresource().aspectMask(VK_IMAGE_ASPECT_COLOR_BIT); + region.imageSubresource().mipLevel(0); + region.imageSubresource().baseArrayLayer(0); + region.imageSubresource().layerCount(1); + region.imageOffset().set(0, 0, 0); + region.imageExtent(VkExtent3D.calloc(stack).set(widthsize, height, 1)); + + vkCmdCopyBufferToImage(cmdBuffer, srcBuffer, dstBuffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region); + } + } + } + + + public int findMemoryType(MemoryStack stack, int typeFilter, int properties) { + + VkPhysicalDeviceMemoryProperties memProperties = VkPhysicalDeviceMemoryProperties.malloc(stack); + vkGetPhysicalDeviceMemoryProperties(physicalDevice, memProperties); + + for(int i = 0;i < memProperties.memoryTypeCount();i++) { + if((typeFilter & (1 << i)) != 0 && (memProperties.memoryTypes(i).propertyFlags() & properties) == properties) { + return i; + } + } + + throw new RuntimeException("Failed to find suitable memory type"); + } + + + private PointerBuffer getRequiredExtensions(MemoryStack stack) { + + PointerBuffer glfwExtensions = glfwGetRequiredInstanceExtensions(); + + if(VKSetup.ENABLE_VALIDATION_LAYERS) { + + PointerBuffer extensions = stack.mallocPointer(glfwExtensions.capacity() + 1); + + extensions.put(glfwExtensions); + extensions.put(stack.UTF8(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)); + + // Rewind the buffer before returning it to reset its position back to 0 + return extensions.rewind(); + } + + return glfwExtensions; + } + + + public int findSupportedFormat(IntBuffer formatCandidates, int tiling, int features) { + + try(MemoryStack stack = stackPush()) { + + VkFormatProperties props = VkFormatProperties.calloc(stack); + + for(int i = 0; i < formatCandidates.capacity(); ++i) { + + int format = formatCandidates.get(i); + + vkGetPhysicalDeviceFormatProperties(physicalDevice, format, props); + + if(tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures() & features) == features) { + return format; + } else if(tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures() & features) == features) { + return format; + } + + } + } + + throw new RuntimeException("Failed to find supported format"); + } + + public int findDepthFormat() { + return findSupportedFormat( + stackGet().ints(VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT), + VK_IMAGE_TILING_OPTIMAL, + VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); + } + + public boolean hasStencilComponent(int format) { + return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT; + } + + + +} diff --git a/core/src/processing/GL2VK/VertexAttribsBinding.java b/core/src/processing/GL2VK/VertexAttribsBinding.java new file mode 100644 index 0000000000..998431943a --- /dev/null +++ b/core/src/processing/GL2VK/VertexAttribsBinding.java @@ -0,0 +1,325 @@ +package processing.GL2VK; + +import static org.lwjgl.vulkan.VK10.VK_VERTEX_INPUT_RATE_VERTEX; + +import java.util.HashSet; + +import org.lwjgl.vulkan.VkVertexInputAttributeDescription; +import org.lwjgl.vulkan.VkVertexInputBindingDescription; + +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32B32A32_SFLOAT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32B32A32_SINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32B32A32_UINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32B32_SFLOAT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32B32_SINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32B32_UINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32_SFLOAT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32_SINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32G32_UINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32_SFLOAT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32_SINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R32_UINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8G8B8A8_UINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8G8B8_UINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8G8_UINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8_UINT; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8_UNORM; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8G8_UNORM; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8G8B8_UNORM; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8G8B8A8_UNORM; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8_SNORM; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8G8_SNORM; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8G8B8_SNORM; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8G8B8A8_SNORM; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R16_UNORM; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R16G16_UNORM; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R16G16B16_UNORM; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R16G16B16A16_UNORM; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R16_SNORM; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R16G16_SNORM; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R16G16B16_SNORM; +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R16G16B16A16_SNORM; + + +// ABOUT BINDINGS: +// Bindings are just like glBindBuffer before calling vertexAttribPointer. +// Bindings are indexes to those buffers. +// For example +// Binding 0: vertexBuffer[0] +// Binding 1: vertexBuffer[1] +// Remember that each of those bindings are tied to the attributes +// For example, attribute (location=0) can be attached to binding 0 +// Then attribute (location=1) can be attached to binding 1 +// And +// When you want interleaved, it's like this +// Attribute (location=0) attached to binding 0 +// Attribute (location=1) attached to binding 0 +// Both attached to binding 0. +// Of course, if you want separate buffers per attribute, you'll need to +// assign them different buffers each. + + +// In our main program, here's what's happening surface level v underneath the hood: + +// EXAMPLE 1 +// Surface: +// glbindBuffer(PGL.ARRAY_BUFFER, vertexVboId); +// glvertexAttribPointer(vertLoc, VERT_CMP_COUNT, PGL.FLOAT, false, vertexStride, vertexOffset); +// glbindBuffer(PGL.ARRAY_BUFFER, colorVboId); +// glvertexAttribPointer(colorLoc, CLR_CMP_COUNT, PGL.FLOAT, false, colorStride, colorOffset); + +// Under the hood: +// - New buffer bound, create new VertexAttribsBinding object +// - Set VertexAttribsBinding object's attribs. +// - New buffer bound, create new VertexAttribsBinding object +// - Set VertexAttribsBinding object's attribs. +// - When pipeline gets created, we go through each VertexAttribsBinding object +// and join the vertexAttributeDescriptions and combine the vertexbindings of each object + +// EXAMPLE 2 +// Surface: +// glbufferData(PGL.ARRAY_BUFFER, Float.BYTES * attribs.length, attribBuffer, PGL.DYNAMIC_DRAW); +// glvertexAttribPointer(vertLoc, VERT_CMP_COUNT, PGL.FLOAT, false, stride, vertexOffset); +// glvertexAttribPointer(colorLoc, CLR_CMP_COUNT, PGL.FLOAT, false, stride, colorOffset); + + +// Under the hood: +// - New buffer bound, create new VertexAttribsBinding object +// - Set VertexAttribsBinding object's attribs. +// - Set VertexAttribsBinding object's attribs. +// - When pipeline gets created, we go through each VertexAttribsBinding object +// and join the vertexAttributeDescriptions and combine the vertexbindings of each object + + + +public class VertexAttribsBinding { + public int myBinding = 0; + private int bindingStride = 0; + + // Need the whole buffer and not just the id because the id could change at any time. + public GraphicsBuffer buffer = null; + private ShaderAttribInfo attribInfo; + + private int stateHash = 0; + + private HashSet usedLocations = new HashSet<>(); + + public VertexAttribsBinding(int binding, ShaderAttribInfo attribInfo) { + this.myBinding = binding; + this.attribInfo = attribInfo; + this.bindingStride = attribInfo.bindingSize; + } + + + + + // As ugly as it is to modify values straight from attribInfo, realistically these + // values will not be changed to anything different if the program uses vertexAttribPointer + // correctly. + // NOTE: Passing the return value from glGetAttribLocation will NOT work because how it works + // in gl: + // Shader 0: + // 1: attrib 0 + // 2: attrib 1 + // Shader 1: + // 3: attrib 0 + // 4: attrib 1 + // 5: attrib 2 + // Instead you will need to pass the attrib number belonging to the shader, i.e. attrib 1,2,3 + // OpenGL is truly a tangled mess. + public void vertexAttribPointer(int vklocation, int count, int type, boolean normalized, int stride, int offset) { + + + int actualSize = 0; + int vktype = -1; + switch (type) { + case GL2VK.GL_UNSIGNED_BYTE: +// System.out.println("GL_UNSIGNED_BYTE"); + + // UNORM: 0 to 1 + if (normalized) { + if (count == 1) { + vktype = VK_FORMAT_R8_UNORM; + } + else if (count == 2) { + vktype = VK_FORMAT_R8G8_UNORM; + } + else if (count == 3) { + vktype = VK_FORMAT_R8G8B8_UNORM; + } + else if (count == 4) { + vktype = VK_FORMAT_R8G8B8A8_UNORM; + } + } + else { + System.err.println("vertexAttribPointer: not supported (unnormalized unsigned byte)"); + } + actualSize = count*Byte.BYTES; + break; + case GL2VK.GL_UNSIGNED_SHORT: +// System.out.println("SHORT"); + + // UNORM: 0 to 1 + if (normalized) { + if (count == 1) { + vktype = VK_FORMAT_R16_UNORM; + } + else if (count == 2) { + vktype = VK_FORMAT_R16G16_UNORM; + } + else if (count == 3) { + vktype = VK_FORMAT_R16G16B16_UNORM; + } + else if (count == 4) { + vktype = VK_FORMAT_R16G16B16A16_UNORM; + } + } + else { + System.err.println("vertexAttribPointer: not supported (unnormalized unsigned short)"); + } + actualSize = count*Short.BYTES; + break; + case GL2VK.GL_UNSIGNED_INT: +// System.out.println("GL_UNSIGNED_INT"); + System.err.println("vertexAttribPointer: Unsigned int not supported"); + actualSize = count*Integer.BYTES; + break; + case GL2VK.GL_INT: +// System.out.println("GL_INT"); + System.err.println("vertexAttribPointer: signed int not supported"); + actualSize = count*Integer.BYTES; + break; + case GL2VK.GL_BYTE: +// System.out.println("GL_BYTE"); + + // SNORM: -1 to 1 + if (normalized) { + if (count == 1) { + vktype = VK_FORMAT_R8_SNORM; + } + else if (count == 2) { + vktype = VK_FORMAT_R8G8_SNORM; + } + else if (count == 3) { + vktype = VK_FORMAT_R8G8B8_SNORM; + } + else if (count == 4) { + vktype = VK_FORMAT_R8G8B8A8_SNORM; + } + } + else { + System.err.println("vertexAttribPointer: not supported (unnormalized signed byte)"); + } + actualSize = count*Byte.BYTES; + break; + case GL2VK.GL_SHORT: + + // SNORM: -1 to 1 + if (normalized) { + if (count == 1) { + vktype = VK_FORMAT_R16_SNORM; + } + else if (count == 2) { + vktype = VK_FORMAT_R16G16_SNORM; + } + else if (count == 3) { + vktype = VK_FORMAT_R16G16B16_SNORM; + } + else if (count == 4) { + vktype = VK_FORMAT_R16G16B16A16_SNORM; + } + } + else { + System.err.println("vertexAttribPointer: not supported (unnormalized signed short)"); + } +// System.out.println("GL_SHORT"); + actualSize = count*Short.BYTES; + break; + case GL2VK.GL_FLOAT: +// System.out.println("GL_FLOAT"); + if (normalized) { + System.err.println("vertexAttribPointer: not supported (normalized float)"); + } + else { + if (count == 1) { + vktype = VK_FORMAT_R32_SFLOAT; + } + else if (count == 2) { + vktype = VK_FORMAT_R32G32_SFLOAT; + } + else if (count == 3) { + vktype = VK_FORMAT_R32G32B32_SFLOAT; + } + else if (count == 4) { + vktype = VK_FORMAT_R32G32B32A32_SFLOAT; + } + } + actualSize = count*Float.BYTES; + break; + case GL2VK.GL_BOOL: +// System.out.println("GL_BOOL"); + System.err.println("vertexAttribPointer: not supported (boolean)"); + actualSize = count*1; + break; + } + + // Something I did not spot in the OpenGL specification: + // "If stride is 0, the generic vertex attributes are understood to be tightly packed in the array" + if (stride == 0) { + stride = actualSize; + } + + + attribInfo.locationToAttrib[vklocation].size = actualSize; + attribInfo.locationToAttrib[vklocation].format = vktype; + attribInfo.locationToAttrib[vklocation].offset = offset; + bindingStride = stride; + + // We're using those attribs + usedLocations.add(vklocation); + + // What's set by this function determines the state of the pipeline (if + // it changes at any point, we need to recreate the pipeline with new + // vertex bindings) + + // TODO: Remove this, move it to getStateHash() instead. + stateHash += (vklocation+1L)*usedLocations.size()*100L + count*2 + offset*3; + } + + // Mostly used for testing purposes + public void vertexAttribPointer(int location) { + // We're using those attribs + usedLocations.add(location); + + // TODO: Remove this, move it to getStateHash() instead. + stateHash += (location+1L)*usedLocations.size()*100L + + attribInfo.locationToAttrib[location].size*2 + + attribInfo.locationToAttrib[location].offset*3; + } + + // TODO: Calculate hash state on the spot instead of relying on possibly invalid states. + public int getHashState() { + return stateHash; + } + + public void updateAttributeDescriptions(VkVertexInputAttributeDescription.Buffer attribDescrptions, int index) { + for (Integer loc : usedLocations) { + VkVertexInputAttributeDescription description = attribDescrptions.get(index++); + description.binding(myBinding); + description.location(loc); + description.format(attribInfo.locationToAttrib[loc].format); + description.offset(attribInfo.locationToAttrib[loc].offset); + } + } + + public int getSize() { + return usedLocations.size(); + } + + public void updateBindingDescription(VkVertexInputBindingDescription bindingDescription) { + bindingDescription.binding(myBinding); + bindingDescription.stride(bindingStride); + bindingDescription.inputRate(VK_VERTEX_INPUT_RATE_VERTEX); + } + +} diff --git a/core/src/processing/GL2VK/VulkanSystem.java b/core/src/processing/GL2VK/VulkanSystem.java new file mode 100644 index 0000000000..069f2c6a4e --- /dev/null +++ b/core/src/processing/GL2VK/VulkanSystem.java @@ -0,0 +1,722 @@ +package processing.GL2VK; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.*; + +import processing.vulkan.PSurfaceVK; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.LongBuffer; +import java.nio.ShortBuffer; +import java.util.*; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.system.MemoryStack.stackPush; +import static org.lwjgl.vulkan.KHRSurface.*; +import static org.lwjgl.vulkan.KHRSwapchain.*; +import static org.lwjgl.vulkan.VK10.*; + + + + +// Poptential useful extensions: + +// VK_KHR_dynamic_rendering - +// Create a single render pass without requiring different render passes +// per pipeline or something + +// enableVertexAttribArray is important because it tells opengl what +// vertex buffers to include for drawing with in commands like drawArrays. + + +public class VulkanSystem { + + + + public static final int MAX_FRAMES_IN_FLIGHT = 2; + + + + // ======= FIELDS ======= // + + public VkDevice device; + + + + + public long renderPass; + + public long colorAttachmentVal; + + private List commandBuffers; + public List swapChainFramebuffers; + + private List inFlightFrames; + private Map imagesInFlight; + private int currentFrame; + + boolean framebufferResize; + + public VKSetup vkbase; + + private int selectedNode = 0; + private ThreadNode[] threadNodes = new ThreadNode[8]; + + public long depthImage; + public long depthImageMemory; + public long depthImageView; + + + // ======= METHODS ======= // + + public void run() { +// initVulkan(); + } + + + + public void initVulkan(PSurfaceVK surface, int threadNodeSize) { + vkbase = new VKSetup(); + vkbase.initBase(surface); + device = vkbase.device; + + createCommandPool(); + createRenderPass(); +// createGraphicsPipeline(); + createDepthBuffer(); + createFramebuffers(); + createCommandBuffers(); + createSyncObjects(); + createThreadNodes(threadNodeSize); + } + + public boolean shouldClose() { + glfwPollEvents(); + return glfwWindowShouldClose(vkbase.window); + } + + + public void cleanupNodes() { + for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkWaitForFences(device, inFlightFrames.get(i).pFence(), true, Util.UINT64_MAX); + } + + for (ThreadNode n : threadNodes) { + n.killAndCleanup(); + } + } + + public void cleanupRest() { + vkbase.destroyOtherThings(); + + cleanupSwapChain(); + + inFlightFrames.forEach(frame -> { + + vkDestroySemaphore(device, frame.renderFinishedSemaphore(), null); + vkDestroySemaphore(device, frame.imageAvailableSemaphore(), null); + vkDestroyFence(device, frame.fence(), null); + }); + inFlightFrames.clear(); + + vkDestroyCommandPool(device, vkbase.commandPool, null); + + vkDestroyDevice(device, null); + + if(VKSetup.ENABLE_VALIDATION_LAYERS) { + VKSetup.destroyDebugUtilsMessengerEXT(vkbase.instance, vkbase.debugMessenger, null); + } + + vkDestroySurfaceKHR(vkbase.instance, vkbase.vkwindow.glfwsurface, null); + + vkDestroyInstance(vkbase.instance, null); + + glfwDestroyWindow(vkbase.window); + + glfwTerminate(); + } + + + private void createThreadNodes(int v) { + threadNodes = new ThreadNode[v]; + for (int i = 0; i < threadNodes.length; i++) { + threadNodes[i] = new ThreadNode(this, i); + } + } + + private void createThreadNodes() { + int availableProcessors = Runtime.getRuntime().availableProcessors(); + createThreadNodes(availableProcessors); + } + + + + private void createRenderPass() { + + try(MemoryStack stack = stackPush()) { + + VkAttachmentDescription.Buffer attachments = VkAttachmentDescription.calloc(2, stack); + VkAttachmentReference.Buffer attachmentRefs = VkAttachmentReference.calloc(2, stack); + + // Color attachments + + VkAttachmentDescription colorAttachment = attachments.get(0); + colorAttachment.format(vkbase.swapChainImageFormat); + colorAttachment.samples(VK_SAMPLE_COUNT_1_BIT); +// colorAttachment.loadOp(VK_ATTACHMENT_LOAD_OP_LOAD); + colorAttachment.loadOp(VK_ATTACHMENT_LOAD_OP_DONT_CARE); +// colorAttachment.loadOp(VK_ATTACHMENT_LOAD_OP_CLEAR); + colorAttachment.storeOp(VK_ATTACHMENT_STORE_OP_STORE); + colorAttachment.stencilLoadOp(VK_ATTACHMENT_LOAD_OP_DONT_CARE); + colorAttachment.stencilStoreOp(VK_ATTACHMENT_STORE_OP_DONT_CARE); + colorAttachment.initialLayout(VK_IMAGE_LAYOUT_UNDEFINED); + colorAttachment.finalLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + + int y = attachments.get(0).samples(); + VkAttachmentReference colorAttachmentRef = attachmentRefs.get(0); + colorAttachmentRef.attachment(0); + colorAttachmentRef.layout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + // Depth-Stencil attachments + + VkAttachmentDescription depthAttachment = attachments.get(1); + depthAttachment.format(vkbase.findDepthFormat()); + depthAttachment.samples(VK_SAMPLE_COUNT_1_BIT); + depthAttachment.loadOp(VK_ATTACHMENT_LOAD_OP_CLEAR); + depthAttachment.storeOp(VK_ATTACHMENT_STORE_OP_DONT_CARE); + depthAttachment.stencilLoadOp(VK_ATTACHMENT_LOAD_OP_DONT_CARE); + depthAttachment.stencilStoreOp(VK_ATTACHMENT_STORE_OP_DONT_CARE); + depthAttachment.initialLayout(VK_IMAGE_LAYOUT_UNDEFINED); + depthAttachment.finalLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + + VkAttachmentReference depthAttachmentRef = attachmentRefs.get(1); + depthAttachmentRef.attachment(1); + depthAttachmentRef.layout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + + VkSubpassDescription.Buffer subpass = VkSubpassDescription.calloc(1, stack); + subpass.pipelineBindPoint(VK_PIPELINE_BIND_POINT_GRAPHICS); + subpass.colorAttachmentCount(1); + subpass.pColorAttachments(VkAttachmentReference.calloc(1, stack).put(0, colorAttachmentRef)); + subpass.pDepthStencilAttachment(depthAttachmentRef); + + + VkSubpassDependency.Buffer dependency = VkSubpassDependency.calloc(1, stack); + dependency.srcSubpass(VK_SUBPASS_EXTERNAL); + dependency.dstSubpass(0); + dependency.srcStageMask(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + dependency.srcAccessMask(0); + dependency.dstStageMask(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + dependency.dstAccessMask(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + + VkRenderPassCreateInfo renderPassInfo = VkRenderPassCreateInfo.calloc(stack); + renderPassInfo.sType(VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO); + renderPassInfo.pAttachments(attachments); + renderPassInfo.pSubpasses(subpass); + renderPassInfo.pDependencies(dependency); + + LongBuffer pRenderPass = stack.mallocLong(1); + + if(vkCreateRenderPass(device, renderPassInfo, null, pRenderPass) != VK_SUCCESS) { + throw new RuntimeException("Failed to create render pass"); + } + + renderPass = pRenderPass.get(0); + } + } + + + private void createSwapChainObjects() { + vkbase.createSwapChain(); + vkbase.createImageViews(); + createRenderPass(); +// createGraphicsPipeline(); + createFramebuffers(); + createCommandBuffers(); + } + + public void recreateSwapChain() { + System.err.println("BUG WARNING recreateSwapChain is temporarily disabled."); +// try(MemoryStack stack = stackPush()) { +// +// IntBuffer width = stack.ints(0); +// IntBuffer height = stack.ints(0); +// +// while(width.get(0) == 0 && height.get(0) == 0) { +// glfwGetFramebufferSize(vkbase.window, width, height); +// glfwWaitEvents(); +// } +// } +// +// vkDeviceWaitIdle(device); +// +// cleanupSwapChain(); +// +// createSwapChainObjects(); + } + + private void createFramebuffers() { + + swapChainFramebuffers = new ArrayList<>(vkbase.swapChainImageViews.size()); + + try(MemoryStack stack = stackPush()) { + + LongBuffer attachments = stack.longs(VK_NULL_HANDLE, depthImageView); + LongBuffer pFramebuffer = stack.mallocLong(1); + + // Lets allocate the create info struct once and just update the pAttachments field each iteration + VkFramebufferCreateInfo framebufferInfo = VkFramebufferCreateInfo.calloc(stack); + framebufferInfo.sType(VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO); + framebufferInfo.renderPass(renderPass); + framebufferInfo.width(vkbase.swapChainExtent.width()); + framebufferInfo.height(vkbase.swapChainExtent.height()); + framebufferInfo.layers(1); + + for(long imageView : vkbase.swapChainImageViews) { + + attachments.put(0, imageView); + + framebufferInfo.pAttachments(attachments); + + if(vkCreateFramebuffer(device, framebufferInfo, null, pFramebuffer) != VK_SUCCESS) { + throw new RuntimeException("Failed to create framebuffer"); + } + + swapChainFramebuffers.add(pFramebuffer.get(0)); + } + colorAttachmentVal = attachments.get(0); + } + } + + + private void createDepthBuffer() { + try(MemoryStack stack = stackPush()) { + int depthFormat = vkbase.findDepthFormat(); + + LongBuffer pDepthImage = stack.mallocLong(1); + LongBuffer pDepthImageMemory = stack.mallocLong(1); + + vkbase.createImage( + vkbase.swapChainExtent.width(), vkbase.swapChainExtent.height(), + depthFormat, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + pDepthImage, + pDepthImageMemory); + + depthImage = pDepthImage.get(0); + depthImageMemory = pDepthImageMemory.get(0); + + depthImageView = vkbase.createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT); + + // Explicitly transitioning the depth image + vkbase.transitionImageLayout(depthImage, depthFormat, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + + } + } + + + + private void createCommandBuffers() { + + final int commandBuffersCount = swapChainFramebuffers.size(); + + commandBuffers = new ArrayList<>(commandBuffersCount); + + try(MemoryStack stack = stackPush()) { + + VkCommandBufferAllocateInfo allocInfo = VkCommandBufferAllocateInfo.calloc(stack); + allocInfo.sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO); + allocInfo.commandPool(vkbase.commandPool); + allocInfo.level(VK_COMMAND_BUFFER_LEVEL_PRIMARY); + allocInfo.commandBufferCount(commandBuffersCount); + + PointerBuffer pCommandBuffers = stack.mallocPointer(commandBuffersCount); + + if(vkAllocateCommandBuffers(device, allocInfo, pCommandBuffers) != VK_SUCCESS) { + throw new RuntimeException("Failed to allocate command buffers"); + } + + for(int i = 0;i < commandBuffersCount;i++) { + commandBuffers.add(new VkCommandBuffer(pCommandBuffers.get(i), device)); + } + } + } + + + + private void createSyncObjects() { + + inFlightFrames = new ArrayList<>(MAX_FRAMES_IN_FLIGHT); + imagesInFlight = new HashMap<>(vkbase.swapChainImages.size()); + + try(MemoryStack stack = stackPush()) { + + VkSemaphoreCreateInfo semaphoreInfo = VkSemaphoreCreateInfo.calloc(stack); + semaphoreInfo.sType(VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO); + + VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.calloc(stack); + fenceInfo.sType(VK_STRUCTURE_TYPE_FENCE_CREATE_INFO); + fenceInfo.flags(VK_FENCE_CREATE_SIGNALED_BIT); + + LongBuffer pImageAvailableSemaphore = stack.mallocLong(1); + LongBuffer pRenderFinishedSemaphore = stack.mallocLong(1); + LongBuffer pFence = stack.mallocLong(1); + + for(int i = 0;i < MAX_FRAMES_IN_FLIGHT;i++) { + + if(vkCreateSemaphore(device, semaphoreInfo, null, pImageAvailableSemaphore) != VK_SUCCESS + || vkCreateSemaphore(device, semaphoreInfo, null, pRenderFinishedSemaphore) != VK_SUCCESS + || vkCreateFence(device, fenceInfo, null, pFence) != VK_SUCCESS) { + + throw new RuntimeException("Failed to create synchronization objects for the frame " + i); + } + + inFlightFrames.add(new Frame(pImageAvailableSemaphore.get(0), pRenderFinishedSemaphore.get(0), pFence.get(0))); + } + + } + } + + private void createCommandPool() { + vkbase.createCommandPool(); + } + + + public int getPushConstantsSizeLimit() { + return vkbase.pushConstantsSizeLimit; + } + + + + public VkCommandBuffer currentCommandBuffer = null; +// private IntBuffer currentImageIndex = null; + private int currentImageIndex = 0; + public VkRenderPassBeginInfo renderPassInfo = null; + + public void beginRecord() { + // All the stuff that was before recordCommandBuffer() + try(MemoryStack stack = stackPush()) { + + // Frames in flight stuff + Frame thisFrame = inFlightFrames.get(currentFrame); + +// Util.beginTmr(); + vkWaitForFences(device, thisFrame.pFence(), true, Util.UINT64_MAX); +// Util.endTmr("wait 1"); + + IntBuffer currentImageIndex = stack.mallocInt(1); + + int vkResult = vkAcquireNextImageKHR(device, vkbase.swapChain, Util.UINT64_MAX, + thisFrame.imageAvailableSemaphore(), VK_NULL_HANDLE, currentImageIndex); + + + // Window resizing + if(vkResult == VK_ERROR_OUT_OF_DATE_KHR) { + recreateSwapChain(); + return; + } else if(vkResult != VK_SUCCESS) { + throw new RuntimeException("Cannot get image"); + } + + final int imageIndex = currentImageIndex.get(0); + this.currentImageIndex = currentImageIndex.get(0); + + // Fence wait for images in flight. +// Util.beginTmr(); +// if(imagesInFlight.containsKey(imageIndex)) { +// vkWaitForFences(device, imagesInFlight.get(imageIndex).fence(), true, Util.UINT64_MAX); +// } +// Util.endTmr("wait 2"); + + imagesInFlight.put(imageIndex, thisFrame); + + } + + currentCommandBuffer = commandBuffers.get(currentFrame); + + // Now to the command buffer stuff. + vkResetCommandBuffer(currentCommandBuffer, 0); + final int imageIndex = this.currentImageIndex; + + try(MemoryStack stack = stackPush()) { + + VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.calloc(stack); + beginInfo.sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO); + + renderPassInfo = VkRenderPassBeginInfo.calloc(stack); + renderPassInfo.sType(VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO); + + renderPassInfo.renderPass(renderPass); + + VkRect2D renderArea = VkRect2D.calloc(stack); + renderArea.offset(VkOffset2D.calloc(stack).set(0, 0)); + renderArea.extent(vkbase.swapChainExtent); + renderPassInfo.renderArea(renderArea); + + VkClearValue.Buffer clearValues = VkClearValue.calloc(2, stack); + clearValues.get(0).color().float32(stack.floats(0.8f, 0.8f, 0.8f, 1.0f)); + clearValues.get(1).depthStencil().set(1.0f, 0); + renderPassInfo.pClearValues(clearValues); + + if(vkBeginCommandBuffer(currentCommandBuffer, beginInfo) != VK_SUCCESS) { + throw new RuntimeException("Failed to begin recording command buffer"); + } + + + renderPassInfo.framebuffer(swapChainFramebuffers.get(imageIndex)); + + vkCmdBeginRenderPass(currentCommandBuffer, renderPassInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); + +// vkCmdBindPipeline(currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); + } + + // And then begin our thread nodes (secondary command buffers) + // TODO: other thread nodes + for (ThreadNode n : threadNodes) { + n.beginRecord(currentFrame, imageIndex); + } + + } + + + public void endRecord() { + // Before we can end recording, we need to think about our secondary command buffers + + + for (ThreadNode n : threadNodes) { + n.endRecord(); + } + for (ThreadNode n : threadNodes) { + n.await(); + } + + // TODO: TEST NODE + try(MemoryStack stack = stackPush()) { + // TODO: avoid garbage collection by making it assign list only once. + List cmdbuffers = new ArrayList<>(); + + for (ThreadNode n : threadNodes) { + cmdbuffers.add(n.getBuffer()); + } + + vkCmdExecuteCommands(currentCommandBuffer, Util.asPointerBuffer(stack, cmdbuffers)); + } + + vkCmdEndRenderPass(currentCommandBuffer); + + if(vkEndCommandBuffer(currentCommandBuffer) != VK_SUCCESS) { + throw new RuntimeException("Failed to record command buffer"); + } + + submitAndPresent(); + } + + public void selectNode(int node) { + selectedNode = node; + } + + public void updateNodePipeline(long pipeline) { + if (pipeline != threadNodes[selectedNode].currentPipeline) { + threadNodes[selectedNode].bindPipeline(pipeline); + } + } + + public void setMaxNodes(int v) { + createThreadNodes(v); + } + + public int getNodesCount() { + return threadNodes.length; + } + + + public void copyBufferFast(VkCommandBuffer cmdbuffer, long srcBuffer, long dstBuffer, long size) { + try(MemoryStack stack = stackPush()) { + { + VkBufferCopy.Buffer copyRegion = VkBufferCopy.calloc(1, stack); + copyRegion.size(size); + vkCmdCopyBuffer(cmdbuffer, srcBuffer, dstBuffer, copyRegion); + } + } + } + + public void submitAndPresent() { + try(MemoryStack stack = stackPush()) { + + Frame thisFrame = inFlightFrames.get(currentFrame); + + // Queue submission (to be carried out after recording a command buffer) + VkSubmitInfo submitInfo = VkSubmitInfo.callocStack(stack); + submitInfo.sType(VK_STRUCTURE_TYPE_SUBMIT_INFO); + + submitInfo.waitSemaphoreCount(1); + submitInfo.pWaitSemaphores(thisFrame.pImageAvailableSemaphore()); + submitInfo.pWaitDstStageMask(stack.ints(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)); + + submitInfo.pSignalSemaphores(thisFrame.pRenderFinishedSemaphore()); + + submitInfo.pCommandBuffers(stack.pointers(commandBuffers.get(currentFrame))); + + vkResetFences(device, thisFrame.pFence()); + + int vkResult = 0; + if((vkResult = vkQueueSubmit(vkbase.graphicsQueue, submitInfo, thisFrame.fence())) != VK_SUCCESS) { + vkResetFences(device, thisFrame.pFence()); + throw new RuntimeException("Failed to submit draw command buffer: " + vkResult); + } + + // Presenting image from swapchain (to be done after submission) + // and also some waiting. + VkPresentInfoKHR presentInfo = VkPresentInfoKHR.callocStack(stack); + presentInfo.sType(VK_STRUCTURE_TYPE_PRESENT_INFO_KHR); + + presentInfo.pWaitSemaphores(thisFrame.pRenderFinishedSemaphore()); + + presentInfo.swapchainCount(1); + presentInfo.pSwapchains(stack.longs(vkbase.swapChain)); + + IntBuffer imgIndexBuffer = stack.mallocInt(1); + imgIndexBuffer.put(0, currentImageIndex); + presentInfo.pImageIndices(imgIndexBuffer); + + vkResult = vkQueuePresentKHR(vkbase.presentQueue, presentInfo); + + // Window resizing + if(vkResult == VK_ERROR_OUT_OF_DATE_KHR || vkResult == VK_SUBOPTIMAL_KHR || framebufferResize) { + framebufferResize = false; + recreateSwapChain(); + } else if(vkResult != VK_SUCCESS) { + throw new RuntimeException("Failed to present swap chain image"); + } + + // update current frame. + currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; + } + } + + public int getFrame() { + return currentFrame; + } + + + + public void cleanupSwapChain() { + swapChainFramebuffers.forEach(framebuffer -> vkDestroyFramebuffer(device, framebuffer, null)); + try(MemoryStack stack = stackPush()) {vkFreeCommandBuffers(device, vkbase.commandPool, Util.asPointerBuffer(stack, commandBuffers));} + + vkDestroyRenderPass(device, renderPass, null); + + vkbase.swapChainImageViews.forEach(imageView -> vkDestroyImageView(device, imageView, null)); + + vkDestroySwapchainKHR(device, vkbase.swapChain, null); + + vkDestroyImageView(device, depthImageView, null); + vkDestroyImage(device, depthImage, null); + vkFreeMemory(device, depthImageMemory, null); + } + + + + //////////////// + // NODE COMMANDS + //////////////// + public void nodeDrawArrays(ArrayList buffers, int size, int first) { + threadNodes[selectedNode].drawArrays(buffers, size, first); + } + + public void nodeBindPipeline(long pipeline) { + threadNodes[selectedNode].bindPipeline(pipeline); + } + + public void nodeBindDescriptorSet(long pipelineLayout, long descriptorSet) { + threadNodes[selectedNode].bindDescriptorSet(pipelineLayout, descriptorSet); + } + + public void nodeDrawIndexed(int indiciesSize, long indiciesBuffer, ArrayList vertexBuffers, int offset, int vertexOffset, int type) { +// System.out.println("Using thread "+selectedNode); + threadNodes[selectedNode].drawIndexed(indiciesSize, indiciesBuffer, vertexBuffers, offset, vertexOffset, type); + } + + public void nodePushConstants(long pipelineLayout, int vertexOfFragment, int offset, ByteBuffer buffer) { + // We need a size because the buffer must be in multiples of 8, but we may have a half-filled long of one float. + threadNodes[selectedNode].pushConstant(pipelineLayout, vertexOfFragment, offset, buffer); // TODO + } + + public void nodePushConstants(long pipelineLayout, int vertexOfFragment, int offset, FloatBuffer buffer) { + // We need a size because the buffer must be in multiples of 8, but we may have a half-filled long of one float. + threadNodes[selectedNode].pushConstant(pipelineLayout, vertexOfFragment, offset, buffer); + } + + public void nodePushConstants(long pipelineLayout, int vertexOfFragment, int offset, float val) { + threadNodes[selectedNode].pushConstant(pipelineLayout, vertexOfFragment, offset, val); + } + + public void nodePushConstants(long pipelineLayout, int vertexOfFragment, int offset, float val0, float val1) { + threadNodes[selectedNode].pushConstant(pipelineLayout, vertexOfFragment, offset, val0, val1); + } + + public void nodePushConstants(long pipelineLayout, int vertexOfFragment, int offset, float val0, float val1, float val2) { + threadNodes[selectedNode].pushConstant(pipelineLayout, vertexOfFragment, offset, val0, val1, val2); + } + + public void nodePushConstants(long pipelineLayout, int vertexOfFragment, int offset, float val0, float val1, float val2, float val3) { + threadNodes[selectedNode].pushConstant(pipelineLayout, vertexOfFragment, offset, val0, val1, val2, val3); + } + + public void nodePushConstants(long pipelineLayout, int vertexOfFragment, int offset, int val) { + threadNodes[selectedNode].pushConstant(pipelineLayout, vertexOfFragment, offset, val); + } + + public void nodePushConstants(long pipelineLayout, int vertexOfFragment, int offset, int val0, int val1) { + threadNodes[selectedNode].pushConstant(pipelineLayout, vertexOfFragment, offset, val0, val1); + } + + public void nodePushConstants(long pipelineLayout, int vertexOfFragment, int offset, int val0, int val1, int val2) { + threadNodes[selectedNode].pushConstant(pipelineLayout, vertexOfFragment, offset, val0, val1, val2); + } + + public void nodePushConstants(long pipelineLayout, int vertexOfFragment, int offset, int val0, int val1, int val2, int val3) { + threadNodes[selectedNode].pushConstant(pipelineLayout, vertexOfFragment, offset, val0, val1, val2, val3); + } + + public void nodeBufferData(GraphicsBuffer graphicsBuffer, int size, ByteBuffer buffer, int instance) { + threadNodes[selectedNode].bufferData(graphicsBuffer, size, buffer, instance); + } + + public void nodeBufferData(GraphicsBuffer graphicsBuffer, int size, FloatBuffer buffer, int instance) { + threadNodes[selectedNode].bufferData(graphicsBuffer, size, buffer, instance); + } + + public void nodeBufferData(GraphicsBuffer graphicsBuffer, int size, LongBuffer buffer, int instance) { + threadNodes[selectedNode].bufferData(graphicsBuffer, size, buffer, instance); + } + + public void nodeBufferData(GraphicsBuffer graphicsBuffer, int size, ShortBuffer buffer, int instance) { + threadNodes[selectedNode].bufferData(graphicsBuffer, size, buffer, instance); + } + + public void nodeBufferData(GraphicsBuffer graphicsBuffer, int size, IntBuffer buffer, int instance) { + threadNodes[selectedNode].bufferData(graphicsBuffer, size, buffer, instance); + } + + public void nodeClearColor(float r, float g, float b, float a) { + threadNodes[selectedNode].clearColor(r, g, b, a); + } + + + + + + + + ///////////////////////////////////////////////// + ///////////////////////////////////////////////// + + + +} \ No newline at end of file diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java index 24c0d5d39a..6b3098da64 100644 --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -38,12 +38,15 @@ import javax.xml.parsers.ParserConfigurationException; import org.xml.sax.SAXException; +import processing.GL2VK.ThreadNode; // TODO have this removed by 4.0 final import processing.awt.ShimAWT; import processing.data.*; import processing.event.*; import processing.opengl.*; +import processing.vulkan.PGraphicsVulkan; +import processing.vulkan.PVK; /** @@ -1761,6 +1764,48 @@ public PGraphics createGraphics(int w, int h) { return createGraphics(w, h, JAVA2D); } + public void selectNode(int node) { + if (g instanceof PGraphicsVulkan) { + if (node == AUTO) { + ((PGraphicsVulkan)g).enableAutoMode(); + } + else if (node == TOP) { + ((PGraphicsVulkan)g).selectNode(maxNodes()-1); + } + else if (node < 0 || node > maxNodes()) { + throw new IllegalArgumentException("Node must be in range of 0 to "+maxNodes()+" or set to AUTO or MAX."); + } + else { + ((PGraphicsVulkan)g).selectNode(node); + } + } + } + + public int maxNodes() { + if (g instanceof PGraphicsVulkan) { + return ((PGraphicsVulkan)g).getNodesCount(); + } + return 0; + } + + public int maxNodes = -1; + public void setMaxNodes(int v) { + maxNodes = v; +// if (g instanceof PGraphicsVulkan) { +// ((PGraphicsVulkan)g).setMaxNodes(v); +// } + } + + public void bufferMultithreaded(boolean onoff) { + if (g instanceof PGraphicsVulkan) { + ((PGraphicsVulkan)g).bufferMultithreaded(onoff); + } + } + + public void printThreadNodeReport() { + ThreadNode.getTimedReport(); + } + /** * @@ -3679,7 +3724,8 @@ public void cursor() { /** * - * Hides the mouse cursor from view. + * Hides the cursor from view. Will not work when running the program in a + * web browser or when running in full screen (Present) mode. * *

Advanced

* Hide the cursor by creating a transparent image @@ -9603,9 +9649,9 @@ public final int color(float v1, float v2, float v3, float alpha) { /** * - * Calculates a new color that is a blend of two other colors. The amt parameter - * controls the amount of each color to use where an amount of 0.0 will produce - * the first color, 1.0 will return the second color, and 0.5 is halfway in + * Calculates a new color that is a blend of two other colors. The amt parameter + * controls the amount of each color to use where an amount of 0.0 will produce + * the first color, 1.0 will return the second color, and 0.5 is halfway in * between. Values between 0.0 and 1.0 will interpolate between the two colors in * that proportion.
* An amount below 0 will be treated as 0. Likewise, amounts above 1 will be @@ -9663,7 +9709,7 @@ public void frameResized(int w, int h) { // WINDOW METHODS - Map windowEventQueue = new ConcurrentHashMap<>(); + Map windowEventQueue = new ConcurrentHashMap<>(); public void windowTitle(String title) { @@ -9683,7 +9729,8 @@ public void windowResize(int newWidth, int newHeight) { * only the notification that the resize has happened. */ public void postWindowResized(int newWidth, int newHeight) { - windowEventQueue.put("wh", new WindowEventValuePairs(newWidth, newHeight)); + windowEventQueue.put("w", newWidth); + windowEventQueue.put("h", newHeight); } @@ -9723,7 +9770,8 @@ public void postWindowMoved(int newX, int newY) { frameMoved(newX, newY); } - windowEventQueue.put("xy", new WindowEventValuePairs(newX, newY)); + windowEventQueue.put("x", newX); + windowEventQueue.put("y", newY); } @@ -9732,28 +9780,21 @@ public void windowMoved() { } private void dequeueWindowEvents() { - if (windowEventQueue.containsKey("xy")) { - WindowEventValuePairs xy = windowEventQueue.remove("xy"); - windowX = xy.num1; - windowY = xy.num2; + if (windowEventQueue.containsKey("x")) { + windowX = windowEventQueue.remove("x"); + windowY = windowEventQueue.remove("y"); windowMoved(); } - if (windowEventQueue.containsKey("wh")) { - WindowEventValuePairs wh = windowEventQueue.remove("wh"); + if (windowEventQueue.containsKey("w")) { + // these should already match width/height + //windowResized(windowEventQueue.remove("w"), + // windowEventQueue.remove("h")); + windowEventQueue.remove("w"); + windowEventQueue.remove("h"); windowResized(); } } - protected class WindowEventValuePairs { - - public int num1; - public int num2; - - public WindowEventValuePairs(int num1, int num2) { - this.num1 = num1; - this.num2 = num2; - } - } /** * Scale the sketch as if it fits this specific width and height. diff --git a/core/src/processing/core/PConstants.java b/core/src/processing/core/PConstants.java index af17c1fbfb..2c1949dc5b 100644 --- a/core/src/processing/core/PConstants.java +++ b/core/src/processing/core/PConstants.java @@ -62,6 +62,11 @@ public interface PConstants { String P2D = "processing.opengl.PGraphics2D"; String P3D = "processing.opengl.PGraphics3D"; + String PV2D = "processing.vulkan.PVKGraphics2D"; + String PV3D = "processing.vulkan.PVKGraphics3D"; + + int AUTO = -101; + // When will it be time to remove this? @Deprecated String OPENGL = P3D; diff --git a/core/src/processing/opengl/FrameBuffer.java b/core/src/processing/opengl/FrameBuffer.java index a5426c52d1..fa04636c0b 100644 --- a/core/src/processing/opengl/FrameBuffer.java +++ b/core/src/processing/opengl/FrameBuffer.java @@ -71,14 +71,14 @@ public class FrameBuffer implements PConstants { protected IntBuffer pixelBuffer; - FrameBuffer(PGraphicsOpenGL pg) { + public FrameBuffer(PGraphicsOpenGL pg) { this.pg = pg; pgl = pg.pgl; context = pgl.createEmptyContext(); } - FrameBuffer(PGraphicsOpenGL pg, int w, int h, int samples, int colorBuffers, + public FrameBuffer(PGraphicsOpenGL pg, int w, int h, int samples, int colorBuffers, int depthBits, int stencilBits, boolean packedDepthStencil, boolean screen) { this(pg); @@ -141,12 +141,12 @@ public class FrameBuffer implements PConstants { } - FrameBuffer(PGraphicsOpenGL pg, int w, int h) { + public FrameBuffer(PGraphicsOpenGL pg, int w, int h) { this(pg, w, h, 1, 1, 0, 0, false, false); } - FrameBuffer(PGraphicsOpenGL pg, int w, int h, boolean screen) { + public FrameBuffer(PGraphicsOpenGL pg, int w, int h, boolean screen) { this(pg, w, h, 1, 1, 0, 0, false, screen); } diff --git a/core/src/processing/opengl/PGL.java b/core/src/processing/opengl/PGL.java index 8f8418e16b..51719db066 100644 --- a/core/src/processing/opengl/PGL.java +++ b/core/src/processing/opengl/PGL.java @@ -2752,7 +2752,7 @@ protected interface Tessellator { } - protected interface TessellatorCallback { + public interface TessellatorCallback { void begin(int type); void end(); void vertex(Object data); diff --git a/core/src/processing/opengl/PGraphicsOpenGL.java b/core/src/processing/opengl/PGraphicsOpenGL.java index 88164f43e1..638f4546bd 100644 --- a/core/src/processing/opengl/PGraphicsOpenGL.java +++ b/core/src/processing/opengl/PGraphicsOpenGL.java @@ -1380,7 +1380,6 @@ protected boolean pointBuffersContextIsOutdated() { return !pgl.contextIsCurrent(pointBuffersContext); } - @Override public void beginDraw() { if (primaryGraphics) { @@ -1433,6 +1432,17 @@ public void beginDraw() { drawing = true; report("bot beginDraw()"); + + if (tessGeo != null) { + tessGeo.resetIndex(); + } + + } + + protected void enableMultipleBuffers() { + if (tessGeo != null) { + tessGeo.enableMultipleBuffers(); + } } @@ -4289,7 +4299,6 @@ public void camera() { 0, 0, 1, 0); } - /** * More flexible method for dealing with camera(). *

@@ -5556,7 +5565,7 @@ protected void processImageBeforeAsyncSave(PImage image) { } - protected static void completeFinishedPixelTransfers() { + public static void completeFinishedPixelTransfers() { ongoingPixelTransfersIterable.addAll(ongoingPixelTransfers); for (AsyncPixelReader pixelReader : ongoingPixelTransfersIterable) { // if the getter was not called this frame, @@ -5592,7 +5601,7 @@ protected void awaitAsyncSaveCompletion(String filename) { } - protected class AsyncPixelReader { + public class AsyncPixelReader { // PImage formats used internally to offload // color format conversion to save threads @@ -9089,17 +9098,39 @@ static protected class TessGeometry { int polyVertexCount; int firstPolyVertex; int lastPolyVertex; - FloatBuffer polyVerticesBuffer; - IntBuffer polyColorsBuffer; - FloatBuffer polyNormalsBuffer; - FloatBuffer polyTexCoordsBuffer; + + int MAX_BUFFERS = 256; + + int verticesIndex = 0; + int colorsIndex = 0; + int normalsIndex = 0; + int texCoordsIndex = 0; + int ambientIndex = 0; + int specularIndex = 0; + int emissiveIndex = 0; + int shininessIndex = 0; + int polyIndicesIndex = 0; + int lineVerticesIndex = 0; + int lineColorsIndex = 0; + int lineDirectionsIndex = 0; + int lineIndiciesIndex = 0; + int pointVerticesIndex = 0; + int pointColorsIndex = 0; + int pointDirectionsIndex = 0; + int pointIndiciesIndex = 0; + + + FloatBuffer[] polyVerticesBuffer; + IntBuffer[] polyColorsBuffer; + FloatBuffer[] polyNormalsBuffer; + FloatBuffer[] polyTexCoordsBuffer; // Polygon material properties (polyColors is used // as the diffuse color when lighting is enabled) - IntBuffer polyAmbientBuffer; - IntBuffer polySpecularBuffer; - IntBuffer polyEmissiveBuffer; - FloatBuffer polyShininessBuffer; + IntBuffer[] polyAmbientBuffer; + IntBuffer[] polySpecularBuffer; + IntBuffer[] polyEmissiveBuffer; + FloatBuffer[] polyShininessBuffer; // Generic attributes HashMap polyAttribBuffers = new HashMap<>(); @@ -9107,7 +9138,7 @@ static protected class TessGeometry { int polyIndexCount; int firstPolyIndex; int lastPolyIndex; - ShortBuffer polyIndicesBuffer; + ShortBuffer[] polyIndicesBuffer; IndexCache polyIndexCache = new IndexCache(); // Tessellated line data @@ -9167,8 +9198,10 @@ static protected class TessGeometry { renderMode = mode; bufObjStreaming = stream; allocate(); + disableMultipleBuffers(); } + // ----------------------------------------------------------------- // // Allocate/dispose @@ -9194,17 +9227,53 @@ void allocate() { pointOffsets = new float[2 * PGL.DEFAULT_TESS_VERTICES]; pointIndices = new short[PGL.DEFAULT_TESS_VERTICES]; + + polyVerticesBuffer = new FloatBuffer[MAX_BUFFERS]; + + polyColorsBuffer = new IntBuffer[MAX_BUFFERS]; + polyNormalsBuffer = new FloatBuffer[MAX_BUFFERS]; + polyTexCoordsBuffer = new FloatBuffer[MAX_BUFFERS]; + polyAmbientBuffer = new IntBuffer[MAX_BUFFERS]; + polySpecularBuffer = new IntBuffer[MAX_BUFFERS]; + polyEmissiveBuffer = new IntBuffer[MAX_BUFFERS]; + polyShininessBuffer = new FloatBuffer[MAX_BUFFERS]; + polyIndicesBuffer = new ShortBuffer[MAX_BUFFERS]; + +// lineVerticesBuffer = new FloatBuffer[MAX_BUFFERS]; +// lineColorsBuffer = new IntBuffer[MAX_BUFFERS]; +// lineDirectionsBuffer = new FloatBuffer[MAX_BUFFERS]; +// lineIndicesBuffer = new ShortBuffer[MAX_BUFFERS]; +// +// pointVerticesBuffer = new FloatBuffer[MAX_BUFFERS]; +// pointColorsBuffer = new IntBuffer[MAX_BUFFERS]; +// pointOffsetsBuffer = new FloatBuffer[MAX_BUFFERS]; +// pointIndicesBuffer = new ShortBuffer[MAX_BUFFERS]; + if (!bufObjStreaming) { - polyVerticesBuffer = PGL.allocateFloatBuffer(polyVertices); - polyColorsBuffer = PGL.allocateIntBuffer(polyColors); - polyNormalsBuffer = PGL.allocateFloatBuffer(polyNormals); - polyTexCoordsBuffer = PGL.allocateFloatBuffer(polyTexCoords); - polyAmbientBuffer = PGL.allocateIntBuffer(polyAmbient); - polySpecularBuffer = PGL.allocateIntBuffer(polySpecular); - polyEmissiveBuffer = PGL.allocateIntBuffer(polyEmissive); - polyShininessBuffer = PGL.allocateFloatBuffer(polyShininess); - polyIndicesBuffer = PGL.allocateShortBuffer(polyIndices); + + for (int i = 0; i < MAX_BUFFERS; i++) { + polyVerticesBuffer[i] = PGL.allocateFloatBuffer(polyVertices); + + polyColorsBuffer[i] = PGL.allocateIntBuffer(polyColors); + polyNormalsBuffer[i] = PGL.allocateFloatBuffer(polyNormals); + polyTexCoordsBuffer[i] = PGL.allocateFloatBuffer(polyTexCoords); + polyAmbientBuffer[i] = PGL.allocateIntBuffer(polyAmbient); + polySpecularBuffer[i] = PGL.allocateIntBuffer(polySpecular); + polyEmissiveBuffer[i] = PGL.allocateIntBuffer(polyEmissive); + polyShininessBuffer[i] = PGL.allocateFloatBuffer(polyShininess); + polyIndicesBuffer[i] = PGL.allocateShortBuffer(polyIndices); + +// lineVerticesBuffer[i] = PGL.allocateFloatBuffer(lineVertices); +// lineColorsBuffer[i] = PGL.allocateIntBuffer(lineColors); +// lineDirectionsBuffer[i] = PGL.allocateFloatBuffer(lineDirections); +// lineIndicesBuffer[i] = PGL.allocateShortBuffer(lineIndices); +// +// pointVerticesBuffer[i] = PGL.allocateFloatBuffer(pointVertices); +// pointColorsBuffer[i] = PGL.allocateIntBuffer(pointColors); +// pointOffsetsBuffer[i] = PGL.allocateFloatBuffer(pointOffsets); +// pointIndicesBuffer[i] = PGL.allocateShortBuffer(pointIndices); + } lineVerticesBuffer = PGL.allocateFloatBuffer(lineVertices); lineColorsBuffer = PGL.allocateIntBuffer(lineColors); @@ -9215,11 +9284,41 @@ void allocate() { pointColorsBuffer = PGL.allocateIntBuffer(pointColors); pointOffsetsBuffer = PGL.allocateFloatBuffer(pointOffsets); pointIndicesBuffer = PGL.allocateShortBuffer(pointIndices); + } clear(); } + public void resetIndex() { + verticesIndex = 0; + colorsIndex = 0; + normalsIndex = 0; + texCoordsIndex = 0; + ambientIndex = 0; + specularIndex = 0; + emissiveIndex = 0; + shininessIndex = 0; + polyIndicesIndex = 0; + lineVerticesIndex = 0; + lineColorsIndex = 0; + lineDirectionsIndex = 0; + lineIndiciesIndex = 0; + pointVerticesIndex = 0; + pointColorsIndex = 0; + pointDirectionsIndex = 0; + pointIndiciesIndex = 0; + } + + public void enableMultipleBuffers() { + MAX_BUFFERS = 256; + } + + public void disableMultipleBuffers() { + MAX_BUFFERS = 1; + } + + void initAttrib(VertexAttribute attrib) { if (attrib.type == PGL.FLOAT && !fpolyAttribs.containsKey(attrib.name)) { float[] temp = new float[attrib.tessSize * PGL.DEFAULT_TESS_VERTICES]; @@ -9485,7 +9584,7 @@ int getPointVertexSum(PVector v, int first, int last) { // Buffer mapping methods protected void mapPolyVerticesBuffer() { - polyVerticesBuffer = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asFloatBuffer(); + polyVerticesBuffer[verticesIndex%MAX_BUFFERS] = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asFloatBuffer(); } protected void initPolyVerticesBuffer(boolean onlymap, boolean unmap, int usage) { @@ -9503,7 +9602,7 @@ protected void initPolyVerticesBuffer(boolean onlymap, boolean unmap, int usage) pgl.unmapBuffer(PGL.ARRAY_BUFFER); } } else { - pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef, polyVerticesBuffer, usage); + pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef, polyVerticesBuffer[verticesIndex%MAX_BUFFERS], usage); } } @@ -9521,8 +9620,9 @@ protected void copyPolyVertices(int usage) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } else { updatePolyVerticesBuffer(); - pgl.bufferData(PGL.ARRAY_BUFFER, 4 * polyVertexCount * PGL.SIZEOF_FLOAT, polyVerticesBuffer, usage); + pgl.bufferData(PGL.ARRAY_BUFFER, 4 * polyVertexCount * PGL.SIZEOF_FLOAT, polyVerticesBuffer[(verticesIndex++)%MAX_BUFFERS], usage); } +// System.out.println("copyPolyVertices "+((verticesIndex)%MAX_BUFFERS)); } protected void copyPolyVertices(int offset, int size) { @@ -9533,14 +9633,15 @@ protected void copyPolyVertices(int offset, int size) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } else { updatePolyVerticesBuffer(offset, size); - polyVerticesBuffer.position(4 * offset); - pgl.bufferSubData(PGL.ARRAY_BUFFER, 4 * offset * PGL.SIZEOF_FLOAT, 4 * size * PGL.SIZEOF_FLOAT, polyVerticesBuffer); - polyVerticesBuffer.rewind(); + polyVerticesBuffer[verticesIndex%MAX_BUFFERS].position(4 * offset); + int before = verticesIndex%MAX_BUFFERS; + pgl.bufferSubData(PGL.ARRAY_BUFFER, 4 * offset * PGL.SIZEOF_FLOAT, 4 * size * PGL.SIZEOF_FLOAT, polyVerticesBuffer[(verticesIndex++)%MAX_BUFFERS]); + polyVerticesBuffer[before].rewind(); } } protected void mapPolyColorsBuffer() { - polyColorsBuffer = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); + polyColorsBuffer[colorsIndex] = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); } protected void initPolyColorsBuffer(boolean onlymap, boolean unmap, int usage) { @@ -9548,17 +9649,17 @@ protected void initPolyColorsBuffer(boolean onlymap, boolean unmap, int usage) { int sizei = polyVertexCount * PGL.SIZEOF_INT; if (bufObjStreaming) { if (onlymap) { - polyColorsBuffer = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); + polyColorsBuffer[colorsIndex%MAX_BUFFERS] = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); } else { pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, usage); - polyColorsBuffer = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); + polyColorsBuffer[colorsIndex%MAX_BUFFERS] = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); updatePolyColorsBuffer(); } if (unmap) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } } else { - pgl.bufferData(PGL.ARRAY_BUFFER, sizei, polyColorsBuffer, usage); + pgl.bufferData(PGL.ARRAY_BUFFER, sizei, polyColorsBuffer[colorsIndex%MAX_BUFFERS], usage); } } @@ -9576,7 +9677,7 @@ protected void copyPolyColors(int usage) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } else { updatePolyColorsBuffer(); - pgl.bufferData(PGL.ARRAY_BUFFER, polyVertexCount * PGL.SIZEOF_INT, polyColorsBuffer, usage); + pgl.bufferData(PGL.ARRAY_BUFFER, polyVertexCount * PGL.SIZEOF_INT, polyColorsBuffer[(colorsIndex++)%MAX_BUFFERS], usage); } } @@ -9588,14 +9689,15 @@ protected void copyPolyColors(int offset, int size) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } else { updatePolyColorsBuffer(offset, size); - polyColorsBuffer.position(offset); - pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, polyColorsBuffer); - polyColorsBuffer.rewind(); + polyColorsBuffer[colorsIndex].position(offset); + int beforeColorsIndex = colorsIndex%MAX_BUFFERS; + pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, polyColorsBuffer[(colorsIndex++)%MAX_BUFFERS]); + polyColorsBuffer[beforeColorsIndex].rewind(); } } protected void mapPolyNormalsBuffer() { - polyNormalsBuffer = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asFloatBuffer(); + polyNormalsBuffer[(colorsIndex)%MAX_BUFFERS] = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asFloatBuffer(); } protected void initPolyNormalsBuffer(boolean onlymap, boolean unmap, int usage) { @@ -9613,7 +9715,7 @@ protected void initPolyNormalsBuffer(boolean onlymap, boolean unmap, int usage) pgl.unmapBuffer(PGL.ARRAY_BUFFER); } } else { - pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, polyNormalsBuffer, usage); + pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, polyNormalsBuffer[(normalsIndex)%MAX_BUFFERS], usage); } } @@ -9631,7 +9733,7 @@ protected void copyPolyNormals(int usage) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } else { updatePolyNormalsBuffer(); - pgl.bufferData(PGL.ARRAY_BUFFER, 3 * polyVertexCount * PGL.SIZEOF_FLOAT, polyNormalsBuffer, usage); + pgl.bufferData(PGL.ARRAY_BUFFER, 3 * polyVertexCount * PGL.SIZEOF_FLOAT, polyNormalsBuffer[(normalsIndex++)%MAX_BUFFERS], usage); } } @@ -9643,14 +9745,15 @@ protected void copyPolyNormals(int offset, int size) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } else { updatePolyNormalsBuffer(offset, size); - polyNormalsBuffer.position(3 * offset); - pgl.bufferSubData(PGL.ARRAY_BUFFER, 3 * offset * PGL.SIZEOF_FLOAT, 3 * size * PGL.SIZEOF_FLOAT, polyNormalsBuffer); - polyNormalsBuffer.rewind(); + polyNormalsBuffer[(normalsIndex)%MAX_BUFFERS].position(3 * offset); + int before = (normalsIndex)%MAX_BUFFERS; + pgl.bufferSubData(PGL.ARRAY_BUFFER, 3 * offset * PGL.SIZEOF_FLOAT, 3 * size * PGL.SIZEOF_FLOAT, polyNormalsBuffer[(normalsIndex++)%MAX_BUFFERS]); + polyNormalsBuffer[before].rewind(); } } protected void mapPolyTexCoordsBuffer() { - polyTexCoordsBuffer = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asFloatBuffer(); + polyTexCoordsBuffer[(normalsIndex)%MAX_BUFFERS] = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asFloatBuffer(); } protected void initPolyTexCoordsBuffer(boolean onlymap, boolean unmap, int usage) { @@ -9668,7 +9771,7 @@ protected void initPolyTexCoordsBuffer(boolean onlymap, boolean unmap, int usage pgl.unmapBuffer(PGL.ARRAY_BUFFER); } } else { - pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef, polyTexCoordsBuffer, usage); + pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef, polyTexCoordsBuffer[(texCoordsIndex)%MAX_BUFFERS], usage); } } @@ -9686,7 +9789,7 @@ protected void copyPolyTexCoords(int usage) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } else { updatePolyTexCoordsBuffer(); - pgl.bufferData(PGL.ARRAY_BUFFER, 2 * polyVertexCount * PGL.SIZEOF_FLOAT, polyTexCoordsBuffer, usage); + pgl.bufferData(PGL.ARRAY_BUFFER, 2 * polyVertexCount * PGL.SIZEOF_FLOAT, polyTexCoordsBuffer[(texCoordsIndex++)%MAX_BUFFERS], usage); } } @@ -9698,14 +9801,15 @@ protected void copyPolyTexCoords(int offset, int size) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } else { updatePolyTexCoordsBuffer(offset, size); - polyTexCoordsBuffer.position(2 * offset); - pgl.bufferSubData(PGL.ARRAY_BUFFER, 2 * offset * PGL.SIZEOF_FLOAT, 2 * size * PGL.SIZEOF_FLOAT, polyTexCoordsBuffer); - polyTexCoordsBuffer.rewind(); + polyTexCoordsBuffer[(texCoordsIndex)%MAX_BUFFERS].position(2 * offset); + int before = (texCoordsIndex)%MAX_BUFFERS; + pgl.bufferSubData(PGL.ARRAY_BUFFER, 2 * offset * PGL.SIZEOF_FLOAT, 2 * size * PGL.SIZEOF_FLOAT, polyTexCoordsBuffer[(texCoordsIndex++)%MAX_BUFFERS]); + polyTexCoordsBuffer[before].rewind(); } } protected void mapPolyAmbientBuffer() { - polyAmbientBuffer = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); + polyAmbientBuffer[(ambientIndex)%MAX_BUFFERS] = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); } protected void initPolyAmbientBuffer(boolean onlymap, boolean unmap, int usage) { @@ -9713,17 +9817,17 @@ protected void initPolyAmbientBuffer(boolean onlymap, boolean unmap, int usage) int sizei = polyVertexCount * PGL.SIZEOF_INT; if (bufObjStreaming) { if (onlymap) { - polyAmbientBuffer = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); + polyAmbientBuffer[(ambientIndex)%MAX_BUFFERS] = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); } else { pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, usage); - polyAmbientBuffer = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); + polyAmbientBuffer[(ambientIndex)%MAX_BUFFERS] = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); updatePolyAmbientBuffer(); } if (unmap) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } } else { - pgl.bufferData(PGL.ARRAY_BUFFER, sizei, polyAmbientBuffer, usage); + pgl.bufferData(PGL.ARRAY_BUFFER, sizei, polyAmbientBuffer[(ambientIndex)%MAX_BUFFERS], usage); } } @@ -9741,7 +9845,7 @@ protected void copyPolyAmbient(int usage) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } else { updatePolyAmbientBuffer(); - pgl.bufferData(PGL.ARRAY_BUFFER, polyVertexCount * PGL.SIZEOF_INT, polyAmbientBuffer, usage); + pgl.bufferData(PGL.ARRAY_BUFFER, polyVertexCount * PGL.SIZEOF_INT, polyAmbientBuffer[(ambientIndex++)%MAX_BUFFERS], usage); } } @@ -9753,14 +9857,15 @@ protected void copyPolyAmbient(int offset, int size) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } else { updatePolyAmbientBuffer(offset, size); - polyAmbientBuffer.position(offset); - pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, polyAmbientBuffer); - polyAmbientBuffer.rewind(); + polyAmbientBuffer[(ambientIndex)%MAX_BUFFERS].position(offset); + int before = (ambientIndex)%MAX_BUFFERS; + pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, polyAmbientBuffer[(ambientIndex++)%MAX_BUFFERS]); + polyAmbientBuffer[before].rewind(); } } protected void mapPolySpecularBuffer() { - polySpecularBuffer = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); + polySpecularBuffer[(specularIndex)%MAX_BUFFERS] = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); } protected void initPolySpecularBuffer(boolean onlymap, boolean unmap, int usage) { @@ -9768,17 +9873,17 @@ protected void initPolySpecularBuffer(boolean onlymap, boolean unmap, int usage) int sizei = polyVertexCount * PGL.SIZEOF_INT; if (bufObjStreaming) { if (onlymap) { - polySpecularBuffer = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); + polySpecularBuffer[(specularIndex)%MAX_BUFFERS] = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); } else { pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, usage); - polySpecularBuffer = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); + polySpecularBuffer[(specularIndex)%MAX_BUFFERS] = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); updatePolySpecularBuffer(); } if (unmap) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } } else { - pgl.bufferData(PGL.ARRAY_BUFFER, sizei, polySpecularBuffer, usage); + pgl.bufferData(PGL.ARRAY_BUFFER, sizei, polySpecularBuffer[(specularIndex)%MAX_BUFFERS], usage); } } @@ -9796,7 +9901,7 @@ protected void copyPolySpecular(int usage) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } else { updatePolySpecularBuffer(); - pgl.bufferData(PGL.ARRAY_BUFFER, polyVertexCount * PGL.SIZEOF_INT, polySpecularBuffer, usage); + pgl.bufferData(PGL.ARRAY_BUFFER, polyVertexCount * PGL.SIZEOF_INT, polySpecularBuffer[(specularIndex++)%MAX_BUFFERS], usage); } } @@ -9808,14 +9913,15 @@ protected void copyPolySpecular(int offset, int size) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } else { updatePolySpecularBuffer(offset, size); - polySpecularBuffer.position(offset); - pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, polySpecularBuffer); - polySpecularBuffer.rewind(); + polySpecularBuffer[(specularIndex)%MAX_BUFFERS].position(offset); + int before = (specularIndex)%MAX_BUFFERS; + pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, polySpecularBuffer[(specularIndex++)%MAX_BUFFERS]); + polySpecularBuffer[before].rewind(); } } protected void mapPolyEmissiveBuffer() { - polyEmissiveBuffer = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); + polyEmissiveBuffer[(emissiveIndex)%MAX_BUFFERS] = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); } protected void initPolyEmissiveBuffer(boolean onlymap, boolean unmap, int usage) { @@ -9823,17 +9929,17 @@ protected void initPolyEmissiveBuffer(boolean onlymap, boolean unmap, int usage) int sizei = polyVertexCount * PGL.SIZEOF_INT; if (bufObjStreaming) { if (onlymap) { - polyEmissiveBuffer = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); + polyEmissiveBuffer[(emissiveIndex)%MAX_BUFFERS] = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); } else { pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, usage); - polyEmissiveBuffer = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); + polyEmissiveBuffer[(emissiveIndex)%MAX_BUFFERS] = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asIntBuffer(); updatePolyEmissiveBuffer(); } if (unmap) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } } else { - pgl.bufferData(PGL.ARRAY_BUFFER, sizei, polyEmissiveBuffer, usage); + pgl.bufferData(PGL.ARRAY_BUFFER, sizei, polyEmissiveBuffer[(emissiveIndex)%MAX_BUFFERS], usage); } } @@ -9851,7 +9957,7 @@ protected void copyPolyEmissive(int usage) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } else { updatePolyEmissiveBuffer(); - pgl.bufferData(PGL.ARRAY_BUFFER, polyVertexCount * PGL.SIZEOF_INT, polyEmissiveBuffer, usage); + pgl.bufferData(PGL.ARRAY_BUFFER, polyVertexCount * PGL.SIZEOF_INT, polyEmissiveBuffer[(emissiveIndex++)%MAX_BUFFERS], usage); } } @@ -9863,14 +9969,15 @@ protected void copyPolyEmissive(int offset, int size) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } else { updatePolyEmissiveBuffer(offset, size); - polyEmissiveBuffer.position(offset); - pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, polyEmissiveBuffer); - polyEmissiveBuffer.rewind(); + polyEmissiveBuffer[(emissiveIndex)%MAX_BUFFERS].position(offset); + int before = (emissiveIndex)%MAX_BUFFERS; + pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, polyEmissiveBuffer[(emissiveIndex++)%MAX_BUFFERS]); + polyEmissiveBuffer[before].rewind(); } } protected void mapPolyShininessBuffer() { - polyShininessBuffer = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asFloatBuffer(); + polyShininessBuffer[(shininessIndex)%MAX_BUFFERS] = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asFloatBuffer(); } protected void initPolyShininessBuffer(boolean onlymap, boolean unmap, int usage) { @@ -9878,17 +9985,17 @@ protected void initPolyShininessBuffer(boolean onlymap, boolean unmap, int usage int sizei = polyVertexCount * PGL.SIZEOF_FLOAT; if (bufObjStreaming) { if (onlymap) { - polyShininessBuffer = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asFloatBuffer(); + polyShininessBuffer[(shininessIndex)%MAX_BUFFERS] = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asFloatBuffer(); } else { pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, usage); - polyShininessBuffer = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asFloatBuffer(); + polyShininessBuffer[(shininessIndex)%MAX_BUFFERS] = pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asFloatBuffer(); updatePolyShininessBuffer(); } if (unmap) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } } else { - pgl.bufferData(PGL.ARRAY_BUFFER, sizei, polyShininessBuffer, usage); + pgl.bufferData(PGL.ARRAY_BUFFER, sizei, polyShininessBuffer[(shininessIndex)%MAX_BUFFERS], usage); } } @@ -9906,7 +10013,7 @@ protected void copyPolyShininess(int usage) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } else { updatePolyShininessBuffer(); - pgl.bufferData(PGL.ARRAY_BUFFER, polyVertexCount * PGL.SIZEOF_FLOAT, polyShininessBuffer, usage); + pgl.bufferData(PGL.ARRAY_BUFFER, polyVertexCount * PGL.SIZEOF_FLOAT, polyShininessBuffer[(shininessIndex++)%MAX_BUFFERS], usage); } } @@ -9918,9 +10025,10 @@ protected void copyPolyShininess(int offset, int size) { pgl.unmapBuffer(PGL.ARRAY_BUFFER); } else { updatePolyShininessBuffer(offset, size); - polyShininessBuffer.position(offset); - pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_FLOAT, size * PGL.SIZEOF_FLOAT, polyShininessBuffer); - polyShininessBuffer.rewind(); + polyShininessBuffer[(shininessIndex)%MAX_BUFFERS].position(offset); + int before = (shininessIndex)%MAX_BUFFERS; + pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_FLOAT, size * PGL.SIZEOF_FLOAT, polyShininessBuffer[(shininessIndex++)%MAX_BUFFERS]); + polyShininessBuffer[before].rewind(); } } @@ -9988,7 +10096,7 @@ protected void copyPolyAttribs(VertexAttribute attrib, int offset, int size) { } protected void mapPolyIndicesBuffer() { - polyIndicesBuffer = pg.pgl.mapBuffer(PGL.ELEMENT_ARRAY_BUFFER, PGL.bufferMapAccess).asShortBuffer(); + polyIndicesBuffer[(polyIndicesIndex)%MAX_BUFFERS] = pg.pgl.mapBuffer(PGL.ELEMENT_ARRAY_BUFFER, PGL.bufferMapAccess).asShortBuffer(); } protected void initPolyIndicesBuffer(boolean onlymap, boolean unmap, int usage) { @@ -10006,7 +10114,7 @@ protected void initPolyIndicesBuffer(boolean onlymap, boolean unmap, int usage) pgl.unmapBuffer(PGL.ELEMENT_ARRAY_BUFFER); } } else { - pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, sizei, polyIndicesBuffer, usage); + pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, sizei, polyIndicesBuffer[(polyIndicesIndex)%MAX_BUFFERS], usage); } } @@ -10019,11 +10127,12 @@ protected void copyPolyIndices(int usage) { pgl.unmapBuffer(PGL.ELEMENT_ARRAY_BUFFER); } else { updatePolyIndicesBuffer(); - pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, polyIndexCount * PGL.SIZEOF_INDEX, polyIndicesBuffer, usage); + pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, polyIndexCount * PGL.SIZEOF_INDEX, polyIndicesBuffer[(polyIndicesIndex++)%MAX_BUFFERS], usage); } } protected void mapLineVerticesBuffer() { +// lineVerticesBuffer[(lineVerticesIndex)%MAX_BUFFERS] = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asFloatBuffer(); lineVerticesBuffer = pg.pgl.mapBuffer(PGL.ARRAY_BUFFER, PGL.bufferMapAccess).asFloatBuffer(); } @@ -10434,7 +10543,30 @@ protected void updatePolyVerticesBuffer() { } protected void updatePolyVerticesBuffer(int offset, int size) { - PGL.updateFloatBuffer(polyVerticesBuffer, polyVertices, 4 * offset, 4 * size); + PGL.updateFloatBuffer(polyVerticesBuffer[verticesIndex%MAX_BUFFERS], polyVertices, 4 * offset, 4 * size); +// System.out.println("-----"); +// System.out.println("offset: "+offset+" size: "+size); +// System.out.println("normal array:"); +// try { +// for (int i = 0; i < 4; i++) { +// System.out.print(polyVertices[i]+" "); +// } +// } +// catch (RuntimeException e) { +// System.out.println("(woops)"); +// } +// +// +// System.out.println("\nBuffer: "); +// int max = polyVerticesBuffer[verticesIndex%MAX_BUFFERS].capacity(); +// if (16 < max) max = 16; +// +// for (int i = 0; i < max; i+=4) { +// float f = polyVerticesBuffer[verticesIndex%MAX_BUFFERS].get(); +// System.out.print(f+" "); +// } +// polyVerticesBuffer[verticesIndex%MAX_BUFFERS].rewind(); +// System.out.println(); } protected void updatePolyColorsBuffer() { @@ -10442,7 +10574,7 @@ protected void updatePolyColorsBuffer() { } protected void updatePolyColorsBuffer(int offset, int size) { - PGL.updateIntBuffer(polyColorsBuffer, polyColors, offset, size); + PGL.updateIntBuffer(polyColorsBuffer[colorsIndex%MAX_BUFFERS], polyColors, offset, size); } protected void updatePolyNormalsBuffer() { @@ -10450,7 +10582,7 @@ protected void updatePolyNormalsBuffer() { } protected void updatePolyNormalsBuffer(int offset, int size) { - PGL.updateFloatBuffer(polyNormalsBuffer, polyNormals, 3 * offset, 3 * size); + PGL.updateFloatBuffer(polyNormalsBuffer[normalsIndex%MAX_BUFFERS], polyNormals, 3 * offset, 3 * size); } protected void updatePolyTexCoordsBuffer() { @@ -10458,7 +10590,7 @@ protected void updatePolyTexCoordsBuffer() { } protected void updatePolyTexCoordsBuffer(int offset, int size) { - PGL.updateFloatBuffer(polyTexCoordsBuffer, polyTexCoords, 2 * offset, 2 * size); + PGL.updateFloatBuffer(polyTexCoordsBuffer[texCoordsIndex%MAX_BUFFERS], polyTexCoords, 2 * offset, 2 * size); } protected void updatePolyAmbientBuffer() { @@ -10466,7 +10598,7 @@ protected void updatePolyAmbientBuffer() { } protected void updatePolyAmbientBuffer(int offset, int size) { - PGL.updateIntBuffer(polyAmbientBuffer, polyAmbient, offset, size); + PGL.updateIntBuffer(polyAmbientBuffer[ambientIndex%MAX_BUFFERS], polyAmbient, offset, size); } protected void updatePolySpecularBuffer() { @@ -10474,7 +10606,7 @@ protected void updatePolySpecularBuffer() { } protected void updatePolySpecularBuffer(int offset, int size) { - PGL.updateIntBuffer(polySpecularBuffer, polySpecular, offset, size); + PGL.updateIntBuffer(polySpecularBuffer[specularIndex%MAX_BUFFERS], polySpecular, offset, size); } protected void updatePolyEmissiveBuffer() { @@ -10482,7 +10614,7 @@ protected void updatePolyEmissiveBuffer() { } protected void updatePolyEmissiveBuffer(int offset, int size) { - PGL.updateIntBuffer(polyEmissiveBuffer, polyEmissive, offset, size); + PGL.updateIntBuffer(polyEmissiveBuffer[emissiveIndex%MAX_BUFFERS], polyEmissive, offset, size); } protected void updatePolyShininessBuffer() { @@ -10490,7 +10622,7 @@ protected void updatePolyShininessBuffer() { } protected void updatePolyShininessBuffer(int offset, int size) { - PGL.updateFloatBuffer(polyShininessBuffer, polyShininess, offset, size); + PGL.updateFloatBuffer(polyShininessBuffer[shininessIndex%MAX_BUFFERS], polyShininess, offset, size); } protected void updateAttribBuffer(String name) { @@ -10522,7 +10654,7 @@ protected void updatePolyIndicesBuffer() { } protected void updatePolyIndicesBuffer(int offset, int size) { - PGL.updateShortBuffer(polyIndicesBuffer, polyIndices, offset, size); + PGL.updateShortBuffer(polyIndicesBuffer[polyIndicesIndex%MAX_BUFFERS], polyIndices, offset, size); } protected void updateLineVerticesBuffer() { @@ -10597,56 +10729,88 @@ void expandPolyVertices(int n) { float[] temp = new float[4 * n]; PApplet.arrayCopy(polyVertices, 0, temp, 0, 4 * polyVertexCount); polyVertices = temp; - if (!bufObjStreaming) polyVerticesBuffer = PGL.allocateFloatBuffer(polyVertices); + if (!bufObjStreaming) { + for (int i = 0; i < MAX_BUFFERS; i++) { + polyVerticesBuffer[i] = PGL.allocateFloatBuffer(polyVertices); + } + } } void expandPolyColors(int n) { int[] temp = new int[n]; PApplet.arrayCopy(polyColors, 0, temp, 0, polyVertexCount); polyColors = temp; - if (!bufObjStreaming) polyColorsBuffer = PGL.allocateIntBuffer(polyColors); + if (!bufObjStreaming) { + for (int i = 0; i < MAX_BUFFERS; i++) { + polyColorsBuffer[i] = PGL.allocateIntBuffer(polyColors); + } + } } void expandPolyNormals(int n) { float[] temp = new float[3 * n]; PApplet.arrayCopy(polyNormals, 0, temp, 0, 3 * polyVertexCount); polyNormals = temp; - if (!bufObjStreaming) polyNormalsBuffer = PGL.allocateFloatBuffer(polyNormals); + if (!bufObjStreaming) { + for (int i = 0; i < MAX_BUFFERS; i++) { + polyNormalsBuffer[i] = PGL.allocateFloatBuffer(polyNormals); + } + } } void expandPolyTexCoords(int n) { float[] temp = new float[2 * n]; PApplet.arrayCopy(polyTexCoords, 0, temp, 0, 2 * polyVertexCount); polyTexCoords = temp; - if (!bufObjStreaming) polyTexCoordsBuffer = PGL.allocateFloatBuffer(polyTexCoords); + if (!bufObjStreaming) { + for (int i = 0; i < MAX_BUFFERS; i++) { + polyTexCoordsBuffer[i] = PGL.allocateFloatBuffer(polyTexCoords); + } + } } void expandPolyAmbient(int n) { int[] temp = new int[n]; PApplet.arrayCopy(polyAmbient, 0, temp, 0, polyVertexCount); polyAmbient = temp; - if (!bufObjStreaming) polyAmbientBuffer = PGL.allocateIntBuffer(polyAmbient); + if (!bufObjStreaming) { + for (int i = 0; i < MAX_BUFFERS; i++) { + polyAmbientBuffer[i] = PGL.allocateIntBuffer(polyAmbient); + } + } } void expandPolySpecular(int n) { int[] temp = new int[n]; PApplet.arrayCopy(polySpecular, 0, temp, 0, polyVertexCount); polySpecular = temp; - if (!bufObjStreaming) polySpecularBuffer = PGL.allocateIntBuffer(polySpecular); + if (!bufObjStreaming) { + for (int i = 0; i < MAX_BUFFERS; i++) { + polySpecularBuffer[i] = PGL.allocateIntBuffer(polySpecular); + } + } } void expandPolyEmissive(int n) { int[] temp = new int[n]; PApplet.arrayCopy(polyEmissive, 0, temp, 0, polyVertexCount); polyEmissive = temp; - if (!bufObjStreaming) polyEmissiveBuffer = PGL.allocateIntBuffer(polyEmissive); + if (!bufObjStreaming) { + for (int i = 0; i < MAX_BUFFERS; i++) { + polyEmissiveBuffer[i] = PGL.allocateIntBuffer(polyEmissive); + } + } } void expandPolyShininess(int n) { float[] temp = new float[n]; PApplet.arrayCopy(polyShininess, 0, temp, 0, polyVertexCount); polyShininess = temp; - if (!bufObjStreaming) polyShininessBuffer = PGL.allocateFloatBuffer(polyShininess); + if (!bufObjStreaming) { + for (int i = 0; i < MAX_BUFFERS; i++) { + polyShininessBuffer[i] = PGL.allocateFloatBuffer(polyShininess); + } + } } void expandAttributes(int n) { @@ -10690,7 +10854,11 @@ void expandPolyIndices(int n) { short[] temp = new short[n]; PApplet.arrayCopy(polyIndices, 0, temp, 0, polyIndexCount); polyIndices = temp; - if (!bufObjStreaming) polyIndicesBuffer = PGL.allocateShortBuffer(polyIndices); + if (!bufObjStreaming) { + for (int i = 0; i < MAX_BUFFERS; i++) { + polyIndicesBuffer[i] = PGL.allocateShortBuffer(polyIndices); + } + } } void expandLineVertices(int n) { @@ -10791,60 +10959,62 @@ void trim() { } } + + // TODO: For loop instead of the current index. void trimPolyVertices() { float[] temp = new float[4 * polyVertexCount]; PApplet.arrayCopy(polyVertices, 0, temp, 0, 4 * polyVertexCount); polyVertices = temp; - if (!bufObjStreaming) polyVerticesBuffer = PGL.allocateFloatBuffer(polyVertices); + if (!bufObjStreaming) polyVerticesBuffer[verticesIndex%MAX_BUFFERS] = PGL.allocateFloatBuffer(polyVertices); } void trimPolyColors() { int[] temp = new int[polyVertexCount]; PApplet.arrayCopy(polyColors, 0, temp, 0, polyVertexCount); polyColors = temp; - if (!bufObjStreaming) polyColorsBuffer = PGL.allocateIntBuffer(polyColors); + if (!bufObjStreaming) polyColorsBuffer[colorsIndex%MAX_BUFFERS] = PGL.allocateIntBuffer(polyColors); } void trimPolyNormals() { float[] temp = new float[3 * polyVertexCount]; PApplet.arrayCopy(polyNormals, 0, temp, 0, 3 * polyVertexCount); polyNormals = temp; - if (!bufObjStreaming) polyNormalsBuffer = PGL.allocateFloatBuffer(polyNormals); + if (!bufObjStreaming) polyNormalsBuffer[normalsIndex%MAX_BUFFERS] = PGL.allocateFloatBuffer(polyNormals); } void trimPolyTexCoords() { float[] temp = new float[2 * polyVertexCount]; PApplet.arrayCopy(polyTexCoords, 0, temp, 0, 2 * polyVertexCount); polyTexCoords = temp; - if (!bufObjStreaming) polyTexCoordsBuffer = PGL.allocateFloatBuffer(polyTexCoords); + if (!bufObjStreaming) polyTexCoordsBuffer[texCoordsIndex%MAX_BUFFERS] = PGL.allocateFloatBuffer(polyTexCoords); } void trimPolyAmbient() { int[] temp = new int[polyVertexCount]; PApplet.arrayCopy(polyAmbient, 0, temp, 0, polyVertexCount); polyAmbient = temp; - if (!bufObjStreaming) polyAmbientBuffer = PGL.allocateIntBuffer(polyAmbient); + if (!bufObjStreaming) polyAmbientBuffer[ambientIndex%MAX_BUFFERS] = PGL.allocateIntBuffer(polyAmbient); } void trimPolySpecular() { int[] temp = new int[polyVertexCount]; PApplet.arrayCopy(polySpecular, 0, temp, 0, polyVertexCount); polySpecular = temp; - if (!bufObjStreaming) polySpecularBuffer = PGL.allocateIntBuffer(polySpecular); + if (!bufObjStreaming) polySpecularBuffer[specularIndex%MAX_BUFFERS] = PGL.allocateIntBuffer(polySpecular); } void trimPolyEmissive() { int[] temp = new int[polyVertexCount]; PApplet.arrayCopy(polyEmissive, 0, temp, 0, polyVertexCount); polyEmissive = temp; - if (!bufObjStreaming) polyEmissiveBuffer = PGL.allocateIntBuffer(polyEmissive); + if (!bufObjStreaming) polyEmissiveBuffer[emissiveIndex%MAX_BUFFERS] = PGL.allocateIntBuffer(polyEmissive); } void trimPolyShininess() { float[] temp = new float[polyVertexCount]; PApplet.arrayCopy(polyShininess, 0, temp, 0, polyVertexCount); polyShininess = temp; - if (!bufObjStreaming) polyShininessBuffer = PGL.allocateFloatBuffer(polyShininess); + if (!bufObjStreaming) polyShininessBuffer[shininessIndex%MAX_BUFFERS] = PGL.allocateFloatBuffer(polyShininess); } void trimPolyAttributes() { @@ -10888,7 +11058,7 @@ void trimPolyIndices() { short[] temp = new short[polyIndexCount]; PApplet.arrayCopy(polyIndices, 0, temp, 0, polyIndexCount); polyIndices = temp; - if (!bufObjStreaming) polyIndicesBuffer = PGL.allocateShortBuffer(polyIndices); + if (!bufObjStreaming) polyIndicesBuffer[polyIndicesIndex%MAX_BUFFERS] = PGL.allocateShortBuffer(polyIndices); } void trimLineVertices() { @@ -14580,4 +14750,4 @@ static void rotateRight(int[] array, int i1, int i2) { } -} +} \ No newline at end of file diff --git a/core/src/processing/vulkan/PGraphicsVulkan.java b/core/src/processing/vulkan/PGraphicsVulkan.java new file mode 100644 index 0000000000..3622327c05 --- /dev/null +++ b/core/src/processing/vulkan/PGraphicsVulkan.java @@ -0,0 +1,51 @@ +package processing.vulkan; + +import processing.GL2VK.GL2VK; +import processing.core.PSurface; +import processing.opengl.PGL; +import processing.opengl.PGraphicsOpenGL; +import processing.opengl.PJOGL; + +public class PGraphicsVulkan extends PGraphicsOpenGL { + + public PGraphicsVulkan() { + super(); + } + + @Override + protected PGL createPGL(PGraphicsOpenGL pg) { + return new PVK(pg); + } + + @Override + public PSurface createSurface() { + return surface = new PSurfaceVK(this); + } + + public void selectNode(int node) { + ((PVK)pgl).selectNode(node); + } + + public void enableAutoMode() { + ((PVK)pgl).enableAutoMode(); + } + + public int getNodesCount() { + return ((PVK)pgl).getNodesCount(); + } + + public void setMaxNodes(int v) { + ((PVK)pgl).setMaxNodes(v); + } + + + public void bufferMultithreaded(boolean onoff) { + ((PVK)pgl).bufferMultithreaded(onoff); + } + + @Override + public void beginDraw() { + super.beginDraw(); + enableMultipleBuffers(); + } +} diff --git a/core/src/processing/vulkan/PJOGLInterface.java b/core/src/processing/vulkan/PJOGLInterface.java new file mode 100644 index 0000000000..7af0c19b3b --- /dev/null +++ b/core/src/processing/vulkan/PJOGLInterface.java @@ -0,0 +1,196 @@ +package processing.vulkan; + +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Shape; +import java.awt.Toolkit; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.PathIterator; +import java.io.IOException; +import java.net.URL; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.HashMap; +import java.util.Map; + +import com.jogamp.common.util.VersionNumber; +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GL2; +import com.jogamp.opengl.GL2ES2; +import com.jogamp.opengl.GL2ES3; +import com.jogamp.opengl.GL2GL3; +import com.jogamp.opengl.GL3ES3; +import com.jogamp.opengl.GLAutoDrawable; +import com.jogamp.opengl.GLCapabilities; +import com.jogamp.opengl.GLCapabilitiesImmutable; +import com.jogamp.opengl.GLContext; +import com.jogamp.opengl.GLDrawable; +import com.jogamp.opengl.fixedfunc.GLMatrixFunc; +import com.jogamp.opengl.GLRendererQuirks; +import com.jogamp.opengl.glu.GLU; +import com.jogamp.opengl.glu.GLUtessellator; +import com.jogamp.opengl.glu.GLUtessellatorCallbackAdapter; + + +public interface PJOGLInterface { +public Object getNative(); +public void setCaps(GLCapabilities caps); +public GLCapabilitiesImmutable getCaps(); +public boolean needSharedObjectSync(); +public void setFps(float fps); +public void getGL(GLAutoDrawable glDrawable); +public void init(GLAutoDrawable glDrawable); +public void showme(String method, int value); +public void flush(); +public void finish(); +public void hint(int target, int hint); +public void enable(int value); +public void disable(int value); +public void getBooleanv(int value, IntBuffer data); +public void getIntegerv(int value, IntBuffer data); +public void getFloatv(int value, FloatBuffer data); +public boolean isEnabled(int value); +public String getString(int name); +public int getError(); +public String errorString(int err); +public void genBuffers(int n, IntBuffer buffers); +public void deleteBuffers(int n, IntBuffer buffers); +public void bindBuffer(int target, int buffer); +public void bufferData(int target, int size, Buffer data, int usage); +public void bufferSubData(int target, int offset, int size, Buffer data); +public void isBuffer(int buffer); +public void getBufferParameteriv(int target, int value, IntBuffer data); +public ByteBuffer mapBuffer(int target, int access); +public ByteBuffer mapBufferRange(int target, int offset, int length, int access); +public void unmapBuffer(int target); +public long fenceSync(int condition, int flags); +public void deleteSync(long sync); +public int clientWaitSync(long sync, int flags, long timeout); +public void depthRangef(float n, float f); +public void viewport(int x, int y, int w, int h); +public void vertexAttrib1f(int index, float value); +public void vertexAttrib2f(int index, float value0, float value1); +public void vertexAttrib3f(int index, float value0, float value1, float value2); +public void vertexAttrib4f(int index, float value0, float value1, float value2, float value3); +public void vertexAttrib1fv(int index, FloatBuffer values); +public void vertexAttrib2fv(int index, FloatBuffer values); +public void vertexAttrib3fv(int index, FloatBuffer values); +public void vertexAttrib4fv(int index, FloatBuffer values); +public void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, int offset); +public void enableVertexAttribArray(int index); +public void disableVertexAttribArray(int index); +public void drawArraysImpl(int mode, int first, int count); +public void drawElementsImpl(int mode, int count, int type, int offset); +public void lineWidth(float width); +public void frontFace(int dir); +public void cullFace(int mode); +public void polygonOffset(float factor, float units); +public void pixelStorei(int pname, int param); +public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, Buffer data); +public void copyTexImage2D(int target, int level, int internalFormat, int x, int y, int width, int height, int border); +public void texSubImage2D(int target, int level, int xOffset, int yOffset, int width, int height, int format, int type, Buffer data); +public void copyTexSubImage2D(int target, int level, int xOffset, int yOffset, int x, int y, int width, int height); +public void compressedTexImage2D(int target, int level, int internalFormat, int width, int height, int border, int imageSize, Buffer data); +public void compressedTexSubImage2D(int target, int level, int xOffset, int yOffset, int width, int height, int format, int imageSize, Buffer data); +public void texParameteri(int target, int pname, int param); +public void texParameterf(int target, int pname, float param); +public void texParameteriv(int target, int pname, IntBuffer params); +public void texParameterfv(int target, int pname, FloatBuffer params); +public void generateMipmap(int target); +public void genTextures(int n, IntBuffer textures); +public void deleteTextures(int n, IntBuffer textures); +public void getTexParameteriv(int target, int pname, IntBuffer params); +public void getTexParameterfv(int target, int pname, FloatBuffer params); +public boolean isTexture(int texture); +public int createShader(int type); +public void shaderSource(int shader, String source); +public void compileShader(int shader); +public void releaseShaderCompiler(); +public void deleteShader(int shader); +public void shaderBinary(int count, IntBuffer shaders, int binaryFormat, Buffer binary, int length); +public int createProgram(); +public void attachShader(int program, int shader); +public void detachShader(int program, int shader); +public void linkProgram(int program); +public void useProgram(int program); +public void deleteProgram(int program); +public String getActiveAttrib(int program, int index, IntBuffer size, IntBuffer type); +public int getAttribLocation(int program, String name); +public void bindAttribLocation(int program, int index, String name); +public int getUniformLocation(int program, String name); +public String getActiveUniform(int program, int index, IntBuffer size, IntBuffer type); +public void uniform1i(int location, int value); +public void uniform2i(int location, int value0, int value1); +public void uniform3i(int location, int value0, int value1, int value2); +public void uniform4i(int location, int value0, int value1, int value2, int value3); +public void uniform1f(int location, float value); +public void uniform2f(int location, float value0, float value1); +public void uniform3f(int location, float value0, float value1, float value2); +public void uniform4f(int location, float value0, float value1, float value2, float value3); +public void uniform1iv(int location, int count, IntBuffer v); +public void uniform2iv(int location, int count, IntBuffer v); +public void uniform3iv(int location, int count, IntBuffer v); +public void uniform4iv(int location, int count, IntBuffer v); +public void uniform1fv(int location, int count, FloatBuffer v); +public void uniform2fv(int location, int count, FloatBuffer v); +public void uniform3fv(int location, int count, FloatBuffer v); +public void uniform4fv(int location, int count, FloatBuffer v); +public void uniformMatrix2fv(int location, int count, boolean transpose, FloatBuffer mat); +public void uniformMatrix3fv(int location, int count, boolean transpose, FloatBuffer mat); +public void uniformMatrix4fv(int location, int count, boolean transpose, FloatBuffer mat); +public void validateProgram(int program); +public boolean isShader(int shader); +public void getShaderiv(int shader, int pname, IntBuffer params); +public void getAttachedShaders(int program, int maxCount, IntBuffer count, IntBuffer shaders); +public String getShaderInfoLog(int shader); +public String getShaderSource(int shader); +public void getShaderPrecisionFormat(int shaderType, int precisionType, IntBuffer range, IntBuffer precision); +public void getVertexAttribfv(int index, int pname, FloatBuffer params); +public void getVertexAttribiv(int index, int pname, IntBuffer params); +public void getVertexAttribPointerv(int index, int pname, ByteBuffer data); +public void getUniformfv(int program, int location, FloatBuffer params); +public void getUniformiv(int program, int location, IntBuffer params); +public boolean isProgram(int program); +public void getProgramiv(int program, int pname, IntBuffer params); +public String getProgramInfoLog(int program); +public void scissor(int x, int y, int w, int h); +public void sampleCoverage(float value, boolean invert); +public void stencilFunc(int func, int ref, int mask); +public void stencilFuncSeparate(int face, int func, int ref, int mask); +public void stencilOp(int sfail, int dpfail, int dppass); +public void stencilOpSeparate(int face, int sfail, int dpfail, int dppass); +public void depthFunc(int func); +public void blendEquation(int mode); +public void blendEquationSeparate(int modeRGB, int modeAlpha); +public void blendFunc(int src, int dst); +public void blendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha); +public void blendColor(float red, float green, float blue, float alpha); +public void colorMask(boolean r, boolean g, boolean b, boolean a); +public void depthMask(boolean mask); +public void stencilMask(int mask); +public void stencilMaskSeparate(int face, int mask); +public void clearColor(float r, float g, float b, float a); +public void clearDepth(float d); +public void clearStencil(int s); +public void clear(int buf); +public void deleteFramebuffers(int n, IntBuffer framebuffers); +public void genFramebuffers(int n, IntBuffer framebuffers); +public void bindRenderbuffer(int target, int renderbuffer); +public void deleteRenderbuffers(int n, IntBuffer renderbuffers); +public void genRenderbuffers(int n, IntBuffer renderbuffers); +public void renderbufferStorage(int target, int internalFormat, int width, int height); +public void framebufferRenderbuffer(int target, int attachment, int rbt, int renderbuffer); +public void framebufferTexture2D(int target, int attachment, int texTarget, int texture, int level); +public int checkFramebufferStatus(int target); +public boolean isFramebuffer(int framebuffer); +public void getFramebufferAttachmentParameteriv(int target, int attachment, int name, IntBuffer params); +public boolean isRenderbuffer(int renderbuffer); +public void getRenderbufferParameteriv(int target, int name, IntBuffer params); +public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter); +public void renderbufferStorageMultisample(int target, int samples, int format, int width, int height); +public void readBuffer(int buf); +public void drawBuffer(int buf); +} diff --git a/core/src/processing/vulkan/PSurfaceVK.java b/core/src/processing/vulkan/PSurfaceVK.java new file mode 100644 index 0000000000..dc27fefd2e --- /dev/null +++ b/core/src/processing/vulkan/PSurfaceVK.java @@ -0,0 +1,682 @@ +package processing.vulkan; + +import java.awt.EventQueue; +import java.awt.FileDialog; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; + +import static org.lwjgl.glfw.GLFW.GLFW_CLIENT_API; +import static org.lwjgl.glfw.GLFW.GLFW_CURSOR; +import static org.lwjgl.glfw.GLFW.GLFW_CURSOR_NORMAL; +import static org.lwjgl.glfw.GLFW.GLFW_FALSE; +import static org.lwjgl.glfw.GLFW.GLFW_NO_API; +import static org.lwjgl.glfw.GLFW.GLFW_RESIZABLE; +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.glfw.GLFWVulkan.glfwCreateWindowSurface; +import static org.lwjgl.system.MemoryStack.stackPush; +import static org.lwjgl.system.MemoryUtil.NULL; +import static org.lwjgl.vulkan.VK10.VK_NULL_HANDLE; +import static org.lwjgl.vulkan.VK10.VK_SUCCESS; + +import java.awt.DisplayMode; +import java.io.File; +import java.nio.BufferOverflowException; +import java.nio.LongBuffer; +import java.util.ArrayList; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkInstance; + +import processing.GL2VK.GL2VK; +import processing.GL2VK.Util; +import processing.awt.ShimAWT; +import processing.core.PApplet; +import processing.core.PConstants; +import processing.core.PGraphics; +import processing.core.PImage; +import processing.core.PSurface; +import processing.event.KeyEvent; +import processing.event.MouseEvent; +import processing.opengl.PGraphicsOpenGL; + +// NEXT TODO: +// Create a function which creates a surface with a specified width and height, puts it into +// animation thread. + +public class PSurfaceVK implements PSurface { + + protected PGraphics graphics; + + protected PApplet sketch; + protected int sketchWidth = 0; + protected int sketchHeight = 0; + private boolean windowCreated = false; + + private Thread drawExceptionHandler; + private Object drawExceptionMutex = new Object(); + protected Throwable drawException; + + private AnimatorTask animationThread; + private boolean isStopped = true; + + private PVK pvk; + + public static GL2VK gl2vk = null; + + ///////////////////////////////// + // GLFW window variables + public long glfwwindow; + public int glfwwidth = 1200; + public int glfwheight = 800; + public long glfwsurface; + public boolean glfwframebufferResize = false; + + private int glfwMouseX = 0, glfwMouseY = 0; + private int glfwButton = 0, glfwAction = 0; + + private static int GLFW_SHIFT = 340; + private static int GLFW_ENTER = 257; + private static int GLFW_BACKSPACE = 259; + private static int GLFW_CTRL = 341; + private static int GLFW_ALT = 342; + + // TODO: Make framerate dynamic. + class AnimatorTask extends TimerTask { + Timer timer; + float fps = 60.0f; + + public AnimatorTask(float fps) { + super(); + this.fps = fps; + } + + public void start() { + final long period = 0 < fps ? (long) (1000.0f / fps) : 1; // 0 -> 1: IllegalArgumentException: Non-positive period + timer = new Timer(); + timer.scheduleAtFixedRate(this, 0, period); + } + + long then = 0; + + @Override + public void run() { +// System.out.println("INTERVAL "+(System.nanoTime()-then)); +// Util.beginTmr(); + + // We create the window here because glfw window needs to be created in the same + // thread as it is run in. + if (!windowCreated) { + initWindow(); + } + + if (pvk.shouldClose()) { + sketch.exit(); + } +// + if (!sketch.finished) { + pvk.getGL(pvk); + +// Util.beginTmr(); + pvk.beginRecord(); + pvk.selectNode(0); + pvk.enableAutoMode(); + sketch.handleDraw(); + pvk.endRecord(); + +// Util.beginTmr(); + +// Util.beginTmr(); + PGraphicsOpenGL.completeFinishedPixelTransfers(); +// Util.endTmr("completeFinishedPixelTransfers"); + } + if (sketch.exitCalled()) { + sketch.dispose(); + sketch.exitActual(); + pvk.cleanup(); + } +// Util.endTmr("end entire cycle"); +// then = System.nanoTime(); + } + } + + public PSurfaceVK(PGraphics graphics) { + this.graphics = graphics; + this.pvk = (PVK)((PGraphicsVulkan) graphics).pgl; + } + + public void initOffscreen(PApplet sketch) { + this.sketch = sketch; + + sketchWidth = sketch.sketchWidth(); + sketchHeight = sketch.sketchHeight(); + } + + public void initFrame(PApplet sketch) { + this.sketch = sketch; + sketchWidth = sketch.sketchWidth(); + sketchHeight = sketch.sketchHeight(); + + initIcons(); + initAnimator(); + } + + private void initIcons() { +// IOUtil.ClassResources res; +// if (PJOGL.icons == null || PJOGL.icons.length == 0) { +// // Default Processing icons +// final int[] sizes = { 16, 32, 48, 64, 128, 256, 512 }; +// String[] iconImages = new String[sizes.length]; +// for (int i = 0; i < sizes.length; i++) { +// iconImages[i] = "/icon/icon-" + sizes[i] + ".png"; +// } +// res = new ClassResources(iconImages, +// PApplet.class.getClassLoader(), +// PApplet.class); +// } else { +// // Loading custom icons from user-provided files. +// String[] iconImages = new String[PJOGL.icons.length]; +// for (int i = 0; i < PJOGL.icons.length; i++) { +// iconImages[i] = resourceFilename(PJOGL.icons[i]); +// } +// +// res = new ClassResources(iconImages, +// sketch.getClass().getClassLoader(), +// sketch.getClass()); +// } +// NewtFactory.setWindowIcons(res); + // TODO: make work for vulkan + } + + public void createGLFWSurface(VkInstance instance) { + + try(MemoryStack stack = stackPush()) { + + LongBuffer pSurface = stack.longs(VK_NULL_HANDLE); + + if(glfwCreateWindowSurface(instance, glfwwindow, null, pSurface) != VK_SUCCESS) { + throw new RuntimeException("Failed to create window surface"); + } + + glfwsurface = pSurface.get(0); + } + } + + + private static boolean isPCodedKey(int code) { + return +// code == com.jogamp.newt.event.KeyEvent.VK_UP || +// code == com.jogamp.newt.event.KeyEvent.VK_DOWN || +// code == com.jogamp.newt.event.KeyEvent.VK_LEFT || +// code == com.jogamp.newt.event.KeyEvent.VK_RIGHT || + code == GLFW_ALT || + code == GLFW_CTRL || + code == GLFW_SHIFT; +// code == GLFW_WINDOWS || +// (!isHackyKey(code)); + } + + + // Why do we need this mapping? + // Relevant discussion and links here: + // http://forum.jogamp.org/Newt-wrong-keycode-for-key-td4033690.html#a4033697 + // (I don't think this is a complete solution). + private static int mapToPConst(int code) { + return switch (code) { + case com.jogamp.newt.event.KeyEvent.VK_UP -> PConstants.UP; + case com.jogamp.newt.event.KeyEvent.VK_DOWN -> PConstants.DOWN; + case com.jogamp.newt.event.KeyEvent.VK_LEFT -> PConstants.LEFT; + case com.jogamp.newt.event.KeyEvent.VK_RIGHT -> PConstants.RIGHT; + case com.jogamp.newt.event.KeyEvent.VK_ALT -> PConstants.ALT; + case com.jogamp.newt.event.KeyEvent.VK_CONTROL -> PConstants.CONTROL; + case com.jogamp.newt.event.KeyEvent.VK_SHIFT -> PConstants.SHIFT; + case com.jogamp.newt.event.KeyEvent.VK_WINDOWS -> java.awt.event.KeyEvent.VK_META; + default -> code; + }; + } + + + private static boolean isHackyKey(int code) { + return (code == GLFW_BACKSPACE || +// code == com.jogamp.newt.event.KeyEvent.VK_TAB || + code == GLFW_ENTER +// code == com.jogamp.newt.event.KeyEvent.VK_ESCAPE || +// code == com.jogamp.newt.event.KeyEvent.VK_DELETE + ); + } + + + private static char hackToChar(int code, char def) { + return switch (code) { + case com.jogamp.newt.event.KeyEvent.VK_BACK_SPACE -> PConstants.BACKSPACE; + case com.jogamp.newt.event.KeyEvent.VK_TAB -> PConstants.TAB; + case com.jogamp.newt.event.KeyEvent.VK_ENTER -> PConstants.ENTER; + case com.jogamp.newt.event.KeyEvent.VK_ESCAPE -> PConstants.ESC; + case com.jogamp.newt.event.KeyEvent.VK_DELETE -> PConstants.DELETE; + default -> def; + }; + } + + + protected void nativeKeyEvent(long millis, int action, int modifiers, + int key, int keyCode, boolean isAutoRepeat) { + // SHIFT, CTRL, META, and ALT are identical to processing.event.Event +// int modifiers = nativeEvent.getModifiers(); +// int peModifiers = nativeEvent.getModifiers() & +// (InputEvent.SHIFT_MASK | +// InputEvent.CTRL_MASK | +// InputEvent.META_MASK | +// InputEvent.ALT_MASK); + + + + + // From http://jogamp.org/deployment/v2.1.0/javadoc/jogl/javadoc/com/jogamp/newt/event/KeyEvent.html + // public final short getKeySymbol() + // Returns the virtual key symbol reflecting the current keyboard layout. + // public final short getKeyCode() + // Returns the virtual key code using a fixed mapping to the US keyboard layout. + // In contrast to key symbol, key code uses a fixed US keyboard layout and therefore is keyboard layout independent. + // E.g. virtual key code VK_Y denotes the same physical key regardless whether keyboard layout QWERTY or QWERTZ is active. The key symbol of the former is VK_Y, where the latter produces VK_Y. + + + if (!isPCodedKey(key)) { + key = Character.toLowerCase(key); + + if (modifiers == GLFW_MOD_SHIFT) { + key = Character.toUpperCase(key); + } + + KeyEvent ke = new KeyEvent(new Object(), millis, + action, modifiers, + (char)key, + keyCode, + isAutoRepeat); + sketch.postEvent(ke); + } + +// if (!isPCodedKey(code, nativeEvent.isPrintableKey()) && !isHackyKey(code)) { +// if (peAction == KeyEvent.PRESS) { +// // Create key typed event +// // TODO: combine dead keys with the following key +// KeyEvent tke = new KeyEvent(nativeEvent, nativeEvent.getWhen(), +// KeyEvent.TYPE, modifiers, +// keyChar, +// 0, +// nativeEvent.isAutoRepeat()); +// +// sketch.postEvent(tke); +// } +// } + } + + + private void nativeMouseEvent(long millis, int action, int modifiers, + int x, int y, int button, int count) { + + + + int peButton = switch (button) { + case GLFW_MOUSE_BUTTON_LEFT -> PConstants.LEFT; + case GLFW_MOUSE_BUTTON_MIDDLE -> PConstants.CENTER; + case GLFW_MOUSE_BUTTON_RIGHT -> PConstants.RIGHT; + default -> 0; + }; + + int peaction = switch (action) { + case GLFW_PRESS -> MouseEvent.PRESS; + case GLFW_RELEASE -> MouseEvent.RELEASE; + case 99 -> MouseEvent.MOVE; + default -> 0; + }; + + int scale = 1; +// if (PApplet.platform == PConstants.MACOS) { +// scale = (int) getCurrentPixelScale(); +// } else { +// scale = (int) getPixelScale(); +// } + int sx = x / scale; + int sy = y / scale; + int mx = sx; + int my = sy; + +// if (pvk.presentMode()) { +// mx -= (int)pvk.presentX; +// my -= (int)pvk.presentY; +// //noinspection IntegerDivisionInFloatingPointContext +// if (peAction == KeyEvent.RELEASE && +// pvk.insideStopButton(sx, sy - screenRect.height / windowScaleFactor)) { +// sketch.exit(); +// } +// if (mx < 0 || sketchWidth < mx || my < 0 || sketchHeight < my) { +// return; +// } +// } + + MouseEvent me = new MouseEvent(new Object(), millis, + peaction, modifiers, + mx, my, + peButton, + count); + + sketch.postEvent(me); + } + + + + + + protected void initListeners() { + } + + private void initWindow() { + if(!glfwInit()) { + throw new RuntimeException("Cannot initialize GLFW"); + } + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + + String title = "Vulkan"; + + glfwwidth = graphics.width; + glfwheight = graphics.height; + + glfwwindow = glfwCreateWindow(glfwwidth, glfwheight, title, NULL, NULL); + + if(glfwwindow == NULL) { + throw new RuntimeException("Cannot create window"); + } + + glfwSetFramebufferSizeCallback(glfwwindow, this::framebufferResizeCallback); + glfwSetCursorPosCallback(glfwwindow, this::cursorMoveCallback); + glfwSetMouseButtonCallback(glfwwindow, this::mouseButtonCallback); + glfwSetKeyCallback(glfwwindow, this::keyActionCallback); + glfwSetInputMode(glfwwindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + + + if (gl2vk == null) { + int nodes = sketch.maxNodes; + if (nodes == -1) { + int availableProcessors = Runtime.getRuntime().availableProcessors(); + nodes = availableProcessors; + } + gl2vk = new GL2VK(this, nodes); + pvk.setGL2VK(gl2vk); + } + windowCreated = true; + } + + private void framebufferResizeCallback(long window, int width, int height) { + glfwframebufferResize = true; + } + + private void cursorMoveCallback(long window, double xpos, double ypos) { + glfwMouseX = (int)xpos; + glfwMouseY = (int)ypos; + glfwAction = 99; + nativeMouseEvent(0, glfwAction, 0, + glfwMouseX, glfwMouseY, glfwButton, 0); + } + + private void mouseButtonCallback(long window, int button, int action, int mod) { + glfwButton = button; + glfwAction = action; + nativeMouseEvent(0, glfwAction, mod, + glfwMouseX, glfwMouseY, glfwButton, 0); + } + + private void keyActionCallback(long window, int key, int scancode, int action, int mods) { +// System.out.println((char)scancode) +// System.out.println(key); + nativeKeyEvent(0L, action, mods, + (char)key, 0, false); + } + + + private void initAnimator() { + if (PApplet.platform == PConstants.WINDOWS) { + // Force Windows to keep timer resolution high by creating a dummy + // thread that sleeps for a time that is not a multiple of 10 ms. + // See section titled "Clocks and Timers on Windows" in this post: + // https://web.archive.org/web/20160308031939/https://blogs.oracle.com/dholmes/entry/inside_the_hotspot_vm_clocks + Thread highResTimerThread = new Thread(() -> { + try { + Thread.sleep(Long.MAX_VALUE); + } catch (InterruptedException ignore) { } + }, "HighResTimerThread"); + highResTimerThread.setDaemon(true); + highResTimerThread.start(); + } + + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice gd = ge.getDefaultScreenDevice(); + DisplayMode dm = gd.getDisplayMode(); + + // Get the refresh rate + int refreshRate = dm.getRefreshRate(); + + if (refreshRate == DisplayMode.REFRESH_RATE_UNKNOWN) { + refreshRate = 60; + } + + animationThread = new AnimatorTask(refreshRate); + + + drawExceptionHandler = new Thread(() -> { + synchronized (drawExceptionMutex) { + try { + while (drawException == null) { + drawExceptionMutex.wait(); + } + Throwable cause = drawException.getCause(); + if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } else if (cause instanceof UnsatisfiedLinkError) { + throw new UnsatisfiedLinkError(cause.getMessage()); + } else if (cause == null) { + throw new RuntimeException(drawException.getMessage()); + } else { + throw new RuntimeException(cause); + } + } catch (InterruptedException ignored) { } + } + }); + drawExceptionHandler.start(); + + } + + @Override + public Object getNative() { + // TODO Auto-generated method stub + System.out.println("WARNING getNative() not implemented."); + return null; + } + + @Override + public void setTitle(String title) { + // TODO Auto-generated method stub + + } + + @Override + public void setVisible(boolean visible) { + // TODO Auto-generated method stub + + } + + @Override + public void setResizable(boolean resizable) { + // TODO Auto-generated method stub + + } + + @Override + public void setAlwaysOnTop(boolean always) { + // TODO Auto-generated method stub + + } + + @Override + public void setIcon(PImage icon) { + // TODO Auto-generated method stub + + } + + @Override + public void placeWindow(int[] location, int[] editorLocation) { + // TODO Auto-generated method stub + + } + + @Override + public void placePresent(int stopColor) { + // TODO Auto-generated method stub + + } + + @Override + public void setLocation(int x, int y) { + // TODO Auto-generated method stub + + } + + @Override + public void setSize(int width, int height) { + // TODO Auto-generated method stub + + } + + @Override + public void setFrameRate(float fps) { +// System.out.println("SET FRAMERATE "+fps); +// animationThread.cancel(); +// animationThread = new AnimatorTask(fps); +// animationThread.start(); + } + + @Override + public void setCursor(int kind) { + // TODO Auto-generated method stub + + } + + @Override + public void setCursor(PImage image, int hotspotX, int hotspotY) { + // TODO Auto-generated method stub + + } + + @Override + public void showCursor() { + // TODO Auto-generated method stub + + } + + @Override + public void hideCursor() { + // TODO Auto-generated method stub + + } + + @Override + public PImage loadImage(String path, Object... args) { + // TODO Auto-generated method stub + return ShimAWT.loadImage(sketch, path, args); + } + + @Override + public boolean openLink(String url) { + // TODO Auto-generated method stub + return ShimAWT.openLink(url); + } + + @Override + public void selectInput(String prompt, String callbackMethod, + File file, Object callbackObject) { + EventQueue.invokeLater(() -> { + // https://github.com/processing/processing/issues/3831 + boolean hide = (sketch != null) && + (PApplet.platform == PConstants.WINDOWS); + if (hide) setVisible(false); + + ShimAWT.selectImpl(prompt, callbackMethod, file, + callbackObject, null, FileDialog.LOAD); + + if (hide) setVisible(true); + }); + } + + @Override + public void selectOutput(String prompt, String callbackMethod, + File file, Object callbackObject) { + EventQueue.invokeLater(() -> { + // https://github.com/processing/processing/issues/3831 + boolean hide = (sketch != null) && + (PApplet.platform == PConstants.WINDOWS); + if (hide) setVisible(false); + + ShimAWT.selectImpl(prompt, callbackMethod, file, + callbackObject, null, FileDialog.SAVE); + + if (hide) setVisible(true); + }); + } + + @Override + public void selectFolder(String prompt, String callbackMethod, + File file, Object callbackObject) { + EventQueue.invokeLater(() -> { + // https://github.com/processing/processing/issues/3831 + boolean hide = (sketch != null) && + (PApplet.platform == PConstants.WINDOWS); + if (hide) setVisible(false); + + ShimAWT.selectFolderImpl(prompt, callbackMethod, file, + callbackObject, null); + + if (hide) setVisible(true); + }); + } + + private AtomicBoolean paused = new AtomicBoolean(false); + + + @Override + public void startThread() { + + // OpenGL compatibility: + // Window gets resized upon initialisation +// pvk.resetFBOLayer(); + + // Our animation thread here. + animationThread.start(); + isStopped = false; + } + + @Override + public void pauseThread() { + paused.set(true); + } + + @Override + public void resumeThread() { + paused.set(false); + } + + @Override + public boolean stopThread() { + if (animationThread != null) animationThread.cancel(); + isStopped = true; + return true; + } + + @Override + public boolean isStopped() { + return isStopped; + } + +} \ No newline at end of file diff --git a/core/src/processing/vulkan/PVK.java b/core/src/processing/vulkan/PVK.java new file mode 100644 index 0000000000..80d9c5e851 --- /dev/null +++ b/core/src/processing/vulkan/PVK.java @@ -0,0 +1,1940 @@ +package processing.vulkan; + +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Toolkit; +import java.nio.Buffer; +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.CharBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.LongBuffer; +import java.nio.ShortBuffer; +import java.util.HashMap; + +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GL2ES2; +import com.jogamp.opengl.GL2ES3; +import com.jogamp.opengl.GL2GL3; +import com.jogamp.opengl.GL3ES3; +import com.jogamp.opengl.GLAutoDrawable; +import com.jogamp.opengl.GLCapabilities; +import com.jogamp.opengl.GLCapabilitiesImmutable; +import com.jogamp.opengl.glu.GLU; +import com.jogamp.opengl.glu.GLUtessellator; +import com.jogamp.opengl.glu.GLUtessellatorCallbackAdapter; + +import processing.GL2VK.GL2VK; +import processing.GL2VK.TextureBuffer; +import processing.GL2VK.Util; +import processing.opengl.PGL; +import processing.opengl.PGraphicsOpenGL; +import processing.opengl.PJOGL; +import processing.opengl.PGL.TessellatorCallback; +public class PVK extends PGL implements PJOGLInterface { + static { + FALSE = GL.GL_FALSE; + TRUE = GL.GL_TRUE; + + INT = GL2ES2.GL_INT; + BYTE = GL.GL_BYTE; + SHORT = GL.GL_SHORT; + FLOAT = GL.GL_FLOAT; + BOOL = GL2ES2.GL_BOOL; + UNSIGNED_INT = GL.GL_UNSIGNED_INT; + UNSIGNED_BYTE = GL.GL_UNSIGNED_BYTE; + UNSIGNED_SHORT = GL.GL_UNSIGNED_SHORT; + + RGB = GL.GL_RGB; + RGBA = GL.GL_RGBA; + ALPHA = GL.GL_ALPHA; + LUMINANCE = GL.GL_LUMINANCE; + LUMINANCE_ALPHA = GL.GL_LUMINANCE_ALPHA; + + UNSIGNED_SHORT_5_6_5 = GL.GL_UNSIGNED_SHORT_5_6_5; + UNSIGNED_SHORT_4_4_4_4 = GL.GL_UNSIGNED_SHORT_4_4_4_4; + UNSIGNED_SHORT_5_5_5_1 = GL.GL_UNSIGNED_SHORT_5_5_5_1; + + RGBA4 = GL.GL_RGBA4; + RGB5_A1 = GL.GL_RGB5_A1; + RGB565 = GL.GL_RGB565; + RGB8 = GL.GL_RGB8; + RGBA8 = GL.GL_RGBA8; + ALPHA8 = GL.GL_ALPHA8; + + READ_ONLY = GL2ES3.GL_READ_ONLY; + WRITE_ONLY = GL.GL_WRITE_ONLY; + READ_WRITE = GL2ES3.GL_READ_WRITE; + + TESS_WINDING_NONZERO = GLU.GLU_TESS_WINDING_NONZERO; + TESS_WINDING_ODD = GLU.GLU_TESS_WINDING_ODD; + TESS_EDGE_FLAG = GLU.GLU_TESS_EDGE_FLAG; + + GENERATE_MIPMAP_HINT = GL.GL_GENERATE_MIPMAP_HINT; + FASTEST = GL.GL_FASTEST; + NICEST = GL.GL_NICEST; + DONT_CARE = GL.GL_DONT_CARE; + + VENDOR = GL.GL_VENDOR; + RENDERER = GL.GL_RENDERER; + VERSION = GL.GL_VERSION; + EXTENSIONS = GL.GL_EXTENSIONS; + SHADING_LANGUAGE_VERSION = GL2ES2.GL_SHADING_LANGUAGE_VERSION; + + MAX_SAMPLES = GL.GL_MAX_SAMPLES; + SAMPLES = GL.GL_SAMPLES; + + ALIASED_LINE_WIDTH_RANGE = GL.GL_ALIASED_LINE_WIDTH_RANGE; + ALIASED_POINT_SIZE_RANGE = GL.GL_ALIASED_POINT_SIZE_RANGE; + + DEPTH_BITS = GL.GL_DEPTH_BITS; + STENCIL_BITS = GL.GL_STENCIL_BITS; + + CCW = GL.GL_CCW; + CW = GL.GL_CW; + + VIEWPORT = GL.GL_VIEWPORT; + + ARRAY_BUFFER = GL.GL_ARRAY_BUFFER; + ELEMENT_ARRAY_BUFFER = GL.GL_ELEMENT_ARRAY_BUFFER; + PIXEL_PACK_BUFFER = GL2ES3.GL_PIXEL_PACK_BUFFER; + + MAX_VERTEX_ATTRIBS = GL2ES2.GL_MAX_VERTEX_ATTRIBS; + + STATIC_DRAW = GL.GL_STATIC_DRAW; + DYNAMIC_DRAW = GL.GL_DYNAMIC_DRAW; + STREAM_DRAW = GL2ES2.GL_STREAM_DRAW; + STREAM_READ = GL2ES3.GL_STREAM_READ; + + BUFFER_SIZE = GL.GL_BUFFER_SIZE; + BUFFER_USAGE = GL.GL_BUFFER_USAGE; + + POINTS = GL.GL_POINTS; + LINE_STRIP = GL.GL_LINE_STRIP; + LINE_LOOP = GL.GL_LINE_LOOP; + LINES = GL.GL_LINES; + TRIANGLE_FAN = GL.GL_TRIANGLE_FAN; + TRIANGLE_STRIP = GL.GL_TRIANGLE_STRIP; + TRIANGLES = GL.GL_TRIANGLES; + + CULL_FACE = GL.GL_CULL_FACE; + FRONT = GL.GL_FRONT; + BACK = GL.GL_BACK; + FRONT_AND_BACK = GL.GL_FRONT_AND_BACK; + + POLYGON_OFFSET_FILL = GL.GL_POLYGON_OFFSET_FILL; + + UNPACK_ALIGNMENT = GL.GL_UNPACK_ALIGNMENT; + PACK_ALIGNMENT = GL.GL_PACK_ALIGNMENT; + + TEXTURE_2D = GL.GL_TEXTURE_2D; + TEXTURE_RECTANGLE = GL2GL3.GL_TEXTURE_RECTANGLE; + + TEXTURE_BINDING_2D = GL.GL_TEXTURE_BINDING_2D; + TEXTURE_BINDING_RECTANGLE = GL2GL3.GL_TEXTURE_BINDING_RECTANGLE; + + MAX_TEXTURE_SIZE = GL.GL_MAX_TEXTURE_SIZE; + TEXTURE_MAX_ANISOTROPY = GL.GL_TEXTURE_MAX_ANISOTROPY_EXT; + MAX_TEXTURE_MAX_ANISOTROPY = GL.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT; + + MAX_VERTEX_TEXTURE_IMAGE_UNITS = GL2ES2.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS; + MAX_TEXTURE_IMAGE_UNITS = GL2ES2.GL_MAX_TEXTURE_IMAGE_UNITS; + MAX_COMBINED_TEXTURE_IMAGE_UNITS = GL2ES2.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS; + + NUM_COMPRESSED_TEXTURE_FORMATS = GL.GL_NUM_COMPRESSED_TEXTURE_FORMATS; + COMPRESSED_TEXTURE_FORMATS = GL.GL_COMPRESSED_TEXTURE_FORMATS; + + NEAREST = GL.GL_NEAREST; + LINEAR = GL.GL_LINEAR; + LINEAR_MIPMAP_NEAREST = GL.GL_LINEAR_MIPMAP_NEAREST; + LINEAR_MIPMAP_LINEAR = GL.GL_LINEAR_MIPMAP_LINEAR; + + CLAMP_TO_EDGE = GL.GL_CLAMP_TO_EDGE; + REPEAT = GL.GL_REPEAT; + + TEXTURE0 = GL.GL_TEXTURE0; + TEXTURE1 = GL.GL_TEXTURE1; + TEXTURE2 = GL.GL_TEXTURE2; + TEXTURE3 = GL.GL_TEXTURE3; + TEXTURE_MIN_FILTER = GL.GL_TEXTURE_MIN_FILTER; + TEXTURE_MAG_FILTER = GL.GL_TEXTURE_MAG_FILTER; + TEXTURE_WRAP_S = GL.GL_TEXTURE_WRAP_S; + TEXTURE_WRAP_T = GL.GL_TEXTURE_WRAP_T; + TEXTURE_WRAP_R = GL2ES2.GL_TEXTURE_WRAP_R; + + TEXTURE_CUBE_MAP = GL.GL_TEXTURE_CUBE_MAP; + TEXTURE_CUBE_MAP_POSITIVE_X = GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X; + TEXTURE_CUBE_MAP_POSITIVE_Y = GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Y; + TEXTURE_CUBE_MAP_POSITIVE_Z = GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Z; + TEXTURE_CUBE_MAP_NEGATIVE_X = GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_X; + TEXTURE_CUBE_MAP_NEGATIVE_Y = GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; + TEXTURE_CUBE_MAP_NEGATIVE_Z = GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; + + VERTEX_SHADER = GL2ES2.GL_VERTEX_SHADER; + FRAGMENT_SHADER = GL2ES2.GL_FRAGMENT_SHADER; + INFO_LOG_LENGTH = GL2ES2.GL_INFO_LOG_LENGTH; + SHADER_SOURCE_LENGTH = GL2ES2.GL_SHADER_SOURCE_LENGTH; + COMPILE_STATUS = GL2ES2.GL_COMPILE_STATUS; + LINK_STATUS = GL2ES2.GL_LINK_STATUS; + VALIDATE_STATUS = GL2ES2.GL_VALIDATE_STATUS; + SHADER_TYPE = GL2ES2.GL_SHADER_TYPE; + DELETE_STATUS = GL2ES2.GL_DELETE_STATUS; + + FLOAT_VEC2 = GL2ES2.GL_FLOAT_VEC2; + FLOAT_VEC3 = GL2ES2.GL_FLOAT_VEC3; + FLOAT_VEC4 = GL2ES2.GL_FLOAT_VEC4; + FLOAT_MAT2 = GL2ES2.GL_FLOAT_MAT2; + FLOAT_MAT3 = GL2ES2.GL_FLOAT_MAT3; + FLOAT_MAT4 = GL2ES2.GL_FLOAT_MAT4; + INT_VEC2 = GL2ES2.GL_INT_VEC2; + INT_VEC3 = GL2ES2.GL_INT_VEC3; + INT_VEC4 = GL2ES2.GL_INT_VEC4; + BOOL_VEC2 = GL2ES2.GL_BOOL_VEC2; + BOOL_VEC3 = GL2ES2.GL_BOOL_VEC3; + BOOL_VEC4 = GL2ES2.GL_BOOL_VEC4; + SAMPLER_2D = GL2ES2.GL_SAMPLER_2D; + SAMPLER_CUBE = GL2ES2.GL_SAMPLER_CUBE; + + LOW_FLOAT = GL2ES2.GL_LOW_FLOAT; + MEDIUM_FLOAT = GL2ES2.GL_MEDIUM_FLOAT; + HIGH_FLOAT = GL2ES2.GL_HIGH_FLOAT; + LOW_INT = GL2ES2.GL_LOW_INT; + MEDIUM_INT = GL2ES2.GL_MEDIUM_INT; + HIGH_INT = GL2ES2.GL_HIGH_INT; + + CURRENT_VERTEX_ATTRIB = GL2ES2.GL_CURRENT_VERTEX_ATTRIB; + + VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = GL2ES2.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING; + VERTEX_ATTRIB_ARRAY_ENABLED = GL2ES2.GL_VERTEX_ATTRIB_ARRAY_ENABLED; + VERTEX_ATTRIB_ARRAY_SIZE = GL2ES2.GL_VERTEX_ATTRIB_ARRAY_SIZE; + VERTEX_ATTRIB_ARRAY_STRIDE = GL2ES2.GL_VERTEX_ATTRIB_ARRAY_STRIDE; + VERTEX_ATTRIB_ARRAY_TYPE = GL2ES2.GL_VERTEX_ATTRIB_ARRAY_TYPE; + VERTEX_ATTRIB_ARRAY_NORMALIZED = GL2ES2.GL_VERTEX_ATTRIB_ARRAY_NORMALIZED; + VERTEX_ATTRIB_ARRAY_POINTER = GL2ES2.GL_VERTEX_ATTRIB_ARRAY_POINTER; + + BLEND = GL.GL_BLEND; + ONE = GL.GL_ONE; + ZERO = GL.GL_ZERO; + SRC_ALPHA = GL.GL_SRC_ALPHA; + DST_ALPHA = GL.GL_DST_ALPHA; + ONE_MINUS_SRC_ALPHA = GL.GL_ONE_MINUS_SRC_ALPHA; + ONE_MINUS_DST_COLOR = GL.GL_ONE_MINUS_DST_COLOR; + ONE_MINUS_SRC_COLOR = GL.GL_ONE_MINUS_SRC_COLOR; + DST_COLOR = GL.GL_DST_COLOR; + SRC_COLOR = GL.GL_SRC_COLOR; + + SAMPLE_ALPHA_TO_COVERAGE = GL.GL_SAMPLE_ALPHA_TO_COVERAGE; + SAMPLE_COVERAGE = GL.GL_SAMPLE_COVERAGE; + + KEEP = GL.GL_KEEP; + REPLACE = GL.GL_REPLACE; + INCR = GL.GL_INCR; + DECR = GL.GL_DECR; + INVERT = GL.GL_INVERT; + INCR_WRAP = GL.GL_INCR_WRAP; + DECR_WRAP = GL.GL_DECR_WRAP; + NEVER = GL.GL_NEVER; + ALWAYS = GL.GL_ALWAYS; + + EQUAL = GL.GL_EQUAL; + LESS = GL.GL_LESS; + LEQUAL = GL.GL_LEQUAL; + GREATER = GL.GL_GREATER; + GEQUAL = GL.GL_GEQUAL; + NOTEQUAL = GL.GL_NOTEQUAL; + + FUNC_ADD = GL.GL_FUNC_ADD; + FUNC_MIN = GL2ES3.GL_MIN; + FUNC_MAX = GL2ES3.GL_MAX; + FUNC_REVERSE_SUBTRACT = GL.GL_FUNC_REVERSE_SUBTRACT; + FUNC_SUBTRACT = GL.GL_FUNC_SUBTRACT; + + DITHER = GL.GL_DITHER; + + CONSTANT_COLOR = GL2ES2.GL_CONSTANT_COLOR; + CONSTANT_ALPHA = GL2ES2.GL_CONSTANT_ALPHA; + ONE_MINUS_CONSTANT_COLOR = GL2ES2.GL_ONE_MINUS_CONSTANT_COLOR; + ONE_MINUS_CONSTANT_ALPHA = GL2ES2.GL_ONE_MINUS_CONSTANT_ALPHA; + SRC_ALPHA_SATURATE = GL.GL_SRC_ALPHA_SATURATE; + + SCISSOR_TEST = GL.GL_SCISSOR_TEST; + STENCIL_TEST = GL.GL_STENCIL_TEST; + DEPTH_TEST = GL.GL_DEPTH_TEST; + DEPTH_WRITEMASK = GL.GL_DEPTH_WRITEMASK; + + COLOR_BUFFER_BIT = GL.GL_COLOR_BUFFER_BIT; + DEPTH_BUFFER_BIT = GL.GL_DEPTH_BUFFER_BIT; + STENCIL_BUFFER_BIT = GL.GL_STENCIL_BUFFER_BIT; + + FRAMEBUFFER = GL.GL_FRAMEBUFFER; + COLOR_ATTACHMENT0 = GL.GL_COLOR_ATTACHMENT0; + COLOR_ATTACHMENT1 = GL2ES2.GL_COLOR_ATTACHMENT1; + COLOR_ATTACHMENT2 = GL2ES2.GL_COLOR_ATTACHMENT2; + COLOR_ATTACHMENT3 = GL2ES2.GL_COLOR_ATTACHMENT3; + RENDERBUFFER = GL.GL_RENDERBUFFER; + DEPTH_ATTACHMENT = GL.GL_DEPTH_ATTACHMENT; + STENCIL_ATTACHMENT = GL.GL_STENCIL_ATTACHMENT; + READ_FRAMEBUFFER = GL.GL_READ_FRAMEBUFFER; + DRAW_FRAMEBUFFER = GL.GL_DRAW_FRAMEBUFFER; + + DEPTH24_STENCIL8 = GL.GL_DEPTH24_STENCIL8; + + DEPTH_COMPONENT = GL2ES2.GL_DEPTH_COMPONENT; + DEPTH_COMPONENT16 = GL.GL_DEPTH_COMPONENT16; + DEPTH_COMPONENT24 = GL.GL_DEPTH_COMPONENT24; + DEPTH_COMPONENT32 = GL.GL_DEPTH_COMPONENT32; + + STENCIL_INDEX = GL2ES2.GL_STENCIL_INDEX; + STENCIL_INDEX1 = GL.GL_STENCIL_INDEX1; + STENCIL_INDEX4 = GL.GL_STENCIL_INDEX4; + STENCIL_INDEX8 = GL.GL_STENCIL_INDEX8; + + DEPTH_STENCIL = GL.GL_DEPTH_STENCIL; + + FRAMEBUFFER_COMPLETE = GL.GL_FRAMEBUFFER_COMPLETE; + FRAMEBUFFER_UNDEFINED = GL2ES3.GL_FRAMEBUFFER_UNDEFINED; + FRAMEBUFFER_INCOMPLETE_ATTACHMENT = GL.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = GL.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + FRAMEBUFFER_INCOMPLETE_DIMENSIONS = GL.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; + FRAMEBUFFER_INCOMPLETE_FORMATS = GL.GL_FRAMEBUFFER_INCOMPLETE_FORMATS; + FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER = GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER; + FRAMEBUFFER_INCOMPLETE_READ_BUFFER = GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER; + FRAMEBUFFER_UNSUPPORTED = GL.GL_FRAMEBUFFER_UNSUPPORTED; + FRAMEBUFFER_INCOMPLETE_MULTISAMPLE = GL.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; + FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS = GL3ES3.GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS; + + FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = GL.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE; + FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = GL.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME; + FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL = GL.GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL; + FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = GL.GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE; + + RENDERBUFFER_WIDTH = GL.GL_RENDERBUFFER_WIDTH; + RENDERBUFFER_HEIGHT = GL.GL_RENDERBUFFER_HEIGHT; + RENDERBUFFER_RED_SIZE = GL.GL_RENDERBUFFER_RED_SIZE; + RENDERBUFFER_GREEN_SIZE = GL.GL_RENDERBUFFER_GREEN_SIZE; + RENDERBUFFER_BLUE_SIZE = GL.GL_RENDERBUFFER_BLUE_SIZE; + RENDERBUFFER_ALPHA_SIZE = GL.GL_RENDERBUFFER_ALPHA_SIZE; + RENDERBUFFER_DEPTH_SIZE = GL.GL_RENDERBUFFER_DEPTH_SIZE; + RENDERBUFFER_STENCIL_SIZE = GL.GL_RENDERBUFFER_STENCIL_SIZE; + RENDERBUFFER_INTERNAL_FORMAT = GL.GL_RENDERBUFFER_INTERNAL_FORMAT; + + MULTISAMPLE = GL.GL_MULTISAMPLE; + LINE_SMOOTH = GL.GL_LINE_SMOOTH; + POLYGON_SMOOTH = GL2GL3.GL_POLYGON_SMOOTH; + + SYNC_GPU_COMMANDS_COMPLETE = GL3ES3.GL_SYNC_GPU_COMMANDS_COMPLETE; + ALREADY_SIGNALED = GL3ES3.GL_ALREADY_SIGNALED; + CONDITION_SATISFIED = GL3ES3.GL_CONDITION_SATISFIED; + } + + private HashMap glEnumToStrings = new HashMap<>(); + + private GL2VK gl2vk; + + + /** GLU interface **/ + // For backward compatibility idk + // I hope this doesn't require openGL + public GLU glu; + + public PVK(PGraphicsOpenGL pg, GL2VK gl2vk) { + this.gl2vk = gl2vk; + this.graphics = pg; + glu = new GLU(); + } + + public PVK(PGraphicsOpenGL pg) { + this.graphics = pg; + glu = new GLU(); + } + + { + prepareStrings(); + if (glColorTex == null) { + glColorFbo = allocateIntBuffer(1); + glColorTex = allocateIntBuffer(2); + glDepthStencil = allocateIntBuffer(1); + glDepth = allocateIntBuffer(1); + glStencil = allocateIntBuffer(1); + + glMultiFbo = allocateIntBuffer(1); + glMultiColor = allocateIntBuffer(1); + glMultiDepthStencil = allocateIntBuffer(1); + glMultiDepth = allocateIntBuffer(1); + glMultiStencil = allocateIntBuffer(1); + } + + byteBuffer = allocateByteBuffer(1); + intBuffer = allocateIntBuffer(1); + viewBuffer = allocateIntBuffer(4); + } + + public void setGL2VK(GL2VK gl2vk) { + this.gl2vk = gl2vk; + } + + public void cleanup() { + gl2vk.close(); + } + + public boolean shouldClose() { + return gl2vk.shouldClose(); + } + + public void beginRecord() { + report("beginRecord"); + gl2vk.beginRecord(); + } + + public void endRecord() { + report("endRecord"); + gl2vk.endRecord(); + } + + public void selectNode(int node) { + gl2vk.disableAutoMode(); + gl2vk.selectNode(node); + } + + public void enableAutoMode() { + gl2vk.enableAutoMode(); + } + + public int getNodesCount() { + return gl2vk.getNodesCount(); + } + + public void setMaxNodes(int v) { + gl2vk.setMaxNodes(v); + } + + + public void bufferMultithreaded(boolean onoff) { + gl2vk.bufferMultithreaded(onoff); + } + + private void report(String func) { +// System.out.println(func); + } + + + private void prepareStrings() { + // OpenGL strings only really have this + // information being loaded into the + // hashmap below. + + // TODO: Query vulkan + glEnumToStrings.put(VENDOR, "Intel"); + glEnumToStrings.put(RENDERER, "Intel(R) UHD Graphics 620"); + + // For obvious reasons, this needs to be spoofed as some version of OpenGL. + // Let's just use the version on my Surface Book 2. + glEnumToStrings.put(VERSION, "4.6.0 - Build 30.0.101.1339"); + + // TODO: + // Convert Vulkan extensions to comparibly equivalent OpenGL + // extensions and hope for the best this doesn't break anything. + glEnumToStrings.put(EXTENSIONS, "GL_3DFX_texture_compression_FXT1 GL_AMD_depth_clamp_separate GL_AMD_vertex_shader_layer GL_AMD_vertex_shader_viewport_index GL_ARB_ES2_compatibility GL_ARB_ES3_1_compatibility GL_ARB_ES3_compatibility GL_ARB_arrays_of_arrays GL_ARB_base_instance GL_ARB_bindless_texture GL_ARB_blend_func_extended GL_ARB_buffer_storage GL_ARB_cl_event GL_ARB_clear_buffer_object GL_ARB_clear_texture GL_ARB_clip_control GL_ARB_color_buffer_float GL_ARB_compressed_texture_pixel_storage GL_ARB_compute_shader GL_ARB_conditional_render_inverted GL_ARB_conservative_depth GL_ARB_copy_buffer GL_ARB_copy_image GL_ARB_cull_distance GL_ARB_debug_output GL_ARB_depth_buffer_float GL_ARB_depth_clamp GL_ARB_depth_texture GL_ARB_derivative_control GL_ARB_direct_state_access GL_ARB_draw_buffers GL_ARB_draw_buffers_blend GL_ARB_draw_elements_base_vertex GL_ARB_draw_indirect GL_ARB_draw_instanced GL_ARB_enhanced_layouts GL_ARB_explicit_attrib_location GL_ARB_explicit_uniform_location GL_ARB_fragment_coord_conventions GL_ARB_fragment_layer_viewport GL_ARB_fragment_program GL_ARB_fragment_program_shadow GL_ARB_fragment_shader GL_ARB_fragment_shader_interlock GL_ARB_framebuffer_no_attachments GL_ARB_framebuffer_object GL_ARB_framebuffer_sRGB GL_ARB_geometry_shader4 GL_ARB_get_program_binary GL_ARB_get_texture_sub_image GL_ARB_gl_spirv GL_ARB_gpu_shader5 GL_ARB_gpu_shader_fp64 GL_ARB_half_float_pixel GL_ARB_half_float_vertex GL_ARB_indirect_parameters GL_ARB_instanced_arrays GL_ARB_internalformat_query GL_ARB_internalformat_query2 GL_ARB_invalidate_subdata GL_ARB_map_buffer_alignment GL_ARB_map_buffer_range GL_ARB_multi_bind GL_ARB_multi_draw_indirect GL_ARB_multisample GL_ARB_multitexture GL_ARB_occlusion_query GL_ARB_occlusion_query2 GL_ARB_pipeline_statistics_query GL_ARB_pixel_buffer_object GL_ARB_point_parameters GL_ARB_point_sprite GL_ARB_polygon_offset_clamp GL_ARB_post_depth_coverage GL_ARB_program_interface_query GL_ARB_provoking_vertex GL_ARB_query_buffer_object GL_ARB_robust_buffer_access_behavior GL_ARB_robustness GL_ARB_robustness_isolation GL_ARB_sample_shading GL_ARB_sampler_objects GL_ARB_seamless_cube_map GL_ARB_seamless_cubemap_per_texture GL_ARB_separate_shader_objects GL_ARB_shader_atomic_counter_ops GL_ARB_shader_atomic_counters GL_ARB_shader_bit_encoding GL_ARB_shader_draw_parameters GL_ARB_shader_group_vote GL_ARB_shader_image_load_store GL_ARB_shader_image_size GL_ARB_shader_objects GL_ARB_shader_precision GL_ARB_shader_stencil_export GL_ARB_shader_storage_buffer_object GL_ARB_shader_subroutine GL_ARB_shader_texture_image_samples GL_ARB_shading_language_100 GL_ARB_shading_language_420pack GL_ARB_shading_language_packing GL_ARB_shadow GL_ARB_spirv_extensions GL_ARB_stencil_texturing GL_ARB_sync GL_ARB_tessellation_shader GL_ARB_texture_barrier GL_ARB_texture_border_clamp GL_ARB_texture_buffer_object GL_ARB_texture_buffer_object_rgb32 GL_ARB_texture_buffer_range GL_ARB_texture_compression GL_ARB_texture_compression_bptc GL_ARB_texture_compression_rgtc GL_ARB_texture_cube_map GL_ARB_texture_cube_map_array GL_ARB_texture_env_add GL_ARB_texture_env_combine GL_ARB_texture_env_crossbar GL_ARB_texture_env_dot3 GL_ARB_texture_filter_anisotropic GL_ARB_texture_float GL_ARB_texture_gather GL_ARB_texture_mirror_clamp_to_edge GL_ARB_texture_mirrored_repeat GL_ARB_texture_multisample GL_ARB_texture_non_power_of_two GL_ARB_texture_query_levels GL_ARB_texture_query_lod GL_ARB_texture_rectangle GL_ARB_texture_rg GL_ARB_texture_rgb10_a2ui GL_ARB_texture_stencil8 GL_ARB_texture_storage GL_ARB_texture_storage_multisample GL_ARB_texture_swizzle GL_ARB_texture_view GL_ARB_timer_query GL_ARB_transform_feedback2 GL_ARB_transform_feedback3 GL_ARB_transform_feedback_instanced GL_ARB_transform_feedback_overflow_query GL_ARB_transpose_matrix GL_ARB_uniform_buffer_object GL_ARB_vertex_array_bgra GL_ARB_vertex_array_object GL_ARB_vertex_attrib_64bit GL_ARB_vertex_attrib_binding GL_ARB_vertex_buffer_object GL_ARB_vertex_program GL_ARB_vertex_shader GL_ARB_vertex_type_10f_11f_11f_rev GL_ARB_vertex_type_2_10_10_10_rev GL_ARB_viewport_array GL_ARB_window_pos GL_ATI_separate_stencil GL_EXT_abgr GL_EXT_bgra GL_EXT_blend_color GL_EXT_blend_equation_separate GL_EXT_blend_func_separate GL_EXT_blend_minmax GL_EXT_blend_subtract GL_EXT_clip_volume_hint GL_EXT_compiled_vertex_array GL_EXT_direct_state_access GL_EXT_draw_buffers2 GL_EXT_draw_range_elements GL_EXT_fog_coord GL_EXT_framebuffer_blit GL_EXT_framebuffer_multisample GL_EXT_framebuffer_object GL_EXT_geometry_shader4 GL_EXT_gpu_program_parameters GL_EXT_gpu_shader4 GL_EXT_memory_object GL_EXT_memory_object_win32 GL_EXT_multi_draw_arrays GL_EXT_packed_depth_stencil GL_EXT_packed_float GL_EXT_packed_pixels GL_EXT_polygon_offset_clamp GL_EXT_rescale_normal GL_EXT_secondary_color GL_EXT_semaphore GL_EXT_semaphore_win32 GL_EXT_separate_specular_color GL_EXT_shader_framebuffer_fetch GL_EXT_shader_integer_mix GL_EXT_shadow_funcs GL_EXT_stencil_two_side GL_EXT_stencil_wrap GL_EXT_texture3D GL_EXT_texture_array GL_EXT_texture_compression_s3tc GL_EXT_texture_edge_clamp GL_EXT_texture_env_add GL_EXT_texture_env_combine GL_EXT_texture_filter_anisotropic GL_EXT_texture_integer GL_EXT_texture_lod_bias GL_EXT_texture_rectangle GL_EXT_texture_sRGB GL_EXT_texture_sRGB_decode GL_EXT_texture_shared_exponent GL_EXT_texture_snorm GL_EXT_texture_storage GL_EXT_texture_swizzle GL_EXT_timer_query GL_EXT_transform_feedback GL_IBM_texture_mirrored_repeat GL_INTEL_conservative_rasterization GL_INTEL_fragment_shader_ordering GL_INTEL_framebuffer_CMAA GL_INTEL_map_texture GL_INTEL_multi_rate_fragment_shader GL_INTEL_performance_query GL_KHR_blend_equation_advanced GL_KHR_blend_equation_advanced_coherent GL_KHR_context_flush_control GL_KHR_debug GL_KHR_no_error GL_KHR_shader_subgroup GL_KHR_shader_subgroup_arithmetic GL_KHR_shader_subgroup_ballot GL_KHR_shader_subgroup_basic GL_KHR_shader_subgroup_clustered GL_KHR_shader_subgroup_quad GL_KHR_shader_subgroup_shuffle GL_KHR_shader_subgroup_shuffle_relative GL_KHR_shader_subgroup_vote GL_KHR_texture_compression_astc_hdr GL_KHR_texture_compression_astc_ldr GL_NV_blend_square GL_NV_conditional_render GL_NV_primitive_restart GL_NV_texgen_reflection GL_SGIS_generate_mipmap GL_SGIS_texture_edge_clamp GL_SGIS_texture_lod GL_SUN_multi_draw_arrays GL_WIN_swap_hint WGL_EXT_swap_control"); + + // This needs to be spoofed too. + glEnumToStrings.put(SHADING_LANGUAGE_VERSION, "4.60 - Build 30.0.101.1339"); + } + + @Override + public Object getNative() { + // TODO Auto-generated method stub + return null; + } + + public void setCaps(GLCapabilities caps) { + // TODO Auto-generated method stub + + } + + public GLCapabilitiesImmutable getCaps() { + // TODO Auto-generated method stub + return null; + } + + public boolean needSharedObjectSync() { + // TODO Auto-generated method stub + return false; + } + + public void setFps(float fps) { + // TODO Auto-generated method stub + + } + + public void getGL(GLAutoDrawable glDrawable) { +// setThread(Thread.currentThread()); + } + + @Override + public boolean threadIsCurrent() { + // idc + return true; + } + + @Override + protected int[] getGLVersion() { + int[] res = {4, 6, 0}; + return res; + } + + public void init(GLAutoDrawable glDrawable) { + // TODO Auto-generated method stub + + } + + public void showme(String method, int value) { + // TODO Auto-generated method stub + + } + + @Override + public void flush() { + // TODO Auto-generated method stub + + } + + @Override + public void finish() { + // TODO Auto-generated method stub + + } + + @Override + public void hint(int target, int hint) { + // TODO Auto-generated method stub + + } + + @Override + public void enable(int value) { + int gl2vkValue = -1; + if (value == DEPTH_TEST) { + gl2vkValue = GL2VK.DEPTH_TEST; + } + else if (value == BLEND) { + gl2vkValue = GL2VK.BLEND; + } + else if (value == MULTISAMPLE) { + gl2vkValue = GL2VK.MULTISAMPLE; + } + else if (value == POLYGON_SMOOTH) { + gl2vkValue = GL2VK.POLYGON_SMOOTH; + } + else if (value == CULL_FACE) { + gl2vkValue = GL2VK.CULL_FACE; + } + + gl2vk.glEnable(gl2vkValue); + } + + @Override + public void disable(int value) { + if (value == DEPTH_TEST) { + + } + else if (value == BLEND) { + + } + else if (value == MULTISAMPLE) { + + } + else if (value == POLYGON_SMOOTH) { + + } + else if (value == CULL_FACE) { + + } +// gl2vk.glDisable(value); + } + + @Override + public void getBooleanv(int value, IntBuffer data) { + // TODO Auto-generated method stub + System.out.println("getBooleanv UNKNOWN "+value); + } + + @Override + // TODO: actual values instead of placeholder values. + // Have fun with that. + public void getIntegerv(int value, IntBuffer data) { +// MAX_TEXTURE_SIZE 16384 +// MAX_SAMPLES 16 +// MAX_TEXTURE_MAX_ANISOTROPY 16.0 + + if (value == MAX_TEXTURE_SIZE) { + data.put(0, 16384); + } + else if (value == MAX_SAMPLES) { + data.put(0, 16); + } + else if (value == MAX_TEXTURE_IMAGE_UNITS) { + data.put(0, 24); + } + else { + System.out.println("getIntegerv UNKNOWN "+value); + } + } + + @Override + public void getFloatv(int value, FloatBuffer data) { +// MAX_TEXTURE_SIZE 16384 +// MAX_SAMPLES 16 +// MAX_TEXTURE_MAX_ANISOTROPY 16.0 + + if (value == MAX_TEXTURE_MAX_ANISOTROPY) { + data.put(0, 16f); + } + else { + System.out.println("getFloatv UNKNOWN "+value); + } + } + + @Override + public boolean isEnabled(int value) { + // TODO Auto-generated method stub + return false; + } + + @SuppressWarnings("deprecation") + private FontMetrics getFontMetrics(Font font) { // ignore + report("getFontMetrics"); + return Toolkit.getDefaultToolkit().getFontMetrics(font); + } + + + @Override + protected int getTextWidth(Object font, char[] buffer, int start, int stop) { + report("getTextWidth"); + // maybe should use one of the newer/fancier functions for this? + int length = stop - start; + FontMetrics metrics = getFontMetrics((Font) font); + return metrics.charsWidth(buffer, start, length); + } + + + + @Override + public int getError() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public String errorString(int err) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void genBuffers(int n, IntBuffer buffers) { + report("genBuffers"); + gl2vk.glGenBuffers(n, buffers); + } + + @Override + public void deleteBuffers(int n, IntBuffer buffers) { + // TODO: Deletebuffers + } + + @Override + public void bindBuffer(int target, int buffer) { + report("bindBuffer"); + gl2vk.glBindBuffer(target, buffer); + } + + + public static ByteBuffer convertToByteBuffer(Buffer outputBuffer) { + if (outputBuffer == null) return null; + + ByteBuffer byteBuffer = null; + if (outputBuffer instanceof ByteBuffer) { + System.out.println("ByteBuffer"); + byteBuffer = (ByteBuffer) outputBuffer; + } else if (outputBuffer instanceof CharBuffer) { + System.out.println("CharBuffer"); + byteBuffer = ByteBuffer.allocate(outputBuffer.capacity()); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + byteBuffer.asCharBuffer().put((CharBuffer) outputBuffer); + } else if (outputBuffer instanceof ShortBuffer) { + System.out.println("ShortBuffer"); + byteBuffer = ByteBuffer.allocate(outputBuffer.capacity() * 2); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + byteBuffer.asShortBuffer().put((ShortBuffer) outputBuffer); + } else if (outputBuffer instanceof IntBuffer) { + System.out.println("IntBuffer"); + byteBuffer = ByteBuffer.allocate(outputBuffer.capacity() * 4); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + byteBuffer.asIntBuffer().put((IntBuffer) outputBuffer); + } else if (outputBuffer instanceof LongBuffer) { + System.out.println("LongBuffer"); + byteBuffer = ByteBuffer.allocate(outputBuffer.capacity() * 8); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + byteBuffer.asLongBuffer().put((LongBuffer) outputBuffer); + } else if (outputBuffer instanceof FloatBuffer) { + System.out.println("FloatBuffer"); + byteBuffer = ByteBuffer.allocate(outputBuffer.capacity() * 4); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + byteBuffer.asFloatBuffer().put((FloatBuffer) outputBuffer); + } else if (outputBuffer instanceof DoubleBuffer) { + System.out.println("DoubleBuffer"); + byteBuffer = ByteBuffer.allocate(outputBuffer.capacity() * 8); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + byteBuffer.asDoubleBuffer().put((DoubleBuffer) outputBuffer); + } + else { + System.err.println("Unknown buffer "+outputBuffer.getClass().toString()); + } + return byteBuffer; + } + + + + + @Override + public void bufferData(int target, int size, Buffer data, int usage) { + int gl2vktarget = -1; + if (target == ARRAY_BUFFER) { + gl2vktarget = GL2VK.GL_VERTEX_BUFFER; + } + else if (target == ELEMENT_ARRAY_BUFFER) { + gl2vktarget = GL2VK.GL_INDEX_BUFFER; + } + else { + System.err.println("bufferData: Dunno what to do with target "+target); + } + + int gl2vkusage = -1; + if (usage == STREAM_DRAW) { + gl2vkusage = GL2VK.STREAM_DRAW; + } else if (usage == STREAM_READ) { + gl2vkusage = GL2VK.STREAM_READ; + } else if (usage == STATIC_DRAW) { + gl2vkusage = GL2VK.STATIC_DRAW; + } else if (usage == DYNAMIC_DRAW) { + gl2vkusage = GL2VK.DYNAMIC_DRAW; + } + else { + System.err.println("bufferData: Unknown usage "+usage); + } + + + report("bufferData"); + +// ByteBuffer databyte = convertToByteBuffer(data); + + +// System.out.println(); +// if (databyte != null) { +//// newData = ByteBuffer.allocateDirect(data.capacity()); +//// newData.order(ByteOrder.LITTLE_ENDIAN); +// databyte.rewind(); +// +// int max = databyte.capacity(); +// if (32 < max) max = 64; +// +// for (int i = 0; i < max; i+=4) { +// float f = databyte.getFloat(); +//// if (f > 1.0f) f /= 512f; +// System.out.print(f+" "); +//// newData.putFloat(f); +// } +// databyte.rewind(); +// System.out.println(); +// +// for (int i = 0; i < max; i+=4) { +// short x = databyte.getShort(); +//// if (f > 1.0f) f /= 512f; +// System.out.print(x+" "); +//// newData.putFloat(f); +// } +// databyte.rewind(); +//// newData.rewind(); +// System.out.println(); + + if (data instanceof ByteBuffer) { + gl2vk.glBufferData(gl2vktarget, size, (ByteBuffer)data, gl2vkusage); + } else if (data instanceof CharBuffer) { + gl2vk.glBufferData(gl2vktarget, size, (ByteBuffer)data, gl2vkusage); + } else if (data instanceof ShortBuffer) { + gl2vk.glBufferData(gl2vktarget, size, (ShortBuffer)data, gl2vkusage); + } else if (data instanceof IntBuffer) { + gl2vk.glBufferData(gl2vktarget, size, (IntBuffer)data, gl2vkusage); + } else if (data instanceof LongBuffer) { + System.err.println("bufferData: LongBuffer not support"); + } else if (data instanceof FloatBuffer) { + gl2vk.glBufferData(gl2vktarget, size, (FloatBuffer)data, gl2vkusage); + } else if (data instanceof DoubleBuffer) { + System.err.println("bufferData: DoubleBuffer not support"); + } else if (data == null) { + // Just create the buffer + gl2vk.glBufferData(gl2vktarget, size, gl2vkusage); + } + else { + System.err.println("bufferData: Unknown buffer type "+data.getClass().getName()); + } + } + + @Override + public void bufferSubData(int target, int offset, int size, Buffer data) { +// TODO + } + + @Override + public void isBuffer(int buffer) { + // TODO + + } + + // Because we don't have our GL anymore, we need to return an emulated list of + // OpenGL's int->names. The emulated list is right here in this class. + @Override + public String getString(int name) { + return glEnumToStrings.get(name); + } + + @Override + protected boolean isES() { + return false; + } + + @Override + public void getBufferParameteriv(int target, int value, IntBuffer data) { + // TODO Auto-generated method stub + + } + + @Override + public ByteBuffer mapBuffer(int target, int access) { + return gl2vk.glMapBuffer(target, access); + } + + @Override + public ByteBuffer mapBufferRange(int target, int offset, int length, + int access) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void unmapBuffer(int target) { + gl2vk.glUnmapBuffer(target); + + } + + @Override + public long fenceSync(int condition, int flags) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void deleteSync(long sync) { + // TODO Auto-generated method stub + + } + + @Override + public int clientWaitSync(long sync, int flags, long timeout) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void depthRangef(float n, float f) { + // TODO Auto-generated method stub + + } + + @Override + public void viewport(int x, int y, int w, int h) { + // TODO Auto-generated method stub + + } + + @Override + public void vertexAttrib1f(int index, float value) { + // TODO Auto-generated method stub + + } + + @Override + public void vertexAttrib2f(int index, float value0, float value1) { + // TODO Auto-generated method stub + + } + + @Override + public void vertexAttrib3f(int index, float value0, float value1, + float value2) { + // TODO Auto-generated method stub + + } + + @Override + public void vertexAttrib4f(int index, float value0, float value1, + float value2, float value3) { + // TODO Auto-generated method stub + + } + + @Override + public void vertexAttrib1fv(int index, FloatBuffer values) { + // TODO Auto-generated method stub + + } + + @Override + public void vertexAttrib2fv(int index, FloatBuffer values) { + // TODO Auto-generated method stub + + } + + @Override + public void vertexAttrib3fv(int index, FloatBuffer values) { + // TODO Auto-generated method stub + + } + + @Override + public void vertexAttrib4fv(int index, FloatBuffer values) { + // TODO Auto-generated method stub + + } + + @Override + public void vertexAttribPointer(int index, int size, int type, + boolean normalized, int stride, int offset) { + report("vertexAttribPointer"); + + int gl2vktype = 0; + if (type == UNSIGNED_BYTE) + gl2vktype = GL2VK.GL_UNSIGNED_BYTE; + else if (type == FLOAT) + gl2vktype = GL2VK.GL_FLOAT; + else if (type == UNSIGNED_SHORT) + gl2vktype = GL2VK.GL_UNSIGNED_SHORT; + else if (type == UNSIGNED_INT) + gl2vktype = GL2VK.GL_UNSIGNED_INT; + else if (type == INT) + gl2vktype = GL2VK.GL_INT; + else if (type == BYTE) + gl2vktype = GL2VK.GL_BYTE; + else if (type == SHORT) + gl2vktype = GL2VK.GL_SHORT; + else if (type == BOOL) + gl2vktype = GL2VK.GL_BOOL; + else System.out.println("WARNING Unknown type "+type); + + gl2vk.glVertexAttribPointer(index, size, gl2vktype, normalized, stride, offset); + + } + + @Override + public void enableVertexAttribArray(int index) { + // Unneeded and unused in gl2vk + + gl2vk.glEnableVertexAttribArray(index); + } + + @Override + public void disableVertexAttribArray(int index) { + // Unneeded and unused in gl2vk + gl2vk.glDisableVertexAttribArray(index); + } + + @Override + public void drawArraysImpl(int mode, int first, int count) { + report("drawArrays"); + gl2vk.glDrawArrays(mode, first, count); + } + + @Override + public void drawElementsImpl(int mode, int count, int type, int offset) { + int gl2vktype = 0; + if (type == UNSIGNED_BYTE) { + gl2vktype = GL2VK.GL_UNSIGNED_BYTE; + } + else if (type == UNSIGNED_INT) { + gl2vktype = GL2VK.GL_UNSIGNED_INT; + } + else if (type == UNSIGNED_SHORT) { + gl2vktype = GL2VK.GL_UNSIGNED_SHORT; + } + report("drawElements"); + gl2vk.glDrawElements(mode, count, gl2vktype, offset); + } + + @Override + public void lineWidth(float width) { + // TODO Auto-generated method stub + } + + @Override + public void frontFace(int dir) { + // TODO Auto-generated method stub + } + + @Override + public void cullFace(int mode) { + // TODO Auto-generated method stub + + } + + @Override + public void polygonOffset(float factor, float units) { + // TODO Auto-generated method stub + + } + + @Override + public void pixelStorei(int pname, int param) { + // TODO Auto-generated method stub + + } + + @Override + public void texImage2D(int target, int level, int internalFormat, int width, + int height, int border, int format, int type, + Buffer data) { + gl2vk.glTexImage2D(target, level, internalFormat, width, height, border, format, type, data); + } + + @Override + public void copyTexImage2D(int target, int level, int internalFormat, int x, + int y, int width, int height, int border) { + // TODO Auto-generated method stub + + } + + @Override + public void texSubImage2D(int target, int level, int xOffset, int yOffset, + int width, int height, int format, int type, + Buffer data) { + report("texSubImage2D"); + + // Don't buffer the white 16x16 memory-saver slow-af clear texture. + // TODO: automatically detect incoming wave of clear textures, and then + // submit vkCmdClearImage command. + if (!(width == 16 && height == 16)) { + gl2vk.glTexSubImage2D(target, level, xOffset, yOffset, width, height, format, type, data); + } + } + + @Override + public void copyTexSubImage2D(int target, int level, int xOffset, int yOffset, + int x, int y, int width, int height) { + // TODO Auto-generated method stub + + } + + @Override + public void compressedTexImage2D(int target, int level, int internalFormat, + int width, int height, int border, + int imageSize, Buffer data) { + // TODO Auto-generated method stub + + } + + @Override + public void compressedTexSubImage2D(int target, int level, int xOffset, + int yOffset, int width, int height, + int format, int imageSize, Buffer data) { + // TODO Auto-generated method stub + + } + + @Override + public void texParameteri(int target, int pname, int param) { + // TODO Auto-generated method stub + + } + + @Override + public void texParameterf(int target, int pname, float param) { + // TODO Auto-generated method stub + + } + + @Override + public void texParameteriv(int target, int pname, IntBuffer params) { + // TODO Auto-generated method stub + + } + + @Override + public void texParameterfv(int target, int pname, FloatBuffer params) { + // TODO Auto-generated method stub + + } + + @Override + public void generateMipmap(int target) { + // TODO Auto-generated method stub + + } + + @Override + public void genTextures(int n, IntBuffer textures) { + report("genTextures"); + gl2vk.glGenTextures(n, textures); + } + + @Override + public void deleteTextures(int n, IntBuffer textures) { + // TODO Auto-generated method stub + + } + + @Override + public void getTexParameteriv(int target, int pname, IntBuffer params) { + // TODO Auto-generated method stub + + } + + @Override + public void getTexParameterfv(int target, int pname, FloatBuffer params) { + // TODO Auto-generated method stub + + } + + @Override + public boolean isTexture(int texture) { + // TODO Auto-generated method stub + return false; + } + + @Override + public int createShader(int type) { + report("createShader"); + int gl2vktype = 0; + if (type == VERTEX_SHADER) { + gl2vktype = GL2VK.GL_VERTEX_SHADER; + } + else { + gl2vktype = GL2VK.GL_FRAGMENT_SHADER; + } + return gl2vk.glCreateShader(gl2vktype); + } + + @Override + public void shaderSource(int shader, String source) { + report("shaderSource"); + gl2vk.glShaderSource(shader, source); + } + + @Override + public void compileShader(int shader) { + report("compileShader"); + gl2vk.glCompileShader(shader); + } + + @Override + public void releaseShaderCompiler() { + // TODO Auto-generated method stub + + } + + @Override + public void deleteShader(int shader) { + report("deleteShader"); + gl2vk.glDeleteShader(shader); + + } + + @Override + public void shaderBinary(int count, IntBuffer shaders, int binaryFormat, + Buffer binary, int length) { + // TODO Auto-generated method stub + + } + + @Override + public int createProgram() { + report("createProgram"); + return gl2vk.glCreateProgram(); + } + + @Override + public void attachShader(int program, int shader) { + report("attachShader"); + gl2vk.glAttachShader(program, shader); + } + + @Override + public void detachShader(int program, int shader) { + // TODO Auto-generated method stub + + } + + @Override + public void linkProgram(int program) { + report("linkProgram"); + gl2vk.glLinkProgram(program); + } + + @Override + public void useProgram(int program) { + report("useProgram"); + gl2vk.glUseProgram(program); + } + + @Override + public void deleteProgram(int program) { + // TODO Auto-generated method stub + } + + @Override + public String getActiveAttrib(int program, int index, IntBuffer size, + IntBuffer type) { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getAttribLocation(int program, String name) { + report("getAttribLocation"); + return gl2vk.glGetAttribLocation(program, name); + } + + @Override + public void bindAttribLocation(int program, int index, String name) { + + } + + @Override + public int getUniformLocation(int program, String name) { + report("getUniformLocation"); + return gl2vk.glGetUniformLocation(program, name); + } + + @Override + public String getActiveUniform(int program, int index, IntBuffer size, + IntBuffer type) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void uniform1i(int location, int value) { + report("uniform1i"); + gl2vk.glUniform1i(location, value); + } + + @Override + public void uniform2i(int location, int value0, int value1) { + report("uniform2i"); + gl2vk.glUniform2i(location, value0, value1); + } + + @Override + public void uniform3i(int location, int value0, int value1, int value2) { + report("uniform3i"); + gl2vk.glUniform3i(location, value0, value1, value2); + } + + @Override + public void uniform4i(int location, int value0, int value1, int value2, + int value3) { + report("uniform4i"); + gl2vk.glUniform4i(location, value0, value1, value2, value3); + } + + @Override + public void uniform1f(int location, float value) { + report("uniform1f"); + gl2vk.glUniform1f(location, value); + + } + + @Override + public void uniform2f(int location, float value0, float value1) { + report("uniform2f"); + gl2vk.glUniform2f(location, value0, value1); + + } + + @Override + public void uniform3f(int location, float value0, float value1, + float value2) { + report("uniform3f"); + gl2vk.glUniform3f(location, value0, value1, value2); + + } + + @Override + public void uniform4f(int location, float value0, float value1, float value2, + float value3) { + report("uniform4f"); + gl2vk.glUniform4f(location, value0, value1, value2, value3); + + } + + @Override + public void uniform1iv(int location, int count, IntBuffer v) { + // TODO Auto-generated method stub + + } + + @Override + public void uniform2iv(int location, int count, IntBuffer v) { + // TODO Auto-generated method stub + + } + + @Override + public void uniform3iv(int location, int count, IntBuffer v) { + // TODO Auto-generated method stub + + } + + @Override + public void uniform4iv(int location, int count, IntBuffer v) { + // TODO Auto-generated method stub + + } + + @Override + public void uniform1fv(int location, int count, FloatBuffer v) { + // TODO Auto-generated method stub + + } + + @Override + public void uniform2fv(int location, int count, FloatBuffer v) { + // TODO Auto-generated method stub + + } + + @Override + public void uniform3fv(int location, int count, FloatBuffer v) { + // TODO Auto-generated method stub + + } + + @Override + public void uniform4fv(int location, int count, FloatBuffer v) { + // TODO Auto-generated method stub + + } + + @Override + public void uniformMatrix2fv(int location, int count, boolean transpose, + FloatBuffer mat) { + // TODO Auto-generated method stub + + } + + @Override + public void uniformMatrix3fv(int location, int count, boolean transpose, + FloatBuffer mat) { + // TODO Auto-generated method stub + + } + + @Override + public void uniformMatrix4fv(int location, int count, boolean transpose, + FloatBuffer mat) { + + report("uniformMatrix4fv"); + gl2vk.glUniformMatrix4fv(location, count, transpose, mat); + +// mat.rewind(); +// for (int y = 0; y < 4; y++) { +// for (int x = 0; x < 4; x++) { +// System.out.print(" "+mat.get()); +// } +// System.out.println(); +// } +// +// System.out.println(); +// +// mat.rewind(); + } + + @Override + public void validateProgram(int program) { + // TODO Auto-generated method stub + + } + + @Override + public boolean isShader(int shader) { + // TODO Auto-generated method stub + return true; + } + + @Override + public void getShaderiv(int shader, int pname, IntBuffer params) { + report("getShaderiv"); + int gl2vkpname = 0; + if (pname == COMPILE_STATUS) { + gl2vkpname = GL2VK.GL_COMPILE_STATUS; + } + else if (pname == INFO_LOG_LENGTH) { + gl2vkpname = GL2VK.GL_INFO_LOG_LENGTH; + } + gl2vk.glGetShaderiv(shader, gl2vkpname, params); + } + + @Override + public void getAttachedShaders(int program, int maxCount, IntBuffer count, + IntBuffer shaders) { + // TODO Auto-generated method stub + + } + + @Override + public String getShaderInfoLog(int shader) { + report("getShaderInfoLog"); + return gl2vk.glGetShaderInfoLog(shader); + } + + @Override + public String getShaderSource(int shader) { + report("getShaderSource"); + return gl2vk.glGetShaderSource(shader); + } + + @Override + public void getShaderPrecisionFormat(int shaderType, int precisionType, + IntBuffer range, IntBuffer precision) { + // TODO Auto-generated method stub + + } + + @Override + public void getVertexAttribfv(int index, int pname, FloatBuffer params) { + // TODO Auto-generated method stub + + } + + @Override + public void getVertexAttribiv(int index, int pname, IntBuffer params) { + // TODO Auto-generated method stub + + } + + @Override + public void getVertexAttribPointerv(int index, int pname, ByteBuffer data) { + // TODO Auto-generated method stub + + } + + @Override + public void getUniformfv(int program, int location, FloatBuffer params) { + // TODO Auto-generated method stub + + } + + @Override + public void getUniformiv(int program, int location, IntBuffer params) { + // TODO Auto-generated method stub + + } + + @Override + public boolean isProgram(int program) { + // TODO Auto-generated method stub + return false; + } + + @Override + public void getProgramiv(int program, int pname, IntBuffer params) { + // TODO Auto-generated method stub + + } + + @Override + public String getProgramInfoLog(int program) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void scissor(int x, int y, int w, int h) { + // TODO Auto-generated method stub + + } + + @Override + public void sampleCoverage(float value, boolean invert) { + // TODO Auto-generated method stub + + } + + @Override + public void stencilFunc(int func, int ref, int mask) { + // TODO Auto-generated method stub + + } + + @Override + public void stencilFuncSeparate(int face, int func, int ref, int mask) { + // TODO Auto-generated method stub + + } + + @Override + public void stencilOp(int sfail, int dpfail, int dppass) { + // TODO Auto-generated method stub + + } + + @Override + public void stencilOpSeparate(int face, int sfail, int dpfail, int dppass) { + // TODO Auto-generated method stub + + } + + @Override + public void depthFunc(int func) { + // TODO Auto-generated method stub + + } + + @Override + public void blendEquation(int mode) { + // TODO Auto-generated method stub + + } + + @Override + public void blendEquationSeparate(int modeRGB, int modeAlpha) { + // TODO Auto-generated method stub + + } + + @Override + public void blendFunc(int src, int dst) { + // TODO Auto-generated method stub + + } + + @Override + public void blendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, + int dstAlpha) { + // TODO Auto-generated method stub + + } + + @Override + public void blendColor(float red, float green, float blue, float alpha) { + // TODO Auto-generated method stub + + } + + @Override + public void colorMask(boolean r, boolean g, boolean b, boolean a) { + // TODO Auto-generated method stub + + } + + @Override + public void depthMask(boolean mask) { + gl2vk.glDepthMask(mask); + } + + @Override + public void stencilMask(int mask) { + // TODO Auto-generated method stub + + } + + @Override + public void stencilMaskSeparate(int face, int mask) { + // TODO Auto-generated method stub + + } + + @Override + public void clearColor(float r, float g, float b, float a) { + // TODO Auto-generated method stub + gl2vk.glClearColor(r, g, b, a); + } + + @Override + public void clearDepth(float d) { + // TODO Auto-generated method stub + + } + + @Override + public void clearStencil(int s) { + // TODO Auto-generated method stub + + } + + @Override + public void clear(int buf) { + // TODO Auto-generated method stub + + } + + @Override + public void deleteFramebuffers(int n, IntBuffer framebuffers) { + // TODO Auto-generated method stub + + } + + @Override + public void genFramebuffers(int n, IntBuffer framebuffers) { + // TODO Auto-generated method stub + + } + + @Override + public void bindRenderbuffer(int target, int renderbuffer) { + // TODO Auto-generated method stub + + } + + @Override + public void deleteRenderbuffers(int n, IntBuffer renderbuffers) { + // TODO Auto-generated method stub + + } + + @Override + public void genRenderbuffers(int n, IntBuffer renderbuffers) { + // TODO Auto-generated method stub + + } + + @Override + public void renderbufferStorage(int target, int internalFormat, int width, + int height) { + // TODO Auto-generated method stub + + } + + @Override + public void framebufferRenderbuffer(int target, int attachment, int rbt, + int renderbuffer) { + // TODO Auto-generated method stub + + } + + @Override + public void framebufferTexture2D(int target, int attachment, int texTarget, + int texture, int level) { + // TODO Auto-generated method stub + + } + + @Override + public int checkFramebufferStatus(int target) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean isFramebuffer(int framebuffer) { + // TODO Auto-generated method stub + return false; + } + + @Override + public void getFramebufferAttachmentParameteriv(int target, int attachment, + int name, IntBuffer params) { + // TODO Auto-generated method stub + + } + + @Override + public boolean isRenderbuffer(int renderbuffer) { + // TODO Auto-generated method stub + return false; + } + + @Override + public void getRenderbufferParameteriv(int target, int name, + IntBuffer params) { + // TODO Auto-generated method stub + + } + + @Override + public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, + int dstX0, int dstY0, int dstX1, int dstY1, + int mask, int filter) { + // TODO Auto-generated method stub + + } + + @Override + public void renderbufferStorageMultisample(int target, int samples, + int format, int width, + int height) { + // TODO Auto-generated method stub + + } + + @Override + public void readBuffer(int buf) { + // TODO Auto-generated method stub + + } + + @Override + public void drawBuffer(int buf) { + // TODO Auto-generated method stub + + } + + @Override + protected void setFrameRate(float fps) { + // TODO Auto-generated method stub + + } + + @Override + protected void initSurface(int antialias) { + // TODO Auto-generated method stub + + } + + @Override + protected void reinitSurface() { + // TODO Auto-generated method stub + + } + + @Override + protected void registerListeners() { + // TODO Auto-generated method stub + + } + + @Override + protected int getDepthBits() { + // TODO Auto-generated method stub + return 0; + } + + @Override + protected int getStencilBits() { + // TODO Auto-generated method stub + return 0; + } + + @Override + protected float getPixelScale() { + // TODO Auto-generated method stub + return 0; + } + + @Override + protected void getGL(PGL pgl) { + } + + @Override + protected boolean canDraw() { + // TODO Auto-generated method stub + return false; + } + + @Override + protected void requestFocus() { + // TODO Auto-generated method stub + + } + + @Override + protected void requestDraw() { + // TODO Auto-generated method stub + + } + + @Override + protected void swapBuffers() { + // TODO Auto-generated method stub + + } + + @Override + protected void initFBOLayer() { + // TODO Auto-generated method stub + + } + + @Override + protected int getGLSLVersion() { + // TODO Auto-generated method stub + return 0; + } + + @Override + protected String getGLSLVersionSuffix() { + // TODO Auto-generated method stub + return null; + } + + + @Override + protected Object getDerivedFont(Object font, float size) { + report("getDerivedFont"); + return ((Font) font).deriveFont(size); + } + +//Tessellator + + + @Override + protected Tessellator createTessellator(TessellatorCallback callback) { + return new Tessellator(callback); + } + + + protected static class Tessellator implements PGL.Tessellator { + protected GLUtessellator tess; + protected TessellatorCallback callback; + protected GLUCallback gluCallback; + + public Tessellator(TessellatorCallback callback) { + this.callback = callback; + tess = GLU.gluNewTess(); + gluCallback = new GLUCallback(); + + GLU.gluTessCallback(tess, GLU.GLU_TESS_BEGIN, gluCallback); + GLU.gluTessCallback(tess, GLU.GLU_TESS_END, gluCallback); + GLU.gluTessCallback(tess, GLU.GLU_TESS_VERTEX, gluCallback); + GLU.gluTessCallback(tess, GLU.GLU_TESS_COMBINE, gluCallback); + GLU.gluTessCallback(tess, GLU.GLU_TESS_ERROR, gluCallback); + } + + @Override + public void setCallback(int flag) { + GLU.gluTessCallback(tess, flag, gluCallback); + } + + @Override + public void setWindingRule(int rule) { + setProperty(GLU.GLU_TESS_WINDING_RULE, rule); + } + + public void setProperty(int property, int value) { + GLU.gluTessProperty(tess, property, value); + } + + @Override + public void beginPolygon() { + beginPolygon(null); + } + + @Override + public void beginPolygon(Object data) { + GLU.gluTessBeginPolygon(tess, data); + } + + @Override + public void endPolygon() { + GLU.gluTessEndPolygon(tess); + } + + @Override + public void beginContour() { + GLU.gluTessBeginContour(tess); + } + + @Override + public void endContour() { + GLU.gluTessEndContour(tess); + } + + @Override + public void addVertex(double[] v) { + addVertex(v, 0, v); + } + + @Override + public void addVertex(double[] v, int n, Object data) { + GLU.gluTessVertex(tess, v, n, data); + } + + protected class GLUCallback extends GLUtessellatorCallbackAdapter { + @Override + public void begin(int type) { + callback.begin(type); + } + + @Override + public void end() { + callback.end(); + } + + @Override + public void vertex(Object data) { + callback.vertex(data); + } + + @Override + public void combine(double[] coords, Object[] data, + float[] weight, Object[] outData) { + callback.combine(coords, data, weight, outData); + } + + @Override + public void error(int errnum) { + callback.error(errnum); + } + } + } + + + @Override + protected String tessError(int err) { + return glu.gluErrorString(err); + } + + @Override + protected FontOutline createFontOutline(char ch, Object font) { + // TODO Auto-generated method stub + return null; + } + + @Override + protected void viewportImpl(int x, int y, int w, int h) { + // TODO Auto-generated method stub + + } + + @Override + protected void readPixelsImpl(int x, int y, int width, int height, int format, + int type, Buffer buffer) { + // TODO Auto-generated method stub + + } + + @Override + protected void readPixelsImpl(int x, int y, int width, int height, int format, + int type, long offset) { + // TODO Auto-generated method stub + + } + + @Override + protected void activeTextureImpl(int texture) { + // TODO Auto-generated method stub + } + + @Override + protected void bindTextureImpl(int target, int texture) { + report("bindTextureImpl"); + gl2vk.glBindTexture(texture); + } + + @Override + protected void bindFramebufferImpl(int target, int framebuffer) { + // TODO Auto-generated method stub + + } + +} diff --git a/core/src/processing/vulkan/PVKGraphics2D.java b/core/src/processing/vulkan/PVKGraphics2D.java new file mode 100644 index 0000000000..728c6b4bd1 --- /dev/null +++ b/core/src/processing/vulkan/PVKGraphics2D.java @@ -0,0 +1,463 @@ +package processing.vulkan; + +import processing.core.PGraphics; +import processing.core.PMatrix3D; +import processing.core.PShape; +import processing.core.PShapeSVG; +import processing.opengl.PGraphicsOpenGL; +import processing.opengl.PShapeOpenGL; + +public class PVKGraphics2D extends PGraphicsVulkan { + public PVKGraphics2D() { + super(); + } + + ////////////////////////////////////////////////////////////// + + // RENDERER SUPPORT QUERIES + + + @Override + public boolean is2D() { + return true; + } + + + @Override + public boolean is3D() { + return false; + } + + + ////////////////////////////////////////////////////////////// + + // HINTS + + + @Override + public void hint(int which) { + if (which == ENABLE_STROKE_PERSPECTIVE) { + showWarning("Strokes cannot be perspective-corrected in 2D."); + return; + } + super.hint(which); + } + + + ////////////////////////////////////////////////////////////// + + // PROJECTION + + + @Override + public void ortho() { + showMethodWarning("ortho"); + } + + + @Override + public void ortho(float left, float right, + float bottom, float top) { + showMethodWarning("ortho"); + } + + + @Override + public void ortho(float left, float right, + float bottom, float top, + float near, float far) { + showMethodWarning("ortho"); + } + + + @Override + public void perspective() { + showMethodWarning("perspective"); + } + + + @Override + public void perspective(float fov, float aspect, float zNear, float zFar) { + showMethodWarning("perspective"); + } + + + @Override + public void frustum(float left, float right, float bottom, float top, + float znear, float zfar) { + showMethodWarning("frustum"); + } + + + @Override + protected void defaultPerspective() { + super.ortho(0, width, -height, 0, -1, +1); + } + + + ////////////////////////////////////////////////////////////// + + // CAMERA + + + @Override + public void beginCamera() { + showMethodWarning("beginCamera"); + } + + + @Override + public void endCamera() { + showMethodWarning("endCamera"); + } + + + @Override + public void camera() { + showMethodWarning("camera"); + } + + + @Override + public void camera(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) { + showMethodWarning("camera"); + } + + + @Override + protected void defaultCamera() { + eyeDist = 1; + resetMatrix(); + } + + + ////////////////////////////////////////////////////////////// + + // MATRIX MORE! + + + @Override + protected void begin2D() { + pushProjection(); + defaultPerspective(); + pushMatrix(); + defaultCamera(); + } + + + @Override + protected void end2D() { + popMatrix(); + popProjection(); + } + + + ////////////////////////////////////////////////////////////// + + // SHAPE + + + @Override + public void shape(PShape shape) { + if (shape.is2D()) { + super.shape(shape); + } else { + showWarning("The shape object is not 2D, cannot be displayed with " + + "this renderer"); + } + } + + + @Override + public void shape(PShape shape, float x, float y) { + if (shape.is2D()) { + super.shape(shape, x, y); + } else { + showWarning("The shape object is not 2D, cannot be displayed with " + + "this renderer"); + } + } + + + @Override + public void shape(PShape shape, float a, float b, float c, float d) { + if (shape.is2D()) { + super.shape(shape, a, b, c, d); + } else { + showWarning("The shape object is not 2D, cannot be displayed with " + + "this renderer"); + } + } + + + @Override + public void shape(PShape shape, float x, float y, float z) { + showDepthWarningXYZ("shape"); + } + + + @Override + public void shape(PShape shape, float x, float y, float z, + float c, float d, float e) { + showDepthWarningXYZ("shape"); + } + + + + ////////////////////////////////////////////////////////////// + + // SHAPE I/O + + + static protected boolean isSupportedExtension(String extension) { + return extension.equals("svg") || extension.equals("svgz"); + } + + + static protected PShape loadShapeImpl(PGraphics pg, + String filename, String extension) { + if (extension.equals("svg") || extension.equals("svgz")) { + PShapeSVG svg = new PShapeSVG(pg.parent.loadXML(filename)); + return PShapeOpenGL.createShape((PGraphicsOpenGL) pg, svg); + } + return null; + } + + + ////////////////////////////////////////////////////////////// + + // SCREEN TRANSFORMS + + + @Override + public float modelX(float x, float y, float z) { + showDepthWarning("modelX"); + return 0; + } + + + @Override + public float modelY(float x, float y, float z) { + showDepthWarning("modelY"); + return 0; + } + + + @Override + public float modelZ(float x, float y, float z) { + showDepthWarning("modelZ"); + return 0; + } + + + + ////////////////////////////////////////////////////////////// + + // BEZIER VERTICES + + + @Override + public void bezierVertex(float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4) { + showDepthWarningXYZ("bezierVertex"); + } + + + ////////////////////////////////////////////////////////////// + + // QUADRATIC BEZIER VERTICES + + + @Override + public void quadraticVertex(float x2, float y2, float z2, + float x4, float y4, float z4) { + showDepthWarningXYZ("quadVertex"); + } + + + ////////////////////////////////////////////////////////////// + + // CURVE VERTICES + + + @Override + public void curveVertex(float x, float y, float z) { + showDepthWarningXYZ("curveVertex"); + } + + + ////////////////////////////////////////////////////////////// + + // BOX + + + @Override + public void box(float w, float h, float d) { + showMethodWarning("box"); + } + + + ////////////////////////////////////////////////////////////// + + // SPHERE + + + @Override + public void sphere(float r) { + showMethodWarning("sphere"); + } + + + ////////////////////////////////////////////////////////////// + + // VERTEX SHAPES + + + @Override + public void vertex(float x, float y, float z) { + showDepthWarningXYZ("vertex"); + } + + @Override + public void vertex(float x, float y, float z, float u, float v) { + showDepthWarningXYZ("vertex"); + } + + ////////////////////////////////////////////////////////////// + + // MATRIX TRANSFORMATIONS + + @Override + public void translate(float tx, float ty, float tz) { + showDepthWarningXYZ("translate"); + } + + @Override + public void rotateX(float angle) { + showDepthWarning("rotateX"); + } + + @Override + public void rotateY(float angle) { + showDepthWarning("rotateY"); + } + + @Override + public void rotateZ(float angle) { + showDepthWarning("rotateZ"); + } + + @Override + public void rotate(float angle, float vx, float vy, float vz) { + showVariationWarning("rotate"); + } + + @Override + public void applyMatrix(PMatrix3D source) { + showVariationWarning("applyMatrix"); + } + + @Override + public void applyMatrix(float n00, float n01, float n02, float n03, + float n10, float n11, float n12, float n13, + float n20, float n21, float n22, float n23, + float n30, float n31, float n32, float n33) { + showVariationWarning("applyMatrix"); + } + + @Override + public void scale(float sx, float sy, float sz) { + showDepthWarningXYZ("scale"); + } + + ////////////////////////////////////////////////////////////// + + // SCREEN AND MODEL COORDS + + @Override + public float screenX(float x, float y, float z) { + showDepthWarningXYZ("screenX"); + return 0; + } + + @Override + public float screenY(float x, float y, float z) { + showDepthWarningXYZ("screenY"); + return 0; + } + + @Override + public float screenZ(float x, float y, float z) { + showDepthWarningXYZ("screenZ"); + return 0; + } + + @Override + public PMatrix3D getMatrix(PMatrix3D target) { + showVariationWarning("getMatrix"); + return target; + } + + @Override + public void setMatrix(PMatrix3D source) { + showVariationWarning("setMatrix"); + } + + ////////////////////////////////////////////////////////////// + + // LIGHTS + + @Override + public void lights() { + showMethodWarning("lights"); + } + + @Override + public void noLights() { + showMethodWarning("noLights"); + } + + @Override + public void ambientLight(float red, float green, float blue) { + showMethodWarning("ambientLight"); + } + + @Override + public void ambientLight(float red, float green, float blue, + float x, float y, float z) { + showMethodWarning("ambientLight"); + } + + @Override + public void directionalLight(float red, float green, float blue, + float nx, float ny, float nz) { + showMethodWarning("directionalLight"); + } + + @Override + public void pointLight(float red, float green, float blue, + float x, float y, float z) { + showMethodWarning("pointLight"); + } + + @Override + public void spotLight(float red, float green, float blue, + float x, float y, float z, + float nx, float ny, float nz, + float angle, float concentration) { + showMethodWarning("spotLight"); + } + + @Override + public void lightFalloff(float constant, float linear, float quadratic) { + showMethodWarning("lightFalloff"); + } + + @Override + public void lightSpecular(float v1, float v2, float v3) { + showMethodWarning("lightSpecular"); + } +} diff --git a/core/src/processing/vulkan/PVKGraphics3D.java b/core/src/processing/vulkan/PVKGraphics3D.java new file mode 100644 index 0000000000..6d4764ee50 --- /dev/null +++ b/core/src/processing/vulkan/PVKGraphics3D.java @@ -0,0 +1,123 @@ +package processing.vulkan; + +import processing.core.PGraphics; +import processing.core.PShape; +import processing.core.PShapeOBJ; +import processing.opengl.PGraphicsOpenGL; +import processing.opengl.PShapeOpenGL; + +public class PVKGraphics3D extends PGraphicsVulkan { + public PVKGraphics3D() { + super(); + } + + + ////////////////////////////////////////////////////////////// + + // RENDERER SUPPORT QUERIES + + + @Override + public boolean is2D() { + return false; + } + + + @Override + public boolean is3D() { + return true; + } + + + ////////////////////////////////////////////////////////////// + + // PROJECTION + + + @Override + protected void defaultPerspective() { + perspective(); + } + + + ////////////////////////////////////////////////////////////// + + // CAMERA + + + @Override + protected void defaultCamera() { + camera(); + } + + + private void dumpStack() { +// Thread.dumpStack(); + } + + + ////////////////////////////////////////////////////////////// + + // MATRIX MORE! + + + @Override + protected void begin2D() { + pushProjection(); + ortho(-width/2f, width/2f, -height/2f, height/2f); + pushMatrix(); + + // Set camera for 2D rendering, it simply centers at (width/2, height/2) + float centerX = width/2f; + float centerY = height/2f; + + + modelview.reset(); + modelview.translate(-centerX, -centerY); + dumpStack(); + + modelviewInv.set(modelview); + modelviewInv.invert(); + + camera.set(modelview); + cameraInv.set(modelviewInv); + + updateProjmodelview(); + } + + + @Override + protected void end2D() { + popMatrix(); + popProjection(); + } + + + + ////////////////////////////////////////////////////////////// + + // SHAPE I/O + + + static protected boolean isSupportedExtension(String extension) { + return extension.equals("obj"); + } + + + static protected PShape loadShapeImpl(PGraphics pg, String filename, + String extension) { + PShapeOBJ obj = null; + + if (extension.equals("obj")) { + obj = new PShapeOBJ(pg.parent, filename); + int prevTextureMode = pg.textureMode; + pg.textureMode = NORMAL; + PShapeOpenGL p3d = PShapeOpenGL.createShape((PGraphicsOpenGL)pg, obj); + pg.textureMode = prevTextureMode; + return p3d; + } + return null; + } +} + + diff --git a/core/src/processing/vulkan/VKFrameBuffer.java b/core/src/processing/vulkan/VKFrameBuffer.java new file mode 100644 index 0000000000..1f78b5bd5a --- /dev/null +++ b/core/src/processing/vulkan/VKFrameBuffer.java @@ -0,0 +1,43 @@ +package processing.vulkan; + +import processing.opengl.FrameBuffer; +import processing.opengl.PGraphicsOpenGL; + +public class VKFrameBuffer extends FrameBuffer { + + private boolean depthTestEnabled = true; + + // TODO: Dummy depth bit. + + public VKFrameBuffer(PGraphicsOpenGL pg) { + super(pg); + } + + + public VKFrameBuffer(PGraphicsOpenGL pg, int w, int h, int samples, int colorBuffers, + boolean depthTest, boolean screen) { + super(pg, w, h, samples, colorBuffers, 0, 0, depthTest, screen); + this.depthTestEnabled = depthTest; + } + + + public VKFrameBuffer(PGraphicsOpenGL pg, int w, int h) { + super(pg, w, h, 1, 1, 0, 0, false, false); + } + + + public VKFrameBuffer(PGraphicsOpenGL pg, int w, int h, boolean screen) { + super(pg, w, h, 1, 1, 0, 0, false, screen); + } + + @Override + public boolean hasDepthBuffer() { + return true; + } + + @Override + public boolean hasStencilBuffer() { + return true; + } + +}