mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-29 00:55:41 +00:00
dep: Add SPIRV-Cross
This commit is contained in:
parent
49a4901c78
commit
b42f4a3b85
|
@ -39,3 +39,7 @@ if(${CPU_ARCH} STREQUAL "riscv64")
|
||||||
add_subdirectory(biscuit)
|
add_subdirectory(biscuit)
|
||||||
add_subdirectory(riscv-disas)
|
add_subdirectory(riscv-disas)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
add_subdirectory(spirv-cross)
|
||||||
|
endif()
|
||||||
|
|
35
dep/spirv-cross/CMakeLists.txt
Normal file
35
dep/spirv-cross/CMakeLists.txt
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
set(SRCS
|
||||||
|
include/spirv-cross/GLSL.std.450.h
|
||||||
|
include/spirv-cross/spirv.h
|
||||||
|
include/spirv-cross/spirv.hpp
|
||||||
|
include/spirv-cross/spirv_cfg.hpp
|
||||||
|
include/spirv-cross/spirv_common.hpp
|
||||||
|
include/spirv-cross/spirv_cpp.hpp
|
||||||
|
include/spirv-cross/spirv_cross.hpp
|
||||||
|
include/spirv-cross/spirv_cross_containers.hpp
|
||||||
|
include/spirv-cross/spirv_cross_error_handling.hpp
|
||||||
|
include/spirv-cross/spirv_cross_parsed_ir.hpp
|
||||||
|
include/spirv-cross/spirv_cross_util.hpp
|
||||||
|
include/spirv-cross/spirv_glsl.hpp
|
||||||
|
include/spirv-cross/spirv_hlsl.hpp
|
||||||
|
include/spirv-cross/spirv_msl.hpp
|
||||||
|
include/spirv-cross/spirv_parser.hpp
|
||||||
|
include/spirv-cross/spirv_reflect.hpp
|
||||||
|
src/spirv_cfg.cpp
|
||||||
|
src/spirv_cpp.cpp
|
||||||
|
src/spirv_cross.cpp
|
||||||
|
src/spirv_cross_parsed_ir.cpp
|
||||||
|
src/spirv_cross_util.cpp
|
||||||
|
src/spirv_glsl.cpp
|
||||||
|
src/spirv_hlsl.cpp
|
||||||
|
src/spirv_msl.cpp
|
||||||
|
src/spirv_parser.cpp
|
||||||
|
src/spirv_reflect.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(spirv-cross ${SRCS})
|
||||||
|
|
||||||
|
target_include_directories(spirv-cross PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-cross")
|
||||||
|
target_include_directories(spirv-cross PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||||
|
target_compile_definitions(spirv-cross PUBLIC SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS)
|
||||||
|
|
202
dep/spirv-cross/LICENSE
Normal file
202
dep/spirv-cross/LICENSE
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
114
dep/spirv-cross/include/spirv-cross/GLSL.std.450.h
Normal file
114
dep/spirv-cross/include/spirv-cross/GLSL.std.450.h
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014-2016,2021 The Khronos Group, Inc.
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
|
||||||
|
* STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
|
||||||
|
* HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GLSLstd450_H
|
||||||
|
#define GLSLstd450_H
|
||||||
|
|
||||||
|
static const int GLSLstd450Version = 100;
|
||||||
|
static const int GLSLstd450Revision = 3;
|
||||||
|
|
||||||
|
enum GLSLstd450 {
|
||||||
|
GLSLstd450Bad = 0, // Don't use
|
||||||
|
|
||||||
|
GLSLstd450Round = 1,
|
||||||
|
GLSLstd450RoundEven = 2,
|
||||||
|
GLSLstd450Trunc = 3,
|
||||||
|
GLSLstd450FAbs = 4,
|
||||||
|
GLSLstd450SAbs = 5,
|
||||||
|
GLSLstd450FSign = 6,
|
||||||
|
GLSLstd450SSign = 7,
|
||||||
|
GLSLstd450Floor = 8,
|
||||||
|
GLSLstd450Ceil = 9,
|
||||||
|
GLSLstd450Fract = 10,
|
||||||
|
|
||||||
|
GLSLstd450Radians = 11,
|
||||||
|
GLSLstd450Degrees = 12,
|
||||||
|
GLSLstd450Sin = 13,
|
||||||
|
GLSLstd450Cos = 14,
|
||||||
|
GLSLstd450Tan = 15,
|
||||||
|
GLSLstd450Asin = 16,
|
||||||
|
GLSLstd450Acos = 17,
|
||||||
|
GLSLstd450Atan = 18,
|
||||||
|
GLSLstd450Sinh = 19,
|
||||||
|
GLSLstd450Cosh = 20,
|
||||||
|
GLSLstd450Tanh = 21,
|
||||||
|
GLSLstd450Asinh = 22,
|
||||||
|
GLSLstd450Acosh = 23,
|
||||||
|
GLSLstd450Atanh = 24,
|
||||||
|
GLSLstd450Atan2 = 25,
|
||||||
|
|
||||||
|
GLSLstd450Pow = 26,
|
||||||
|
GLSLstd450Exp = 27,
|
||||||
|
GLSLstd450Log = 28,
|
||||||
|
GLSLstd450Exp2 = 29,
|
||||||
|
GLSLstd450Log2 = 30,
|
||||||
|
GLSLstd450Sqrt = 31,
|
||||||
|
GLSLstd450InverseSqrt = 32,
|
||||||
|
|
||||||
|
GLSLstd450Determinant = 33,
|
||||||
|
GLSLstd450MatrixInverse = 34,
|
||||||
|
|
||||||
|
GLSLstd450Modf = 35, // second operand needs an OpVariable to write to
|
||||||
|
GLSLstd450ModfStruct = 36, // no OpVariable operand
|
||||||
|
GLSLstd450FMin = 37,
|
||||||
|
GLSLstd450UMin = 38,
|
||||||
|
GLSLstd450SMin = 39,
|
||||||
|
GLSLstd450FMax = 40,
|
||||||
|
GLSLstd450UMax = 41,
|
||||||
|
GLSLstd450SMax = 42,
|
||||||
|
GLSLstd450FClamp = 43,
|
||||||
|
GLSLstd450UClamp = 44,
|
||||||
|
GLSLstd450SClamp = 45,
|
||||||
|
GLSLstd450FMix = 46,
|
||||||
|
GLSLstd450IMix = 47, // Reserved
|
||||||
|
GLSLstd450Step = 48,
|
||||||
|
GLSLstd450SmoothStep = 49,
|
||||||
|
|
||||||
|
GLSLstd450Fma = 50,
|
||||||
|
GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to
|
||||||
|
GLSLstd450FrexpStruct = 52, // no OpVariable operand
|
||||||
|
GLSLstd450Ldexp = 53,
|
||||||
|
|
||||||
|
GLSLstd450PackSnorm4x8 = 54,
|
||||||
|
GLSLstd450PackUnorm4x8 = 55,
|
||||||
|
GLSLstd450PackSnorm2x16 = 56,
|
||||||
|
GLSLstd450PackUnorm2x16 = 57,
|
||||||
|
GLSLstd450PackHalf2x16 = 58,
|
||||||
|
GLSLstd450PackDouble2x32 = 59,
|
||||||
|
GLSLstd450UnpackSnorm2x16 = 60,
|
||||||
|
GLSLstd450UnpackUnorm2x16 = 61,
|
||||||
|
GLSLstd450UnpackHalf2x16 = 62,
|
||||||
|
GLSLstd450UnpackSnorm4x8 = 63,
|
||||||
|
GLSLstd450UnpackUnorm4x8 = 64,
|
||||||
|
GLSLstd450UnpackDouble2x32 = 65,
|
||||||
|
|
||||||
|
GLSLstd450Length = 66,
|
||||||
|
GLSLstd450Distance = 67,
|
||||||
|
GLSLstd450Cross = 68,
|
||||||
|
GLSLstd450Normalize = 69,
|
||||||
|
GLSLstd450FaceForward = 70,
|
||||||
|
GLSLstd450Reflect = 71,
|
||||||
|
GLSLstd450Refract = 72,
|
||||||
|
|
||||||
|
GLSLstd450FindILsb = 73,
|
||||||
|
GLSLstd450FindSMsb = 74,
|
||||||
|
GLSLstd450FindUMsb = 75,
|
||||||
|
|
||||||
|
GLSLstd450InterpolateAtCentroid = 76,
|
||||||
|
GLSLstd450InterpolateAtSample = 77,
|
||||||
|
GLSLstd450InterpolateAtOffset = 78,
|
||||||
|
|
||||||
|
GLSLstd450NMin = 79,
|
||||||
|
GLSLstd450NMax = 80,
|
||||||
|
GLSLstd450NClamp = 81,
|
||||||
|
|
||||||
|
GLSLstd450Count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // #ifndef GLSLstd450_H
|
2568
dep/spirv-cross/include/spirv-cross/spirv.h
Normal file
2568
dep/spirv-cross/include/spirv-cross/spirv.h
Normal file
File diff suppressed because it is too large
Load diff
2579
dep/spirv-cross/include/spirv-cross/spirv.hpp
Normal file
2579
dep/spirv-cross/include/spirv-cross/spirv.hpp
Normal file
File diff suppressed because it is too large
Load diff
168
dep/spirv-cross/include/spirv-cross/spirv_cfg.hpp
Normal file
168
dep/spirv-cross/include/spirv-cross/spirv_cfg.hpp
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016-2021 Arm Limited
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At your option, you may choose to accept this material under either:
|
||||||
|
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||||
|
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SPIRV_CROSS_CFG_HPP
|
||||||
|
#define SPIRV_CROSS_CFG_HPP
|
||||||
|
|
||||||
|
#include "spirv_common.hpp"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
{
|
||||||
|
class Compiler;
|
||||||
|
class CFG
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CFG(Compiler &compiler, const SPIRFunction &function);
|
||||||
|
|
||||||
|
Compiler &get_compiler()
|
||||||
|
{
|
||||||
|
return compiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Compiler &get_compiler() const
|
||||||
|
{
|
||||||
|
return compiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SPIRFunction &get_function() const
|
||||||
|
{
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t get_immediate_dominator(uint32_t block) const
|
||||||
|
{
|
||||||
|
auto itr = immediate_dominators.find(block);
|
||||||
|
if (itr != std::end(immediate_dominators))
|
||||||
|
return itr->second;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_reachable(uint32_t block) const
|
||||||
|
{
|
||||||
|
return visit_order.count(block) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t get_visit_order(uint32_t block) const
|
||||||
|
{
|
||||||
|
auto itr = visit_order.find(block);
|
||||||
|
assert(itr != std::end(visit_order));
|
||||||
|
int v = itr->second.get();
|
||||||
|
assert(v > 0);
|
||||||
|
return uint32_t(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t find_common_dominator(uint32_t a, uint32_t b) const;
|
||||||
|
|
||||||
|
const SmallVector<uint32_t> &get_preceding_edges(uint32_t block) const
|
||||||
|
{
|
||||||
|
auto itr = preceding_edges.find(block);
|
||||||
|
if (itr != std::end(preceding_edges))
|
||||||
|
return itr->second;
|
||||||
|
else
|
||||||
|
return empty_vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SmallVector<uint32_t> &get_succeeding_edges(uint32_t block) const
|
||||||
|
{
|
||||||
|
auto itr = succeeding_edges.find(block);
|
||||||
|
if (itr != std::end(succeeding_edges))
|
||||||
|
return itr->second;
|
||||||
|
else
|
||||||
|
return empty_vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Op>
|
||||||
|
void walk_from(std::unordered_set<uint32_t> &seen_blocks, uint32_t block, const Op &op) const
|
||||||
|
{
|
||||||
|
if (seen_blocks.count(block))
|
||||||
|
return;
|
||||||
|
seen_blocks.insert(block);
|
||||||
|
|
||||||
|
if (op(block))
|
||||||
|
{
|
||||||
|
for (auto b : get_succeeding_edges(block))
|
||||||
|
walk_from(seen_blocks, b, op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t find_loop_dominator(uint32_t block) const;
|
||||||
|
|
||||||
|
bool node_terminates_control_flow_in_sub_graph(BlockID from, BlockID to) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct VisitOrder
|
||||||
|
{
|
||||||
|
int &get()
|
||||||
|
{
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int &get() const
|
||||||
|
{
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
int v = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
Compiler &compiler;
|
||||||
|
const SPIRFunction &func;
|
||||||
|
std::unordered_map<uint32_t, SmallVector<uint32_t>> preceding_edges;
|
||||||
|
std::unordered_map<uint32_t, SmallVector<uint32_t>> succeeding_edges;
|
||||||
|
std::unordered_map<uint32_t, uint32_t> immediate_dominators;
|
||||||
|
std::unordered_map<uint32_t, VisitOrder> visit_order;
|
||||||
|
SmallVector<uint32_t> post_order;
|
||||||
|
SmallVector<uint32_t> empty_vector;
|
||||||
|
|
||||||
|
void add_branch(uint32_t from, uint32_t to);
|
||||||
|
void build_post_order_visit_order();
|
||||||
|
void build_immediate_dominators();
|
||||||
|
bool post_order_visit(uint32_t block);
|
||||||
|
uint32_t visit_count = 0;
|
||||||
|
|
||||||
|
bool is_back_edge(uint32_t to) const;
|
||||||
|
bool has_visited_forward_edge(uint32_t to) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DominatorBuilder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DominatorBuilder(const CFG &cfg);
|
||||||
|
|
||||||
|
void add_block(uint32_t block);
|
||||||
|
uint32_t get_dominator() const
|
||||||
|
{
|
||||||
|
return dominator;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lift_continue_block_dominator();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const CFG &cfg;
|
||||||
|
uint32_t dominator = 0;
|
||||||
|
};
|
||||||
|
} // namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
|
||||||
|
#endif
|
1921
dep/spirv-cross/include/spirv-cross/spirv_common.hpp
Normal file
1921
dep/spirv-cross/include/spirv-cross/spirv_common.hpp
Normal file
File diff suppressed because it is too large
Load diff
93
dep/spirv-cross/include/spirv-cross/spirv_cpp.hpp
Normal file
93
dep/spirv-cross/include/spirv-cross/spirv_cpp.hpp
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015-2021 Arm Limited
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At your option, you may choose to accept this material under either:
|
||||||
|
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||||
|
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SPIRV_CROSS_CPP_HPP
|
||||||
|
#define SPIRV_CROSS_CPP_HPP
|
||||||
|
|
||||||
|
#include "spirv_glsl.hpp"
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
{
|
||||||
|
class CompilerCPP : public CompilerGLSL
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit CompilerCPP(std::vector<uint32_t> spirv_)
|
||||||
|
: CompilerGLSL(std::move(spirv_))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilerCPP(const uint32_t *ir_, size_t word_count)
|
||||||
|
: CompilerGLSL(ir_, word_count)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit CompilerCPP(const ParsedIR &ir_)
|
||||||
|
: CompilerGLSL(ir_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit CompilerCPP(ParsedIR &&ir_)
|
||||||
|
: CompilerGLSL(std::move(ir_))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string compile() override;
|
||||||
|
|
||||||
|
// Sets a custom symbol name that can override
|
||||||
|
// spirv_cross_get_interface.
|
||||||
|
//
|
||||||
|
// Useful when several shader interfaces are linked
|
||||||
|
// statically into the same binary.
|
||||||
|
void set_interface_name(std::string name)
|
||||||
|
{
|
||||||
|
interface_name = std::move(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void emit_header() override;
|
||||||
|
void emit_c_linkage();
|
||||||
|
void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override;
|
||||||
|
|
||||||
|
void emit_resources();
|
||||||
|
void emit_buffer_block(const SPIRVariable &type) override;
|
||||||
|
void emit_push_constant_block(const SPIRVariable &var) override;
|
||||||
|
void emit_interface_block(const SPIRVariable &type);
|
||||||
|
void emit_block_chain(SPIRBlock &block);
|
||||||
|
void emit_uniform(const SPIRVariable &var) override;
|
||||||
|
void emit_shared(const SPIRVariable &var);
|
||||||
|
void emit_block_struct(SPIRType &type);
|
||||||
|
std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id) override;
|
||||||
|
|
||||||
|
std::string argument_decl(const SPIRFunction::Parameter &arg);
|
||||||
|
|
||||||
|
SmallVector<std::string> resource_registrations;
|
||||||
|
std::string impl_type;
|
||||||
|
std::string resource_type;
|
||||||
|
uint32_t shared_counter = 0;
|
||||||
|
|
||||||
|
std::string interface_name;
|
||||||
|
};
|
||||||
|
} // namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
|
||||||
|
#endif
|
1175
dep/spirv-cross/include/spirv-cross/spirv_cross.hpp
Normal file
1175
dep/spirv-cross/include/spirv-cross/spirv_cross.hpp
Normal file
File diff suppressed because it is too large
Load diff
755
dep/spirv-cross/include/spirv-cross/spirv_cross_containers.hpp
Normal file
755
dep/spirv-cross/include/spirv-cross/spirv_cross_containers.hpp
Normal file
|
@ -0,0 +1,755 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019-2021 Hans-Kristian Arntzen
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At your option, you may choose to accept this material under either:
|
||||||
|
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||||
|
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SPIRV_CROSS_CONTAINERS_HPP
|
||||||
|
#define SPIRV_CROSS_CONTAINERS_HPP
|
||||||
|
|
||||||
|
#include "spirv_cross_error_handling.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <exception>
|
||||||
|
#include <functional>
|
||||||
|
#include <iterator>
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
#include <stack>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef SPIRV_CROSS_NAMESPACE_OVERRIDE
|
||||||
|
#define SPIRV_CROSS_NAMESPACE SPIRV_CROSS_NAMESPACE_OVERRIDE
|
||||||
|
#else
|
||||||
|
#define SPIRV_CROSS_NAMESPACE spirv_cross
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
{
|
||||||
|
#ifndef SPIRV_CROSS_FORCE_STL_TYPES
|
||||||
|
// std::aligned_storage does not support size == 0, so roll our own.
|
||||||
|
template <typename T, size_t N>
|
||||||
|
class AlignedBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
T *data()
|
||||||
|
{
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||||
|
// MSVC 2013 workarounds, sigh ...
|
||||||
|
// Only use this workaround on MSVC 2013 due to some confusion around default initialized unions.
|
||||||
|
// Spec seems to suggest the memory will be zero-initialized, which is *not* what we want.
|
||||||
|
return reinterpret_cast<T *>(u.aligned_char);
|
||||||
|
#else
|
||||||
|
return reinterpret_cast<T *>(aligned_char);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||||
|
// MSVC 2013 workarounds, sigh ...
|
||||||
|
union
|
||||||
|
{
|
||||||
|
char aligned_char[sizeof(T) * N];
|
||||||
|
double dummy_aligner;
|
||||||
|
} u;
|
||||||
|
#else
|
||||||
|
alignas(T) char aligned_char[sizeof(T) * N];
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class AlignedBuffer<T, 0>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
T *data()
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// An immutable version of SmallVector which erases type information about storage.
|
||||||
|
template <typename T>
|
||||||
|
class VectorView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
T &operator[](size_t i) SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
return ptr[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
const T &operator[](size_t i) const SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
return ptr[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
return buffer_size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
return buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *data() SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T *data() const SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *begin() SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *end() SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
return ptr + buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T *begin() const SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T *end() const SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
return ptr + buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
T &front() SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
return ptr[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const T &front() const SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
return ptr[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
T &back() SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
return ptr[buffer_size - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
const T &back() const SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
return ptr[buffer_size - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes it easier to consume SmallVector.
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||||
|
explicit operator std::vector<T>() const
|
||||||
|
{
|
||||||
|
// Another MSVC 2013 workaround. It does not understand lvalue/rvalue qualified operations.
|
||||||
|
return std::vector<T>(ptr, ptr + buffer_size);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Makes it easier to consume SmallVector.
|
||||||
|
explicit operator std::vector<T>() const &
|
||||||
|
{
|
||||||
|
return std::vector<T>(ptr, ptr + buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are converting as an r-value, we can pilfer our elements.
|
||||||
|
explicit operator std::vector<T>() &&
|
||||||
|
{
|
||||||
|
return std::vector<T>(std::make_move_iterator(ptr), std::make_move_iterator(ptr + buffer_size));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Avoid sliced copies. Base class should only be read as a reference.
|
||||||
|
VectorView(const VectorView &) = delete;
|
||||||
|
void operator=(const VectorView &) = delete;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
VectorView() = default;
|
||||||
|
T *ptr = nullptr;
|
||||||
|
size_t buffer_size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Simple vector which supports up to N elements inline, without malloc/free.
|
||||||
|
// We use a lot of throwaway vectors all over the place which triggers allocations.
|
||||||
|
// This class only implements the subset of std::vector we need in SPIRV-Cross.
|
||||||
|
// It is *NOT* a drop-in replacement in general projects.
|
||||||
|
template <typename T, size_t N = 8>
|
||||||
|
class SmallVector : public VectorView<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SmallVector() SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
this->ptr = stack_storage.data();
|
||||||
|
buffer_capacity = N;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
SmallVector(const U *arg_list_begin, const U *arg_list_end) SPIRV_CROSS_NOEXCEPT : SmallVector()
|
||||||
|
{
|
||||||
|
auto count = size_t(arg_list_end - arg_list_begin);
|
||||||
|
reserve(count);
|
||||||
|
for (size_t i = 0; i < count; i++, arg_list_begin++)
|
||||||
|
new (&this->ptr[i]) T(*arg_list_begin);
|
||||||
|
this->buffer_size = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
SmallVector(std::initializer_list<U> init) SPIRV_CROSS_NOEXCEPT : SmallVector(init.begin(), init.end())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U, size_t M>
|
||||||
|
explicit SmallVector(const U (&init)[M]) SPIRV_CROSS_NOEXCEPT : SmallVector(init, init + M)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallVector(SmallVector &&other) SPIRV_CROSS_NOEXCEPT : SmallVector()
|
||||||
|
{
|
||||||
|
*this = std::move(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallVector &operator=(SmallVector &&other) SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
if (other.ptr != other.stack_storage.data())
|
||||||
|
{
|
||||||
|
// Pilfer allocated pointer.
|
||||||
|
if (this->ptr != stack_storage.data())
|
||||||
|
free(this->ptr);
|
||||||
|
this->ptr = other.ptr;
|
||||||
|
this->buffer_size = other.buffer_size;
|
||||||
|
buffer_capacity = other.buffer_capacity;
|
||||||
|
other.ptr = nullptr;
|
||||||
|
other.buffer_size = 0;
|
||||||
|
other.buffer_capacity = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Need to move the stack contents individually.
|
||||||
|
reserve(other.buffer_size);
|
||||||
|
for (size_t i = 0; i < other.buffer_size; i++)
|
||||||
|
{
|
||||||
|
new (&this->ptr[i]) T(std::move(other.ptr[i]));
|
||||||
|
other.ptr[i].~T();
|
||||||
|
}
|
||||||
|
this->buffer_size = other.buffer_size;
|
||||||
|
other.buffer_size = 0;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallVector(const SmallVector &other) SPIRV_CROSS_NOEXCEPT : SmallVector()
|
||||||
|
{
|
||||||
|
*this = other;
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallVector &operator=(const SmallVector &other) SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
clear();
|
||||||
|
reserve(other.buffer_size);
|
||||||
|
for (size_t i = 0; i < other.buffer_size; i++)
|
||||||
|
new (&this->ptr[i]) T(other.ptr[i]);
|
||||||
|
this->buffer_size = other.buffer_size;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit SmallVector(size_t count) SPIRV_CROSS_NOEXCEPT : SmallVector()
|
||||||
|
{
|
||||||
|
resize(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
~SmallVector()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
if (this->ptr != stack_storage.data())
|
||||||
|
free(this->ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < this->buffer_size; i++)
|
||||||
|
this->ptr[i].~T();
|
||||||
|
this->buffer_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_back(const T &t) SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
reserve(this->buffer_size + 1);
|
||||||
|
new (&this->ptr[this->buffer_size]) T(t);
|
||||||
|
this->buffer_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_back(T &&t) SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
reserve(this->buffer_size + 1);
|
||||||
|
new (&this->ptr[this->buffer_size]) T(std::move(t));
|
||||||
|
this->buffer_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_back() SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
// Work around false positive warning on GCC 8.3.
|
||||||
|
// Calling pop_back on empty vector is undefined.
|
||||||
|
if (!this->empty())
|
||||||
|
resize(this->buffer_size - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
void emplace_back(Ts &&... ts) SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
reserve(this->buffer_size + 1);
|
||||||
|
new (&this->ptr[this->buffer_size]) T(std::forward<Ts>(ts)...);
|
||||||
|
this->buffer_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reserve(size_t count) SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
if ((count > (std::numeric_limits<size_t>::max)() / sizeof(T)) ||
|
||||||
|
(count > (std::numeric_limits<size_t>::max)() / 2))
|
||||||
|
{
|
||||||
|
// Only way this should ever happen is with garbage input, terminate.
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > buffer_capacity)
|
||||||
|
{
|
||||||
|
size_t target_capacity = buffer_capacity;
|
||||||
|
if (target_capacity == 0)
|
||||||
|
target_capacity = 1;
|
||||||
|
|
||||||
|
// Weird parens works around macro issues on Windows if NOMINMAX is not used.
|
||||||
|
target_capacity = (std::max)(target_capacity, N);
|
||||||
|
|
||||||
|
// Need to ensure there is a POT value of target capacity which is larger than count,
|
||||||
|
// otherwise this will overflow.
|
||||||
|
while (target_capacity < count)
|
||||||
|
target_capacity <<= 1u;
|
||||||
|
|
||||||
|
T *new_buffer =
|
||||||
|
target_capacity > N ? static_cast<T *>(malloc(target_capacity * sizeof(T))) : stack_storage.data();
|
||||||
|
|
||||||
|
// If we actually fail this malloc, we are hosed anyways, there is no reason to attempt recovery.
|
||||||
|
if (!new_buffer)
|
||||||
|
std::terminate();
|
||||||
|
|
||||||
|
// In case for some reason two allocations both come from same stack.
|
||||||
|
if (new_buffer != this->ptr)
|
||||||
|
{
|
||||||
|
// We don't deal with types which can throw in move constructor.
|
||||||
|
for (size_t i = 0; i < this->buffer_size; i++)
|
||||||
|
{
|
||||||
|
new (&new_buffer[i]) T(std::move(this->ptr[i]));
|
||||||
|
this->ptr[i].~T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->ptr != stack_storage.data())
|
||||||
|
free(this->ptr);
|
||||||
|
this->ptr = new_buffer;
|
||||||
|
buffer_capacity = target_capacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(T *itr, const T *insert_begin, const T *insert_end) SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
auto count = size_t(insert_end - insert_begin);
|
||||||
|
if (itr == this->end())
|
||||||
|
{
|
||||||
|
reserve(this->buffer_size + count);
|
||||||
|
for (size_t i = 0; i < count; i++, insert_begin++)
|
||||||
|
new (&this->ptr[this->buffer_size + i]) T(*insert_begin);
|
||||||
|
this->buffer_size += count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (this->buffer_size + count > buffer_capacity)
|
||||||
|
{
|
||||||
|
auto target_capacity = this->buffer_size + count;
|
||||||
|
if (target_capacity == 0)
|
||||||
|
target_capacity = 1;
|
||||||
|
if (target_capacity < N)
|
||||||
|
target_capacity = N;
|
||||||
|
|
||||||
|
while (target_capacity < count)
|
||||||
|
target_capacity <<= 1u;
|
||||||
|
|
||||||
|
// Need to allocate new buffer. Move everything to a new buffer.
|
||||||
|
T *new_buffer =
|
||||||
|
target_capacity > N ? static_cast<T *>(malloc(target_capacity * sizeof(T))) : stack_storage.data();
|
||||||
|
|
||||||
|
// If we actually fail this malloc, we are hosed anyways, there is no reason to attempt recovery.
|
||||||
|
if (!new_buffer)
|
||||||
|
std::terminate();
|
||||||
|
|
||||||
|
// First, move elements from source buffer to new buffer.
|
||||||
|
// We don't deal with types which can throw in move constructor.
|
||||||
|
auto *target_itr = new_buffer;
|
||||||
|
auto *original_source_itr = this->begin();
|
||||||
|
|
||||||
|
if (new_buffer != this->ptr)
|
||||||
|
{
|
||||||
|
while (original_source_itr != itr)
|
||||||
|
{
|
||||||
|
new (target_itr) T(std::move(*original_source_itr));
|
||||||
|
original_source_itr->~T();
|
||||||
|
++original_source_itr;
|
||||||
|
++target_itr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy-construct new elements.
|
||||||
|
for (auto *source_itr = insert_begin; source_itr != insert_end; ++source_itr, ++target_itr)
|
||||||
|
new (target_itr) T(*source_itr);
|
||||||
|
|
||||||
|
// Move over the other half.
|
||||||
|
if (new_buffer != this->ptr || insert_begin != insert_end)
|
||||||
|
{
|
||||||
|
while (original_source_itr != this->end())
|
||||||
|
{
|
||||||
|
new (target_itr) T(std::move(*original_source_itr));
|
||||||
|
original_source_itr->~T();
|
||||||
|
++original_source_itr;
|
||||||
|
++target_itr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->ptr != stack_storage.data())
|
||||||
|
free(this->ptr);
|
||||||
|
this->ptr = new_buffer;
|
||||||
|
buffer_capacity = target_capacity;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Move in place, need to be a bit careful about which elements are constructed and which are not.
|
||||||
|
// Move the end and construct the new elements.
|
||||||
|
auto *target_itr = this->end() + count;
|
||||||
|
auto *source_itr = this->end();
|
||||||
|
while (target_itr != this->end() && source_itr != itr)
|
||||||
|
{
|
||||||
|
--target_itr;
|
||||||
|
--source_itr;
|
||||||
|
new (target_itr) T(std::move(*source_itr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// For already constructed elements we can move-assign.
|
||||||
|
std::move_backward(itr, source_itr, target_itr);
|
||||||
|
|
||||||
|
// For the inserts which go to already constructed elements, we can do a plain copy.
|
||||||
|
while (itr != this->end() && insert_begin != insert_end)
|
||||||
|
*itr++ = *insert_begin++;
|
||||||
|
|
||||||
|
// For inserts into newly allocated memory, we must copy-construct instead.
|
||||||
|
while (insert_begin != insert_end)
|
||||||
|
{
|
||||||
|
new (itr) T(*insert_begin);
|
||||||
|
++itr;
|
||||||
|
++insert_begin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->buffer_size += count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(T *itr, const T &value) SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
insert(itr, &value, &value + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
T *erase(T *itr) SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
std::move(itr + 1, this->end(), itr);
|
||||||
|
this->ptr[--this->buffer_size].~T();
|
||||||
|
return itr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase(T *start_erase, T *end_erase) SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
if (end_erase == this->end())
|
||||||
|
{
|
||||||
|
resize(size_t(start_erase - this->begin()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto new_size = this->buffer_size - (end_erase - start_erase);
|
||||||
|
std::move(end_erase, this->end(), start_erase);
|
||||||
|
resize(new_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize(size_t new_size) SPIRV_CROSS_NOEXCEPT
|
||||||
|
{
|
||||||
|
if (new_size < this->buffer_size)
|
||||||
|
{
|
||||||
|
for (size_t i = new_size; i < this->buffer_size; i++)
|
||||||
|
this->ptr[i].~T();
|
||||||
|
}
|
||||||
|
else if (new_size > this->buffer_size)
|
||||||
|
{
|
||||||
|
reserve(new_size);
|
||||||
|
for (size_t i = this->buffer_size; i < new_size; i++)
|
||||||
|
new (&this->ptr[i]) T();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->buffer_size = new_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t buffer_capacity = 0;
|
||||||
|
AlignedBuffer<T, N> stack_storage;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A vector without stack storage.
|
||||||
|
// Could also be a typedef-ed to std::vector,
|
||||||
|
// but might as well use the one we have.
|
||||||
|
template <typename T>
|
||||||
|
using Vector = SmallVector<T, 0>;
|
||||||
|
|
||||||
|
#else // SPIRV_CROSS_FORCE_STL_TYPES
|
||||||
|
|
||||||
|
template <typename T, size_t N = 8>
|
||||||
|
using SmallVector = std::vector<T>;
|
||||||
|
template <typename T>
|
||||||
|
using Vector = std::vector<T>;
|
||||||
|
template <typename T>
|
||||||
|
using VectorView = std::vector<T>;
|
||||||
|
|
||||||
|
#endif // SPIRV_CROSS_FORCE_STL_TYPES
|
||||||
|
|
||||||
|
// An object pool which we use for allocating IVariant-derived objects.
|
||||||
|
// We know we are going to allocate a bunch of objects of each type,
|
||||||
|
// so amortize the mallocs.
|
||||||
|
class ObjectPoolBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~ObjectPoolBase() = default;
|
||||||
|
virtual void deallocate_opaque(void *ptr) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class ObjectPool : public ObjectPoolBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ObjectPool(unsigned start_object_count_ = 16)
|
||||||
|
: start_object_count(start_object_count_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... P>
|
||||||
|
T *allocate(P &&... p)
|
||||||
|
{
|
||||||
|
if (vacants.empty())
|
||||||
|
{
|
||||||
|
unsigned num_objects = start_object_count << memory.size();
|
||||||
|
T *ptr = static_cast<T *>(malloc(num_objects * sizeof(T)));
|
||||||
|
if (!ptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < num_objects; i++)
|
||||||
|
vacants.push_back(&ptr[i]);
|
||||||
|
|
||||||
|
memory.emplace_back(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
T *ptr = vacants.back();
|
||||||
|
vacants.pop_back();
|
||||||
|
new (ptr) T(std::forward<P>(p)...);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(T *ptr)
|
||||||
|
{
|
||||||
|
ptr->~T();
|
||||||
|
vacants.push_back(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate_opaque(void *ptr) override
|
||||||
|
{
|
||||||
|
deallocate(static_cast<T *>(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
vacants.clear();
|
||||||
|
memory.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Vector<T *> vacants;
|
||||||
|
|
||||||
|
struct MallocDeleter
|
||||||
|
{
|
||||||
|
void operator()(T *ptr)
|
||||||
|
{
|
||||||
|
::free(ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SmallVector<std::unique_ptr<T, MallocDeleter>> memory;
|
||||||
|
unsigned start_object_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <size_t StackSize = 4096, size_t BlockSize = 4096>
|
||||||
|
class StringStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StringStream()
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
~StringStream()
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable copies and moves. Makes it easier to implement, and we don't need it.
|
||||||
|
StringStream(const StringStream &) = delete;
|
||||||
|
void operator=(const StringStream &) = delete;
|
||||||
|
|
||||||
|
template <typename T, typename std::enable_if<!std::is_floating_point<T>::value, int>::type = 0>
|
||||||
|
StringStream &operator<<(const T &t)
|
||||||
|
{
|
||||||
|
auto s = std::to_string(t);
|
||||||
|
append(s.data(), s.size());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only overload this to make float/double conversions ambiguous.
|
||||||
|
StringStream &operator<<(uint32_t v)
|
||||||
|
{
|
||||||
|
auto s = std::to_string(v);
|
||||||
|
append(s.data(), s.size());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringStream &operator<<(char c)
|
||||||
|
{
|
||||||
|
append(&c, 1);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringStream &operator<<(const std::string &s)
|
||||||
|
{
|
||||||
|
append(s.data(), s.size());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringStream &operator<<(const char *s)
|
||||||
|
{
|
||||||
|
append(s, strlen(s));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
StringStream &operator<<(const char (&s)[N])
|
||||||
|
{
|
||||||
|
append(s, strlen(s));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string str() const
|
||||||
|
{
|
||||||
|
std::string ret;
|
||||||
|
size_t target_size = 0;
|
||||||
|
for (auto &saved : saved_buffers)
|
||||||
|
target_size += saved.offset;
|
||||||
|
target_size += current_buffer.offset;
|
||||||
|
ret.reserve(target_size);
|
||||||
|
|
||||||
|
for (auto &saved : saved_buffers)
|
||||||
|
ret.insert(ret.end(), saved.buffer, saved.buffer + saved.offset);
|
||||||
|
ret.insert(ret.end(), current_buffer.buffer, current_buffer.buffer + current_buffer.offset);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
for (auto &saved : saved_buffers)
|
||||||
|
if (saved.buffer != stack_buffer)
|
||||||
|
free(saved.buffer);
|
||||||
|
if (current_buffer.buffer != stack_buffer)
|
||||||
|
free(current_buffer.buffer);
|
||||||
|
|
||||||
|
saved_buffers.clear();
|
||||||
|
current_buffer.buffer = stack_buffer;
|
||||||
|
current_buffer.offset = 0;
|
||||||
|
current_buffer.size = sizeof(stack_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Buffer
|
||||||
|
{
|
||||||
|
char *buffer = nullptr;
|
||||||
|
size_t offset = 0;
|
||||||
|
size_t size = 0;
|
||||||
|
};
|
||||||
|
Buffer current_buffer;
|
||||||
|
char stack_buffer[StackSize];
|
||||||
|
SmallVector<Buffer> saved_buffers;
|
||||||
|
|
||||||
|
void append(const char *s, size_t len)
|
||||||
|
{
|
||||||
|
size_t avail = current_buffer.size - current_buffer.offset;
|
||||||
|
if (avail < len)
|
||||||
|
{
|
||||||
|
if (avail > 0)
|
||||||
|
{
|
||||||
|
memcpy(current_buffer.buffer + current_buffer.offset, s, avail);
|
||||||
|
s += avail;
|
||||||
|
len -= avail;
|
||||||
|
current_buffer.offset += avail;
|
||||||
|
}
|
||||||
|
|
||||||
|
saved_buffers.push_back(current_buffer);
|
||||||
|
size_t target_size = len > BlockSize ? len : BlockSize;
|
||||||
|
current_buffer.buffer = static_cast<char *>(malloc(target_size));
|
||||||
|
if (!current_buffer.buffer)
|
||||||
|
SPIRV_CROSS_THROW("Out of memory.");
|
||||||
|
|
||||||
|
memcpy(current_buffer.buffer, s, len);
|
||||||
|
current_buffer.offset = len;
|
||||||
|
current_buffer.size = target_size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(current_buffer.buffer + current_buffer.offset, s, len);
|
||||||
|
current_buffer.offset += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015-2021 Arm Limited
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At your option, you may choose to accept this material under either:
|
||||||
|
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||||
|
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SPIRV_CROSS_ERROR_HANDLING
|
||||||
|
#define SPIRV_CROSS_ERROR_HANDLING
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string>
|
||||||
|
#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
|
||||||
|
#include <stdexcept>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SPIRV_CROSS_NAMESPACE_OVERRIDE
|
||||||
|
#define SPIRV_CROSS_NAMESPACE SPIRV_CROSS_NAMESPACE_OVERRIDE
|
||||||
|
#else
|
||||||
|
#define SPIRV_CROSS_NAMESPACE spirv_cross
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
{
|
||||||
|
#ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
|
||||||
|
#if !defined(_MSC_VER) || defined(__clang__)
|
||||||
|
[[noreturn]]
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
__declspec(noreturn)
|
||||||
|
#endif
|
||||||
|
inline void
|
||||||
|
report_and_abort(const std::string &msg)
|
||||||
|
{
|
||||||
|
#ifdef NDEBUG
|
||||||
|
(void)msg;
|
||||||
|
#else
|
||||||
|
fprintf(stderr, "There was a compiler error: %s\n", msg.c_str());
|
||||||
|
#endif
|
||||||
|
fflush(stderr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SPIRV_CROSS_THROW(x) report_and_abort(x)
|
||||||
|
#else
|
||||||
|
class CompilerError : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit CompilerError(const std::string &str)
|
||||||
|
: std::runtime_error(str)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SPIRV_CROSS_THROW(x) throw CompilerError(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// MSVC 2013 does not have noexcept. We need this for Variant to get move constructor to work correctly
|
||||||
|
// instead of copy constructor.
|
||||||
|
// MSVC 2013 ignores that move constructors cannot throw in std::vector, so just don't define it.
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||||
|
#define SPIRV_CROSS_NOEXCEPT
|
||||||
|
#else
|
||||||
|
#define SPIRV_CROSS_NOEXCEPT noexcept
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __cplusplus >= 201402l
|
||||||
|
#define SPIRV_CROSS_DEPRECATED(reason) [[deprecated(reason)]]
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#define SPIRV_CROSS_DEPRECATED(reason) __attribute__((deprecated))
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define SPIRV_CROSS_DEPRECATED(reason) __declspec(deprecated(reason))
|
||||||
|
#else
|
||||||
|
#define SPIRV_CROSS_DEPRECATED(reason)
|
||||||
|
#endif
|
||||||
|
} // namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
|
||||||
|
#endif
|
256
dep/spirv-cross/include/spirv-cross/spirv_cross_parsed_ir.hpp
Normal file
256
dep/spirv-cross/include/spirv-cross/spirv_cross_parsed_ir.hpp
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018-2021 Arm Limited
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At your option, you may choose to accept this material under either:
|
||||||
|
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||||
|
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SPIRV_CROSS_PARSED_IR_HPP
|
||||||
|
#define SPIRV_CROSS_PARSED_IR_HPP
|
||||||
|
|
||||||
|
#include "spirv_common.hpp"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
{
|
||||||
|
|
||||||
|
// This data structure holds all information needed to perform cross-compilation and reflection.
|
||||||
|
// It is the output of the Parser, but any implementation could create this structure.
|
||||||
|
// It is intentionally very "open" and struct-like with some helper functions to deal with decorations.
|
||||||
|
// Parser is the reference implementation of how this data structure should be filled in.
|
||||||
|
|
||||||
|
class ParsedIR
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// This must be destroyed after the "ids" vector.
|
||||||
|
std::unique_ptr<ObjectPoolGroup> pool_group;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ParsedIR();
|
||||||
|
|
||||||
|
// Due to custom allocations from object pools, we cannot use a default copy constructor.
|
||||||
|
ParsedIR(const ParsedIR &other);
|
||||||
|
ParsedIR &operator=(const ParsedIR &other);
|
||||||
|
|
||||||
|
// Moves are unproblematic, but we need to implement it anyways, since MSVC 2013 does not understand
|
||||||
|
// how to default-implement these.
|
||||||
|
ParsedIR(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT;
|
||||||
|
ParsedIR &operator=(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT;
|
||||||
|
|
||||||
|
// Resizes ids, meta and block_meta.
|
||||||
|
void set_id_bounds(uint32_t bounds);
|
||||||
|
|
||||||
|
// The raw SPIR-V, instructions and opcodes refer to this by offset + count.
|
||||||
|
std::vector<uint32_t> spirv;
|
||||||
|
|
||||||
|
// Holds various data structures which inherit from IVariant.
|
||||||
|
SmallVector<Variant> ids;
|
||||||
|
|
||||||
|
// Various meta data for IDs, decorations, names, etc.
|
||||||
|
std::unordered_map<ID, Meta> meta;
|
||||||
|
|
||||||
|
// Holds all IDs which have a certain type.
|
||||||
|
// This is needed so we can iterate through a specific kind of resource quickly,
|
||||||
|
// and in-order of module declaration.
|
||||||
|
SmallVector<ID> ids_for_type[TypeCount];
|
||||||
|
|
||||||
|
// Special purpose lists which contain a union of types.
|
||||||
|
// This is needed so we can declare specialization constants and structs in an interleaved fashion,
|
||||||
|
// among other things.
|
||||||
|
// Constants can be undef or of struct type, and struct array sizes can use specialization constants.
|
||||||
|
SmallVector<ID> ids_for_constant_undef_or_type;
|
||||||
|
SmallVector<ID> ids_for_constant_or_variable;
|
||||||
|
|
||||||
|
// We need to keep track of the width the Ops that contains a type for the
|
||||||
|
// OpSwitch instruction, since this one doesn't contains the type in the
|
||||||
|
// instruction itself. And in some case we need to cast the condition to
|
||||||
|
// wider types. We only need the width to do the branch fixup since the
|
||||||
|
// type check itself can be done at runtime
|
||||||
|
std::unordered_map<ID, uint32_t> load_type_width;
|
||||||
|
|
||||||
|
// Declared capabilities and extensions in the SPIR-V module.
|
||||||
|
// Not really used except for reflection at the moment.
|
||||||
|
SmallVector<spv::Capability> declared_capabilities;
|
||||||
|
SmallVector<std::string> declared_extensions;
|
||||||
|
|
||||||
|
// Meta data about blocks. The cross-compiler needs to query if a block is either of these types.
|
||||||
|
// It is a bitset as there can be more than one tag per block.
|
||||||
|
enum BlockMetaFlagBits
|
||||||
|
{
|
||||||
|
BLOCK_META_LOOP_HEADER_BIT = 1 << 0,
|
||||||
|
BLOCK_META_CONTINUE_BIT = 1 << 1,
|
||||||
|
BLOCK_META_LOOP_MERGE_BIT = 1 << 2,
|
||||||
|
BLOCK_META_SELECTION_MERGE_BIT = 1 << 3,
|
||||||
|
BLOCK_META_MULTISELECT_MERGE_BIT = 1 << 4
|
||||||
|
};
|
||||||
|
using BlockMetaFlags = uint8_t;
|
||||||
|
SmallVector<BlockMetaFlags> block_meta;
|
||||||
|
std::unordered_map<BlockID, BlockID> continue_block_to_loop_header;
|
||||||
|
|
||||||
|
// Normally, we'd stick SPIREntryPoint in ids array, but it conflicts with SPIRFunction.
|
||||||
|
// Entry points can therefore be seen as some sort of meta structure.
|
||||||
|
std::unordered_map<FunctionID, SPIREntryPoint> entry_points;
|
||||||
|
FunctionID default_entry_point = 0;
|
||||||
|
|
||||||
|
struct Source
|
||||||
|
{
|
||||||
|
uint32_t version = 0;
|
||||||
|
bool es = false;
|
||||||
|
bool known = false;
|
||||||
|
bool hlsl = false;
|
||||||
|
|
||||||
|
Source() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
Source source;
|
||||||
|
|
||||||
|
spv::AddressingModel addressing_model = spv::AddressingModelMax;
|
||||||
|
spv::MemoryModel memory_model = spv::MemoryModelMax;
|
||||||
|
|
||||||
|
// Decoration handling methods.
|
||||||
|
// Can be useful for simple "raw" reflection.
|
||||||
|
// However, most members are here because the Parser needs most of these,
|
||||||
|
// and might as well just have the whole suite of decoration/name handling in one place.
|
||||||
|
void set_name(ID id, const std::string &name);
|
||||||
|
const std::string &get_name(ID id) const;
|
||||||
|
void set_decoration(ID id, spv::Decoration decoration, uint32_t argument = 0);
|
||||||
|
void set_decoration_string(ID id, spv::Decoration decoration, const std::string &argument);
|
||||||
|
bool has_decoration(ID id, spv::Decoration decoration) const;
|
||||||
|
uint32_t get_decoration(ID id, spv::Decoration decoration) const;
|
||||||
|
const std::string &get_decoration_string(ID id, spv::Decoration decoration) const;
|
||||||
|
const Bitset &get_decoration_bitset(ID id) const;
|
||||||
|
void unset_decoration(ID id, spv::Decoration decoration);
|
||||||
|
|
||||||
|
// Decoration handling methods (for members of a struct).
|
||||||
|
void set_member_name(TypeID id, uint32_t index, const std::string &name);
|
||||||
|
const std::string &get_member_name(TypeID id, uint32_t index) const;
|
||||||
|
void set_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration, uint32_t argument = 0);
|
||||||
|
void set_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration,
|
||||||
|
const std::string &argument);
|
||||||
|
uint32_t get_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const;
|
||||||
|
const std::string &get_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration) const;
|
||||||
|
bool has_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const;
|
||||||
|
const Bitset &get_member_decoration_bitset(TypeID id, uint32_t index) const;
|
||||||
|
void unset_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration);
|
||||||
|
|
||||||
|
void mark_used_as_array_length(ID id);
|
||||||
|
uint32_t increase_bound_by(uint32_t count);
|
||||||
|
Bitset get_buffer_block_flags(const SPIRVariable &var) const;
|
||||||
|
Bitset get_buffer_block_type_flags(const SPIRType &type) const;
|
||||||
|
|
||||||
|
void add_typed_id(Types type, ID id);
|
||||||
|
void remove_typed_id(Types type, ID id);
|
||||||
|
|
||||||
|
class LoopLock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit LoopLock(uint32_t *counter);
|
||||||
|
LoopLock(const LoopLock &) = delete;
|
||||||
|
void operator=(const LoopLock &) = delete;
|
||||||
|
LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT;
|
||||||
|
LoopLock &operator=(LoopLock &&other) SPIRV_CROSS_NOEXCEPT;
|
||||||
|
~LoopLock();
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t *lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This must be held while iterating over a type ID array.
|
||||||
|
// It is undefined if someone calls set<>() while we're iterating over a data structure, so we must
|
||||||
|
// make sure that this case is avoided.
|
||||||
|
|
||||||
|
// If we have a hard lock, it is an error to call set<>(), and an exception is thrown.
|
||||||
|
// If we have a soft lock, we silently ignore any additions to the typed arrays.
|
||||||
|
// This should only be used for physical ID remapping where we need to create an ID, but we will never
|
||||||
|
// care about iterating over them.
|
||||||
|
LoopLock create_loop_hard_lock() const;
|
||||||
|
LoopLock create_loop_soft_lock() const;
|
||||||
|
|
||||||
|
template <typename T, typename Op>
|
||||||
|
void for_each_typed_id(const Op &op)
|
||||||
|
{
|
||||||
|
auto loop_lock = create_loop_hard_lock();
|
||||||
|
for (auto &id : ids_for_type[T::type])
|
||||||
|
{
|
||||||
|
if (ids[id].get_type() == static_cast<Types>(T::type))
|
||||||
|
op(id, get<T>(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Op>
|
||||||
|
void for_each_typed_id(const Op &op) const
|
||||||
|
{
|
||||||
|
auto loop_lock = create_loop_hard_lock();
|
||||||
|
for (auto &id : ids_for_type[T::type])
|
||||||
|
{
|
||||||
|
if (ids[id].get_type() == static_cast<Types>(T::type))
|
||||||
|
op(id, get<T>(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void reset_all_of_type()
|
||||||
|
{
|
||||||
|
reset_all_of_type(static_cast<Types>(T::type));
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_all_of_type(Types type);
|
||||||
|
|
||||||
|
Meta *find_meta(ID id);
|
||||||
|
const Meta *find_meta(ID id) const;
|
||||||
|
|
||||||
|
const std::string &get_empty_string() const
|
||||||
|
{
|
||||||
|
return empty_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set);
|
||||||
|
|
||||||
|
void fixup_reserved_names();
|
||||||
|
|
||||||
|
static void sanitize_underscores(std::string &str);
|
||||||
|
static void sanitize_identifier(std::string &str, bool member, bool allow_reserved_prefixes);
|
||||||
|
static bool is_globally_reserved_identifier(std::string &str, bool allow_reserved_prefixes);
|
||||||
|
|
||||||
|
uint32_t get_spirv_version() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename T>
|
||||||
|
T &get(uint32_t id)
|
||||||
|
{
|
||||||
|
return variant_get<T>(ids[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T &get(uint32_t id) const
|
||||||
|
{
|
||||||
|
return variant_get<T>(ids[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutable uint32_t loop_iteration_depth_hard = 0;
|
||||||
|
mutable uint32_t loop_iteration_depth_soft = 0;
|
||||||
|
std::string empty_string;
|
||||||
|
Bitset cleared_bitset;
|
||||||
|
|
||||||
|
std::unordered_set<uint32_t> meta_needing_name_fixup;
|
||||||
|
};
|
||||||
|
} // namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
|
||||||
|
#endif
|
37
dep/spirv-cross/include/spirv-cross/spirv_cross_util.hpp
Normal file
37
dep/spirv-cross/include/spirv-cross/spirv_cross_util.hpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015-2021 Arm Limited
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At your option, you may choose to accept this material under either:
|
||||||
|
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||||
|
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SPIRV_CROSS_UTIL_HPP
|
||||||
|
#define SPIRV_CROSS_UTIL_HPP
|
||||||
|
|
||||||
|
#include "spirv_cross.hpp"
|
||||||
|
|
||||||
|
namespace spirv_cross_util
|
||||||
|
{
|
||||||
|
void rename_interface_variable(SPIRV_CROSS_NAMESPACE::Compiler &compiler,
|
||||||
|
const SPIRV_CROSS_NAMESPACE::SmallVector<SPIRV_CROSS_NAMESPACE::Resource> &resources,
|
||||||
|
uint32_t location, const std::string &name);
|
||||||
|
void inherit_combined_sampler_bindings(SPIRV_CROSS_NAMESPACE::Compiler &compiler);
|
||||||
|
} // namespace spirv_cross_util
|
||||||
|
|
||||||
|
#endif
|
1033
dep/spirv-cross/include/spirv-cross/spirv_glsl.hpp
Normal file
1033
dep/spirv-cross/include/spirv-cross/spirv_glsl.hpp
Normal file
File diff suppressed because it is too large
Load diff
407
dep/spirv-cross/include/spirv-cross/spirv_hlsl.hpp
Normal file
407
dep/spirv-cross/include/spirv-cross/spirv_hlsl.hpp
Normal file
|
@ -0,0 +1,407 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016-2021 Robert Konrad
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At your option, you may choose to accept this material under either:
|
||||||
|
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||||
|
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SPIRV_HLSL_HPP
|
||||||
|
#define SPIRV_HLSL_HPP
|
||||||
|
|
||||||
|
#include "spirv_glsl.hpp"
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
{
|
||||||
|
// Interface which remaps vertex inputs to a fixed semantic name to make linking easier.
|
||||||
|
struct HLSLVertexAttributeRemap
|
||||||
|
{
|
||||||
|
uint32_t location;
|
||||||
|
std::string semantic;
|
||||||
|
};
|
||||||
|
// Specifying a root constant (d3d12) or push constant range (vulkan).
|
||||||
|
//
|
||||||
|
// `start` and `end` denotes the range of the root constant in bytes.
|
||||||
|
// Both values need to be multiple of 4.
|
||||||
|
struct RootConstants
|
||||||
|
{
|
||||||
|
uint32_t start;
|
||||||
|
uint32_t end;
|
||||||
|
|
||||||
|
uint32_t binding;
|
||||||
|
uint32_t space;
|
||||||
|
};
|
||||||
|
|
||||||
|
// For finer control, decorations may be removed from specific resources instead with unset_decoration().
|
||||||
|
enum HLSLBindingFlagBits
|
||||||
|
{
|
||||||
|
HLSL_BINDING_AUTO_NONE_BIT = 0,
|
||||||
|
|
||||||
|
// Push constant (root constant) resources will be declared as CBVs (b-space) without a register() declaration.
|
||||||
|
// A register will be automatically assigned by the D3D compiler, but must therefore be reflected in D3D-land.
|
||||||
|
// Push constants do not normally have a DecorationBinding set, but if they do, this can be used to ignore it.
|
||||||
|
HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT = 1 << 0,
|
||||||
|
|
||||||
|
// cbuffer resources will be declared as CBVs (b-space) without a register() declaration.
|
||||||
|
// A register will be automatically assigned, but must be reflected in D3D-land.
|
||||||
|
HLSL_BINDING_AUTO_CBV_BIT = 1 << 1,
|
||||||
|
|
||||||
|
// All SRVs (t-space) will be declared without a register() declaration.
|
||||||
|
HLSL_BINDING_AUTO_SRV_BIT = 1 << 2,
|
||||||
|
|
||||||
|
// All UAVs (u-space) will be declared without a register() declaration.
|
||||||
|
HLSL_BINDING_AUTO_UAV_BIT = 1 << 3,
|
||||||
|
|
||||||
|
// All samplers (s-space) will be declared without a register() declaration.
|
||||||
|
HLSL_BINDING_AUTO_SAMPLER_BIT = 1 << 4,
|
||||||
|
|
||||||
|
// No resources will be declared with register().
|
||||||
|
HLSL_BINDING_AUTO_ALL = 0x7fffffff
|
||||||
|
};
|
||||||
|
using HLSLBindingFlags = uint32_t;
|
||||||
|
|
||||||
|
// By matching stage, desc_set and binding for a SPIR-V resource,
|
||||||
|
// register bindings are set based on whether the HLSL resource is a
|
||||||
|
// CBV, UAV, SRV or Sampler. A single binding in SPIR-V might contain multiple
|
||||||
|
// resource types, e.g. COMBINED_IMAGE_SAMPLER, and SRV/Sampler bindings will be used respectively.
|
||||||
|
// On SM 5.0 and lower, register_space is ignored.
|
||||||
|
//
|
||||||
|
// To remap a push constant block which does not have any desc_set/binding associated with it,
|
||||||
|
// use ResourceBindingPushConstant{DescriptorSet,Binding} as values for desc_set/binding.
|
||||||
|
// For deeper control of push constants, set_root_constant_layouts() can be used instead.
|
||||||
|
struct HLSLResourceBinding
|
||||||
|
{
|
||||||
|
spv::ExecutionModel stage = spv::ExecutionModelMax;
|
||||||
|
uint32_t desc_set = 0;
|
||||||
|
uint32_t binding = 0;
|
||||||
|
|
||||||
|
struct Binding
|
||||||
|
{
|
||||||
|
uint32_t register_space = 0;
|
||||||
|
uint32_t register_binding = 0;
|
||||||
|
} cbv, uav, srv, sampler;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HLSLAuxBinding
|
||||||
|
{
|
||||||
|
HLSL_AUX_BINDING_BASE_VERTEX_INSTANCE = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
class CompilerHLSL : public CompilerGLSL
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Options
|
||||||
|
{
|
||||||
|
uint32_t shader_model = 30; // TODO: map ps_4_0_level_9_0,... somehow
|
||||||
|
|
||||||
|
// Allows the PointSize builtin in SM 4.0+, and ignores it, as PointSize is not supported in SM 4+.
|
||||||
|
bool point_size_compat = false;
|
||||||
|
|
||||||
|
// Allows the PointCoord builtin, returns float2(0.5, 0.5), as PointCoord is not supported in HLSL.
|
||||||
|
bool point_coord_compat = false;
|
||||||
|
|
||||||
|
// If true, the backend will assume that VertexIndex and InstanceIndex will need to apply
|
||||||
|
// a base offset, and you will need to fill in a cbuffer with offsets.
|
||||||
|
// Set to false if you know you will never use base instance or base vertex
|
||||||
|
// functionality as it might remove an internal cbuffer.
|
||||||
|
bool support_nonzero_base_vertex_base_instance = false;
|
||||||
|
|
||||||
|
// Forces a storage buffer to always be declared as UAV, even if the readonly decoration is used.
|
||||||
|
// By default, a readonly storage buffer will be declared as ByteAddressBuffer (SRV) instead.
|
||||||
|
// Alternatively, use set_hlsl_force_storage_buffer_as_uav to specify individually.
|
||||||
|
bool force_storage_buffer_as_uav = false;
|
||||||
|
|
||||||
|
// Forces any storage image type marked as NonWritable to be considered an SRV instead.
|
||||||
|
// For this to work with function call parameters, NonWritable must be considered to be part of the type system
|
||||||
|
// so that NonWritable image arguments are also translated to Texture rather than RWTexture.
|
||||||
|
bool nonwritable_uav_texture_as_srv = false;
|
||||||
|
|
||||||
|
// Enables native 16-bit types. Needs SM 6.2.
|
||||||
|
// Uses half/int16_t/uint16_t instead of min16* types.
|
||||||
|
// Also adds support for 16-bit load-store from (RW)ByteAddressBuffer.
|
||||||
|
bool enable_16bit_types = false;
|
||||||
|
|
||||||
|
// If matrices are used as IO variables, flatten the attribute declaration to use
|
||||||
|
// TEXCOORD{N,N+1,N+2,...} rather than TEXCOORDN_{0,1,2,3}.
|
||||||
|
// If add_vertex_attribute_remap is used and this feature is used,
|
||||||
|
// the semantic name will be queried once per active location.
|
||||||
|
bool flatten_matrix_vertex_input_semantics = false;
|
||||||
|
|
||||||
|
// Rather than emitting main() for the entry point, use the name in SPIR-V.
|
||||||
|
bool use_entry_point_name = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit CompilerHLSL(std::vector<uint32_t> spirv_)
|
||||||
|
: CompilerGLSL(std::move(spirv_))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilerHLSL(const uint32_t *ir_, size_t size)
|
||||||
|
: CompilerGLSL(ir_, size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit CompilerHLSL(const ParsedIR &ir_)
|
||||||
|
: CompilerGLSL(ir_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit CompilerHLSL(ParsedIR &&ir_)
|
||||||
|
: CompilerGLSL(std::move(ir_))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const Options &get_hlsl_options() const
|
||||||
|
{
|
||||||
|
return hlsl_options;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_hlsl_options(const Options &opts)
|
||||||
|
{
|
||||||
|
hlsl_options = opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionally specify a custom root constant layout.
|
||||||
|
//
|
||||||
|
// Push constants ranges will be split up according to the
|
||||||
|
// layout specified.
|
||||||
|
void set_root_constant_layouts(std::vector<RootConstants> layout);
|
||||||
|
|
||||||
|
// Compiles and remaps vertex attributes at specific locations to a fixed semantic.
|
||||||
|
// The default is TEXCOORD# where # denotes location.
|
||||||
|
// Matrices are unrolled to vectors with notation ${SEMANTIC}_#, where # denotes row.
|
||||||
|
// $SEMANTIC is either TEXCOORD# or a semantic name specified here.
|
||||||
|
void add_vertex_attribute_remap(const HLSLVertexAttributeRemap &vertex_attributes);
|
||||||
|
std::string compile() override;
|
||||||
|
|
||||||
|
// This is a special HLSL workaround for the NumWorkGroups builtin.
|
||||||
|
// This does not exist in HLSL, so the calling application must create a dummy cbuffer in
|
||||||
|
// which the application will store this builtin.
|
||||||
|
// The cbuffer layout will be:
|
||||||
|
// cbuffer SPIRV_Cross_NumWorkgroups : register(b#, space#) { uint3 SPIRV_Cross_NumWorkgroups_count; };
|
||||||
|
// This must be called before compile().
|
||||||
|
// The function returns 0 if NumWorkGroups builtin is not statically used in the shader from the current entry point.
|
||||||
|
// If non-zero, this returns the variable ID of a cbuffer which corresponds to
|
||||||
|
// the cbuffer declared above. By default, no binding or descriptor set decoration is set,
|
||||||
|
// so the calling application should declare explicit bindings on this ID before calling compile().
|
||||||
|
VariableID remap_num_workgroups_builtin();
|
||||||
|
|
||||||
|
// Controls how resource bindings are declared in the output HLSL.
|
||||||
|
void set_resource_binding_flags(HLSLBindingFlags flags);
|
||||||
|
|
||||||
|
// resource is a resource binding to indicate the HLSL CBV, SRV, UAV or sampler binding
|
||||||
|
// to use for a particular SPIR-V description set
|
||||||
|
// and binding. If resource bindings are provided,
|
||||||
|
// is_hlsl_resource_binding_used() will return true after calling ::compile() if
|
||||||
|
// the set/binding combination was used by the HLSL code.
|
||||||
|
void add_hlsl_resource_binding(const HLSLResourceBinding &resource);
|
||||||
|
bool is_hlsl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding) const;
|
||||||
|
|
||||||
|
// Controls which storage buffer bindings will be forced to be declared as UAVs.
|
||||||
|
void set_hlsl_force_storage_buffer_as_uav(uint32_t desc_set, uint32_t binding);
|
||||||
|
|
||||||
|
// By default, these magic buffers are not assigned a specific binding.
|
||||||
|
void set_hlsl_aux_buffer_binding(HLSLAuxBinding binding, uint32_t register_index, uint32_t register_space);
|
||||||
|
void unset_hlsl_aux_buffer_binding(HLSLAuxBinding binding);
|
||||||
|
bool is_hlsl_aux_buffer_binding_used(HLSLAuxBinding binding) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override;
|
||||||
|
std::string image_type_hlsl(const SPIRType &type, uint32_t id);
|
||||||
|
std::string image_type_hlsl_modern(const SPIRType &type, uint32_t id);
|
||||||
|
std::string image_type_hlsl_legacy(const SPIRType &type, uint32_t id);
|
||||||
|
void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override;
|
||||||
|
void emit_hlsl_entry_point();
|
||||||
|
void emit_header() override;
|
||||||
|
void emit_resources();
|
||||||
|
void emit_interface_block_globally(const SPIRVariable &type);
|
||||||
|
void emit_interface_block_in_struct(const SPIRVariable &var, std::unordered_set<uint32_t> &active_locations);
|
||||||
|
void emit_interface_block_member_in_struct(const SPIRVariable &var, uint32_t member_index, uint32_t location,
|
||||||
|
std::unordered_set<uint32_t> &active_locations);
|
||||||
|
void emit_builtin_inputs_in_struct();
|
||||||
|
void emit_builtin_outputs_in_struct();
|
||||||
|
void emit_builtin_primitive_outputs_in_struct();
|
||||||
|
void emit_texture_op(const Instruction &i, bool sparse) override;
|
||||||
|
void emit_instruction(const Instruction &instruction) override;
|
||||||
|
void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args,
|
||||||
|
uint32_t count) override;
|
||||||
|
void emit_buffer_block(const SPIRVariable &type) override;
|
||||||
|
void emit_push_constant_block(const SPIRVariable &var) override;
|
||||||
|
void emit_uniform(const SPIRVariable &var) override;
|
||||||
|
void emit_modern_uniform(const SPIRVariable &var);
|
||||||
|
void emit_legacy_uniform(const SPIRVariable &var);
|
||||||
|
void emit_specialization_constants_and_structs();
|
||||||
|
void emit_composite_constants();
|
||||||
|
void emit_fixup() override;
|
||||||
|
std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override;
|
||||||
|
std::string layout_for_member(const SPIRType &type, uint32_t index) override;
|
||||||
|
std::string to_interpolation_qualifiers(const Bitset &flags) override;
|
||||||
|
std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override;
|
||||||
|
bool emit_complex_bitcast(uint32_t result_type, uint32_t id, uint32_t op0) override;
|
||||||
|
std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) override;
|
||||||
|
std::string to_sampler_expression(uint32_t id);
|
||||||
|
std::string to_resource_binding(const SPIRVariable &var);
|
||||||
|
std::string to_resource_binding_sampler(const SPIRVariable &var);
|
||||||
|
std::string to_resource_register(HLSLBindingFlagBits flag, char space, uint32_t binding, uint32_t set);
|
||||||
|
std::string to_initializer_expression(const SPIRVariable &var) override;
|
||||||
|
void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override;
|
||||||
|
void emit_access_chain(const Instruction &instruction);
|
||||||
|
void emit_load(const Instruction &instruction);
|
||||||
|
void read_access_chain(std::string *expr, const std::string &lhs, const SPIRAccessChain &chain);
|
||||||
|
void read_access_chain_struct(const std::string &lhs, const SPIRAccessChain &chain);
|
||||||
|
void read_access_chain_array(const std::string &lhs, const SPIRAccessChain &chain);
|
||||||
|
void write_access_chain(const SPIRAccessChain &chain, uint32_t value, const SmallVector<uint32_t> &composite_chain);
|
||||||
|
void write_access_chain_struct(const SPIRAccessChain &chain, uint32_t value,
|
||||||
|
const SmallVector<uint32_t> &composite_chain);
|
||||||
|
void write_access_chain_array(const SPIRAccessChain &chain, uint32_t value,
|
||||||
|
const SmallVector<uint32_t> &composite_chain);
|
||||||
|
std::string write_access_chain_value(uint32_t value, const SmallVector<uint32_t> &composite_chain, bool enclose);
|
||||||
|
void emit_store(const Instruction &instruction);
|
||||||
|
void emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op);
|
||||||
|
void emit_subgroup_op(const Instruction &i) override;
|
||||||
|
void emit_block_hints(const SPIRBlock &block) override;
|
||||||
|
|
||||||
|
void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, const std::string &qualifier,
|
||||||
|
uint32_t base_offset = 0) override;
|
||||||
|
void emit_rayquery_function(const char *commited, const char *candidate, const uint32_t *ops);
|
||||||
|
void emit_mesh_tasks(SPIRBlock &block) override;
|
||||||
|
|
||||||
|
const char *to_storage_qualifiers_glsl(const SPIRVariable &var) override;
|
||||||
|
void replace_illegal_names() override;
|
||||||
|
|
||||||
|
bool is_hlsl_force_storage_buffer_as_uav(ID id) const;
|
||||||
|
|
||||||
|
Options hlsl_options;
|
||||||
|
|
||||||
|
// TODO: Refactor this to be more similar to MSL, maybe have some common system in place?
|
||||||
|
bool requires_op_fmod = false;
|
||||||
|
bool requires_fp16_packing = false;
|
||||||
|
bool requires_uint2_packing = false;
|
||||||
|
bool requires_explicit_fp16_packing = false;
|
||||||
|
bool requires_unorm8_packing = false;
|
||||||
|
bool requires_snorm8_packing = false;
|
||||||
|
bool requires_unorm16_packing = false;
|
||||||
|
bool requires_snorm16_packing = false;
|
||||||
|
bool requires_bitfield_insert = false;
|
||||||
|
bool requires_bitfield_extract = false;
|
||||||
|
bool requires_inverse_2x2 = false;
|
||||||
|
bool requires_inverse_3x3 = false;
|
||||||
|
bool requires_inverse_4x4 = false;
|
||||||
|
bool requires_scalar_reflect = false;
|
||||||
|
bool requires_scalar_refract = false;
|
||||||
|
bool requires_scalar_faceforward = false;
|
||||||
|
|
||||||
|
struct TextureSizeVariants
|
||||||
|
{
|
||||||
|
// MSVC 2013 workaround.
|
||||||
|
TextureSizeVariants()
|
||||||
|
{
|
||||||
|
srv = 0;
|
||||||
|
for (auto &unorm : uav)
|
||||||
|
for (auto &u : unorm)
|
||||||
|
u = 0;
|
||||||
|
}
|
||||||
|
uint64_t srv;
|
||||||
|
uint64_t uav[3][4];
|
||||||
|
} required_texture_size_variants;
|
||||||
|
|
||||||
|
void require_texture_query_variant(uint32_t var_id);
|
||||||
|
void emit_texture_size_variants(uint64_t variant_mask, const char *vecsize_qualifier, bool uav,
|
||||||
|
const char *type_qualifier);
|
||||||
|
|
||||||
|
enum TextureQueryVariantDim
|
||||||
|
{
|
||||||
|
Query1D = 0,
|
||||||
|
Query1DArray,
|
||||||
|
Query2D,
|
||||||
|
Query2DArray,
|
||||||
|
Query3D,
|
||||||
|
QueryBuffer,
|
||||||
|
QueryCube,
|
||||||
|
QueryCubeArray,
|
||||||
|
Query2DMS,
|
||||||
|
Query2DMSArray,
|
||||||
|
QueryDimCount
|
||||||
|
};
|
||||||
|
|
||||||
|
enum TextureQueryVariantType
|
||||||
|
{
|
||||||
|
QueryTypeFloat = 0,
|
||||||
|
QueryTypeInt = 16,
|
||||||
|
QueryTypeUInt = 32,
|
||||||
|
QueryTypeCount = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
enum BitcastType
|
||||||
|
{
|
||||||
|
TypeNormal,
|
||||||
|
TypePackUint2x32,
|
||||||
|
TypeUnpackUint64
|
||||||
|
};
|
||||||
|
|
||||||
|
void analyze_meshlet_writes();
|
||||||
|
void analyze_meshlet_writes(uint32_t func_id, uint32_t id_per_vertex, uint32_t id_per_primitive,
|
||||||
|
std::unordered_set<uint32_t> &processed_func_ids);
|
||||||
|
|
||||||
|
BitcastType get_bitcast_type(uint32_t result_type, uint32_t op0);
|
||||||
|
|
||||||
|
void emit_builtin_variables();
|
||||||
|
bool require_output = false;
|
||||||
|
bool require_input = false;
|
||||||
|
SmallVector<HLSLVertexAttributeRemap> remap_vertex_attributes;
|
||||||
|
|
||||||
|
uint32_t type_to_consumed_locations(const SPIRType &type) const;
|
||||||
|
|
||||||
|
std::string to_semantic(uint32_t location, spv::ExecutionModel em, spv::StorageClass sc);
|
||||||
|
|
||||||
|
uint32_t num_workgroups_builtin = 0;
|
||||||
|
HLSLBindingFlags resource_binding_flags = 0;
|
||||||
|
|
||||||
|
// Custom root constant layout, which should be emitted
|
||||||
|
// when translating push constant ranges.
|
||||||
|
std::vector<RootConstants> root_constants_layout;
|
||||||
|
|
||||||
|
void validate_shader_model();
|
||||||
|
|
||||||
|
std::string get_unique_identifier();
|
||||||
|
uint32_t unique_identifier_count = 0;
|
||||||
|
|
||||||
|
std::unordered_map<StageSetBinding, std::pair<HLSLResourceBinding, bool>, InternalHasher> resource_bindings;
|
||||||
|
void remap_hlsl_resource_binding(HLSLBindingFlagBits type, uint32_t &desc_set, uint32_t &binding);
|
||||||
|
|
||||||
|
std::unordered_set<SetBindingPair, InternalHasher> force_uav_buffer_bindings;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint32_t register_index = 0;
|
||||||
|
uint32_t register_space = 0;
|
||||||
|
bool explicit_binding = false;
|
||||||
|
bool used = false;
|
||||||
|
} base_vertex_info;
|
||||||
|
|
||||||
|
// Returns true for BuiltInSampleMask because gl_SampleMask[] is an array in SPIR-V, but SV_Coverage is a scalar in HLSL.
|
||||||
|
bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const override;
|
||||||
|
|
||||||
|
std::vector<TypeID> composite_selection_workaround_types;
|
||||||
|
|
||||||
|
std::string get_inner_entry_point_name() const;
|
||||||
|
};
|
||||||
|
} // namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
|
||||||
|
#endif
|
1297
dep/spirv-cross/include/spirv-cross/spirv_msl.hpp
Normal file
1297
dep/spirv-cross/include/spirv-cross/spirv_msl.hpp
Normal file
File diff suppressed because it is too large
Load diff
103
dep/spirv-cross/include/spirv-cross/spirv_parser.hpp
Normal file
103
dep/spirv-cross/include/spirv-cross/spirv_parser.hpp
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018-2021 Arm Limited
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At your option, you may choose to accept this material under either:
|
||||||
|
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||||
|
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SPIRV_CROSS_PARSER_HPP
|
||||||
|
#define SPIRV_CROSS_PARSER_HPP
|
||||||
|
|
||||||
|
#include "spirv_cross_parsed_ir.hpp"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
{
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Parser(const uint32_t *spirv_data, size_t word_count);
|
||||||
|
Parser(std::vector<uint32_t> spirv);
|
||||||
|
|
||||||
|
void parse();
|
||||||
|
|
||||||
|
ParsedIR &get_parsed_ir()
|
||||||
|
{
|
||||||
|
return ir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ParsedIR ir;
|
||||||
|
SPIRFunction *current_function = nullptr;
|
||||||
|
SPIRBlock *current_block = nullptr;
|
||||||
|
// For workarounds.
|
||||||
|
bool ignore_trailing_block_opcodes = false;
|
||||||
|
|
||||||
|
void parse(const Instruction &instr);
|
||||||
|
const uint32_t *stream(const Instruction &instr) const;
|
||||||
|
|
||||||
|
template <typename T, typename... P>
|
||||||
|
T &set(uint32_t id, P &&... args)
|
||||||
|
{
|
||||||
|
ir.add_typed_id(static_cast<Types>(T::type), id);
|
||||||
|
auto &var = variant_set<T>(ir.ids[id], std::forward<P>(args)...);
|
||||||
|
var.self = id;
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T &get(uint32_t id)
|
||||||
|
{
|
||||||
|
return variant_get<T>(ir.ids[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T *maybe_get(uint32_t id)
|
||||||
|
{
|
||||||
|
if (ir.ids[id].get_type() == static_cast<Types>(T::type))
|
||||||
|
return &get<T>(id);
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T &get(uint32_t id) const
|
||||||
|
{
|
||||||
|
return variant_get<T>(ir.ids[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T *maybe_get(uint32_t id) const
|
||||||
|
{
|
||||||
|
if (ir.ids[id].get_type() == T::type)
|
||||||
|
return &get<T>(id);
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This must be an ordered data structure so we always pick the same type aliases.
|
||||||
|
SmallVector<uint32_t> global_struct_cache;
|
||||||
|
SmallVector<std::pair<uint32_t, uint32_t>> forward_pointer_fixups;
|
||||||
|
|
||||||
|
bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const;
|
||||||
|
bool variable_storage_is_aliased(const SPIRVariable &v) const;
|
||||||
|
};
|
||||||
|
} // namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
|
||||||
|
#endif
|
91
dep/spirv-cross/include/spirv-cross/spirv_reflect.hpp
Normal file
91
dep/spirv-cross/include/spirv-cross/spirv_reflect.hpp
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018-2021 Bradley Austin Davis
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At your option, you may choose to accept this material under either:
|
||||||
|
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||||
|
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SPIRV_CROSS_REFLECT_HPP
|
||||||
|
#define SPIRV_CROSS_REFLECT_HPP
|
||||||
|
|
||||||
|
#include "spirv_glsl.hpp"
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace simple_json
|
||||||
|
{
|
||||||
|
class Stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
{
|
||||||
|
class CompilerReflection : public CompilerGLSL
|
||||||
|
{
|
||||||
|
using Parent = CompilerGLSL;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CompilerReflection(std::vector<uint32_t> spirv_)
|
||||||
|
: Parent(std::move(spirv_))
|
||||||
|
{
|
||||||
|
options.vulkan_semantics = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilerReflection(const uint32_t *ir_, size_t word_count)
|
||||||
|
: Parent(ir_, word_count)
|
||||||
|
{
|
||||||
|
options.vulkan_semantics = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit CompilerReflection(const ParsedIR &ir_)
|
||||||
|
: CompilerGLSL(ir_)
|
||||||
|
{
|
||||||
|
options.vulkan_semantics = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit CompilerReflection(ParsedIR &&ir_)
|
||||||
|
: CompilerGLSL(std::move(ir_))
|
||||||
|
{
|
||||||
|
options.vulkan_semantics = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_format(const std::string &format);
|
||||||
|
std::string compile() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::string execution_model_to_str(spv::ExecutionModel model);
|
||||||
|
|
||||||
|
void emit_entry_points();
|
||||||
|
void emit_types();
|
||||||
|
void emit_resources();
|
||||||
|
void emit_specialization_constants();
|
||||||
|
|
||||||
|
void emit_type(uint32_t type_id, bool &emitted_open_tag);
|
||||||
|
void emit_type_member(const SPIRType &type, uint32_t index);
|
||||||
|
void emit_type_member_qualifiers(const SPIRType &type, uint32_t index);
|
||||||
|
void emit_type_array(const SPIRType &type);
|
||||||
|
void emit_resources(const char *tag, const SmallVector<Resource> &resources);
|
||||||
|
bool type_is_reference(const SPIRType &type) const;
|
||||||
|
|
||||||
|
std::string to_member_name(const SPIRType &type, uint32_t index) const;
|
||||||
|
|
||||||
|
std::shared_ptr<simple_json::Stream> json_stream;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
|
||||||
|
#endif
|
430
dep/spirv-cross/src/spirv_cfg.cpp
Normal file
430
dep/spirv-cross/src/spirv_cfg.cpp
Normal file
|
@ -0,0 +1,430 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016-2021 Arm Limited
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At your option, you may choose to accept this material under either:
|
||||||
|
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||||
|
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "spirv_cfg.hpp"
|
||||||
|
#include "spirv_cross.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace SPIRV_CROSS_NAMESPACE
|
||||||
|
{
|
||||||
|
CFG::CFG(Compiler &compiler_, const SPIRFunction &func_)
|
||||||
|
: compiler(compiler_)
|
||||||
|
, func(func_)
|
||||||
|
{
|
||||||
|
build_post_order_visit_order();
|
||||||
|
build_immediate_dominators();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t CFG::find_common_dominator(uint32_t a, uint32_t b) const
|
||||||
|
{
|
||||||
|
while (a != b)
|
||||||
|
{
|
||||||
|
if (get_visit_order(a) < get_visit_order(b))
|
||||||
|
a = get_immediate_dominator(a);
|
||||||
|
else
|
||||||
|
b = get_immediate_dominator(b);
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFG::build_immediate_dominators()
|
||||||
|
{
|
||||||
|
// Traverse the post-order in reverse and build up the immediate dominator tree.
|
||||||
|
immediate_dominators.clear();
|
||||||
|
immediate_dominators[func.entry_block] = func.entry_block;
|
||||||
|
|
||||||
|
for (auto i = post_order.size(); i; i--)
|
||||||
|
{
|
||||||
|
uint32_t block = post_order[i - 1];
|
||||||
|
auto &pred = preceding_edges[block];
|
||||||
|
if (pred.empty()) // This is for the entry block, but we've already set up the dominators.
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (auto &edge : pred)
|
||||||
|
{
|
||||||
|
if (immediate_dominators[block])
|
||||||
|
{
|
||||||
|
assert(immediate_dominators[edge]);
|
||||||
|
immediate_dominators[block] = find_common_dominator(immediate_dominators[block], edge);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
immediate_dominators[block] = edge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFG::is_back_edge(uint32_t to) const
|
||||||
|
{
|
||||||
|
// We have a back edge if the visit order is set with the temporary magic value 0.
|
||||||
|
// Crossing edges will have already been recorded with a visit order.
|
||||||
|
auto itr = visit_order.find(to);
|
||||||
|
return itr != end(visit_order) && itr->second.get() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFG::has_visited_forward_edge(uint32_t to) const
|
||||||
|
{
|
||||||
|
// If > 0, we have visited the edge already, and this is not a back edge branch.
|
||||||
|
auto itr = visit_order.find(to);
|
||||||
|
return itr != end(visit_order) && itr->second.get() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFG::post_order_visit(uint32_t block_id)
|
||||||
|
{
|
||||||
|
// If we have already branched to this block (back edge), stop recursion.
|
||||||
|
// If our branches are back-edges, we do not record them.
|
||||||
|
// We have to record crossing edges however.
|
||||||
|
if (has_visited_forward_edge(block_id))
|
||||||
|
return true;
|
||||||
|
else if (is_back_edge(block_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Block back-edges from recursively revisiting ourselves.
|
||||||
|
visit_order[block_id].get() = 0;
|
||||||
|
|
||||||
|
auto &block = compiler.get<SPIRBlock>(block_id);
|
||||||
|
|
||||||
|
// If this is a loop header, add an implied branch to the merge target.
|
||||||
|
// This is needed to avoid annoying cases with do { ... } while(false) loops often generated by inliners.
|
||||||
|
// To the CFG, this is linear control flow, but we risk picking the do/while scope as our dominating block.
|
||||||
|
// This makes sure that if we are accessing a variable outside the do/while, we choose the loop header as dominator.
|
||||||
|
// We could use has_visited_forward_edge, but this break code-gen where the merge block is unreachable in the CFG.
|
||||||
|
|
||||||
|
// Make a point out of visiting merge target first. This is to make sure that post visit order outside the loop
|
||||||
|
// is lower than inside the loop, which is going to be key for some traversal algorithms like post-dominance analysis.
|
||||||
|
// For selection constructs true/false blocks will end up visiting the merge block directly and it works out fine,
|
||||||
|
// but for loops, only the header might end up actually branching to merge block.
|
||||||
|
if (block.merge == SPIRBlock::MergeLoop && post_order_visit(block.merge_block))
|
||||||
|
add_branch(block_id, block.merge_block);
|
||||||
|
|
||||||
|
// First visit our branch targets.
|
||||||
|
switch (block.terminator)
|
||||||
|
{
|
||||||
|
case SPIRBlock::Direct:
|
||||||
|
if (post_order_visit(block.next_block))
|
||||||
|
add_branch(block_id, block.next_block);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPIRBlock::Select:
|
||||||
|
if (post_order_visit(block.true_block))
|
||||||
|
add_branch(block_id, block.true_block);
|
||||||
|
if (post_order_visit(block.false_block))
|
||||||
|
add_branch(block_id, block.false_block);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPIRBlock::MultiSelect:
|
||||||
|
{
|
||||||
|
const auto &cases = compiler.get_case_list(block);
|
||||||
|
for (const auto &target : cases)
|
||||||
|
{
|
||||||
|
if (post_order_visit(target.block))
|
||||||
|
add_branch(block_id, target.block);
|
||||||
|
}
|
||||||
|
if (block.default_block && post_order_visit(block.default_block))
|
||||||
|
add_branch(block_id, block.default_block);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a selection merge, add an implied branch to the merge target.
|
||||||
|
// This is needed to avoid cases where an inner branch dominates the outer branch.
|
||||||
|
// This can happen if one of the branches exit early, e.g.:
|
||||||
|
// if (cond) { ...; break; } else { var = 100 } use_var(var);
|
||||||
|
// We can use the variable without a Phi since there is only one possible parent here.
|
||||||
|
// However, in this case, we need to hoist out the inner variable to outside the branch.
|
||||||
|
// Use same strategy as loops.
|
||||||
|
if (block.merge == SPIRBlock::MergeSelection && post_order_visit(block.next_block))
|
||||||
|
{
|
||||||
|
// If there is only one preceding edge to the merge block and it's not ourselves, we need a fixup.
|
||||||
|
// Add a fake branch so any dominator in either the if (), or else () block, or a lone case statement
|
||||||
|
// will be hoisted out to outside the selection merge.
|
||||||
|
// If size > 1, the variable will be automatically hoisted, so we should not mess with it.
|
||||||
|
// The exception here is switch blocks, where we can have multiple edges to merge block,
|
||||||
|
// all coming from same scope, so be more conservative in this case.
|
||||||
|
// Adding fake branches unconditionally breaks parameter preservation analysis,
|
||||||
|
// which looks at how variables are accessed through the CFG.
|
||||||
|
auto pred_itr = preceding_edges.find(block.next_block);
|
||||||
|
if (pred_itr != end(preceding_edges))
|
||||||
|
{
|
||||||
|
auto &pred = pred_itr->second;
|
||||||
|
auto succ_itr = succeeding_edges.find(block_id);
|
||||||
|
size_t num_succeeding_edges = 0;
|
||||||
|
if (succ_itr != end(succeeding_edges))
|
||||||
|
num_succeeding_edges = succ_itr->second.size();
|
||||||
|
|
||||||
|
if (block.terminator == SPIRBlock::MultiSelect && num_succeeding_edges == 1)
|
||||||
|
{
|
||||||
|
// Multiple branches can come from the same scope due to "break;", so we need to assume that all branches
|
||||||
|
// come from same case scope in worst case, even if there are multiple preceding edges.
|
||||||
|
// If we have more than one succeeding edge from the block header, it should be impossible
|
||||||
|
// to have a dominator be inside the block.
|
||||||
|
// Only case this can go wrong is if we have 2 or more edges from block header and
|
||||||
|
// 2 or more edges to merge block, and still have dominator be inside a case label.
|
||||||
|
if (!pred.empty())
|
||||||
|
add_branch(block_id, block.next_block);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (pred.size() == 1 && *pred.begin() != block_id)
|
||||||
|
add_branch(block_id, block.next_block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the merge block does not have any preceding edges, i.e. unreachable, hallucinate it.
|
||||||
|
// We're going to do code-gen for it, and domination analysis requires that we have at least one preceding edge.
|
||||||
|
add_branch(block_id, block.next_block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then visit ourselves. Start counting at one, to let 0 be a magic value for testing back vs. crossing edges.
|
||||||
|
visit_order[block_id].get() = ++visit_count;
|
||||||
|
post_order.push_back(block_id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFG::build_post_order_visit_order()
|
||||||
|
{
|
||||||
|
uint32_t block = func.entry_block;
|
||||||
|
visit_count = 0;
|
||||||
|
visit_order.clear();
|
||||||
|
post_order.clear();
|
||||||
|
post_order_visit(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFG::add_branch(uint32_t from, uint32_t to)
|
||||||
|
{
|
||||||
|
const auto add_unique = [](SmallVector<uint32_t> &l, uint32_t value) {
|
||||||
|
auto itr = find(begin(l), end(l), value);
|
||||||
|
if (itr == end(l))
|
||||||
|
l.push_back(value);
|
||||||
|
};
|
||||||
|
add_unique(preceding_edges[to], from);
|
||||||
|
add_unique(succeeding_edges[from], to);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t CFG::find_loop_dominator(uint32_t block_id) const
|
||||||
|
{
|
||||||
|
while (block_id != SPIRBlock::NoDominator)
|
||||||
|
{
|
||||||
|
auto itr = preceding_edges.find(block_id);
|
||||||
|
if (itr == end(preceding_edges))
|
||||||
|
return SPIRBlock::NoDominator;
|
||||||
|
if (itr->second.empty())
|
||||||
|
return SPIRBlock::NoDominator;
|
||||||
|
|
||||||
|
uint32_t pred_block_id = SPIRBlock::NoDominator;
|
||||||
|
bool ignore_loop_header = false;
|
||||||
|
|
||||||
|
// If we are a merge block, go directly to the header block.
|
||||||
|
// Only consider a loop dominator if we are branching from inside a block to a loop header.
|
||||||
|
// NOTE: In the CFG we forced an edge from header to merge block always to support variable scopes properly.
|
||||||
|
for (auto &pred : itr->second)
|
||||||
|
{
|
||||||
|
auto &pred_block = compiler.get<SPIRBlock>(pred);
|
||||||
|
if (pred_block.merge == SPIRBlock::MergeLoop && pred_block.merge_block == ID(block_id))
|
||||||
|
{
|
||||||
|
pred_block_id = pred;
|
||||||
|
ignore_loop_header = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (pred_block.merge == SPIRBlock::MergeSelection && pred_block.next_block == ID(block_id))
|
||||||
|
{
|
||||||
|
pred_block_id = pred;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No merge block means we can just pick any edge. Loop headers dominate the inner loop, so any path we
|
||||||
|
// take will lead there.
|
||||||
|
if (pred_block_id == SPIRBlock::NoDominator)
|
||||||
|
pred_block_id = itr->second.front();
|
||||||
|
|
||||||
|
block_id = pred_block_id;
|
||||||
|
|
||||||
|
if (!ignore_loop_header && block_id)
|
||||||
|
{
|
||||||
|
auto &block = compiler.get<SPIRBlock>(block_id);
|
||||||
|
if (block.merge == SPIRBlock::MergeLoop)
|
||||||
|
return block_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return block_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFG::node_terminates_control_flow_in_sub_graph(BlockID from, BlockID to) const
|
||||||
|
{
|
||||||
|
// Walk backwards, starting from "to" block.
|
||||||
|
// Only follow pred edges if they have a 1:1 relationship, or a merge relationship.
|
||||||
|
// If we cannot find a path to "from", we must assume that to is inside control flow in some way.
|
||||||
|
|
||||||
|
auto &from_block = compiler.get<SPIRBlock>(from);
|
||||||
|
BlockID ignore_block_id = 0;
|
||||||
|
if (from_block.merge == SPIRBlock::MergeLoop)
|
||||||
|
ignore_block_id = from_block.merge_block;
|
||||||
|
|
||||||
|
while (to != from)
|
||||||
|
{
|
||||||
|
auto pred_itr = preceding_edges.find(to);
|
||||||
|
if (pred_itr == end(preceding_edges))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DominatorBuilder builder(*this);
|
||||||
|
for (auto &edge : pred_itr->second)
|
||||||
|
builder.add_block(edge);
|
||||||
|
|
||||||
|
uint32_t dominator = builder.get_dominator();
|
||||||
|
if (dominator == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto &dom = compiler.get<SPIRBlock>(dominator);
|
||||||
|
|
||||||
|
bool true_path_ignore = false;
|
||||||
|
bool false_path_ignore = false;
|
||||||
|
|
||||||
|
bool merges_to_nothing = dom.merge == SPIRBlock::MergeNone ||
|
||||||
|
(dom.merge == SPIRBlock::MergeSelection && dom.next_block &&
|
||||||
|
compiler.get<SPIRBlock>(dom.next_block).terminator == SPIRBlock::Unreachable) ||
|
||||||
|
(dom.merge == SPIRBlock::MergeLoop && dom.merge_block &&
|
||||||
|
compiler.get<SPIRBlock>(dom.merge_block).terminator == SPIRBlock::Unreachable);
|
||||||
|
|
||||||
|
if (dom.self == from || merges_to_nothing)
|
||||||
|
{
|
||||||
|
// We can only ignore inner branchy paths if there is no merge,
|
||||||
|
// i.e. no code is generated afterwards. E.g. this allows us to elide continue:
|
||||||
|
// for (;;) { if (cond) { continue; } else { break; } }.
|
||||||
|
// Codegen here in SPIR-V will be something like either no merge if one path directly breaks, or
|
||||||
|
// we merge to Unreachable.
|
||||||
|
if (ignore_block_id && dom.terminator == SPIRBlock::Select)
|
||||||
|
{
|
||||||
|
auto &true_block = compiler.get<SPIRBlock>(dom.true_block);
|
||||||
|
auto &false_block = compiler.get<SPIRBlock>(dom.false_block);
|
||||||
|
auto &ignore_block = compiler.get<SPIRBlock>(ignore_block_id);
|
||||||
|
true_path_ignore = compiler.execution_is_branchless(true_block, ignore_block);
|
||||||
|
false_path_ignore = compiler.execution_is_branchless(false_block, ignore_block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cases where we allow traversal. This serves as a proxy for post-dominance in a loop body.
|
||||||
|
// TODO: Might want to do full post-dominance analysis, but it's a lot of churn for something like this ...
|
||||||
|
// - We're the merge block of a selection construct. Jump to header.
|
||||||
|
// - We're the merge block of a loop. Jump to header.
|
||||||
|
// - Direct branch. Trivial.
|
||||||
|
// - Allow cases inside a branch if the header cannot merge execution before loop exit.
|
||||||
|
if ((dom.merge == SPIRBlock::MergeSelection && dom.next_block == to) ||
|
||||||
|
(dom.merge == SPIRBlock::MergeLoop && dom.merge_block == to) ||
|
||||||
|
(dom.terminator == SPIRBlock::Direct && dom.next_block == to) ||
|
||||||
|
(dom.terminator == SPIRBlock::Select && dom.true_block == to && false_path_ignore) ||
|
||||||
|
(dom.terminator == SPIRBlock::Select && dom.false_block == to && true_path_ignore))
|
||||||
|
{
|
||||||
|
// Allow walking selection constructs if the other branch reaches out of a loop construct.
|
||||||
|
// It cannot be in-scope anymore.
|
||||||
|
to = dominator;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DominatorBuilder::DominatorBuilder(const CFG &cfg_)
|
||||||
|
: cfg(cfg_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void DominatorBuilder::add_block(uint32_t block)
|
||||||
|
{
|
||||||
|
if (!cfg.get_immediate_dominator(block))
|
||||||
|
{
|
||||||
|
// Unreachable block via the CFG, we will never emit this code anyways.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dominator)
|
||||||
|
{
|
||||||
|
dominator = block;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block != dominator)
|
||||||
|
dominator = cfg.find_common_dominator(block, dominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DominatorBuilder::lift_continue_block_dominator()
|
||||||
|
{
|
||||||
|
// It is possible for a continue block to be the dominator of a variable is only accessed inside the while block of a do-while loop.
|
||||||
|
// We cannot safely declare variables inside a continue block, so move any variable declared
|
||||||
|
// in a continue block to the entry block to simplify.
|
||||||
|
// It makes very little sense for a continue block to ever be a dominator, so fall back to the simplest
|
||||||
|
// solution.
|
||||||
|
|
||||||
|
if (!dominator)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto &block = cfg.get_compiler().get<SPIRBlock>(dominator);
|
||||||
|
auto post_order = cfg.get_visit_order(dominator);
|
||||||
|
|
||||||
|
// If we are branching to a block with a higher post-order traversal index (continue blocks), we have a problem
|
||||||
|
// since we cannot create sensible GLSL code for this, fallback to entry block.
|
||||||
|
bool back_edge_dominator = false;
|
||||||
|
switch (block.terminator)
|
||||||
|
{
|
||||||
|
case SPIRBlock::Direct:
|
||||||
|
if (cfg.get_visit_order(block.next_block) > post_order)
|
||||||
|
back_edge_dominator = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPIRBlock::Select:
|
||||||
|
if (cfg.get_visit_order(block.true_block) > post_order)
|
||||||
|
back_edge_dominator = true;
|
||||||
|
if (cfg.get_visit_order(block.false_block) > post_order)
|
||||||
|
back_edge_dominator = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPIRBlock::MultiSelect:
|
||||||
|
{
|
||||||
|
auto &cases = cfg.get_compiler().get_case_list(block);
|
||||||
|
for (auto &target : cases)
|
||||||
|
{
|
||||||
|
if (cfg.get_visit_order(target.block) > post_order)
|
||||||
|
back_edge_dominator = true;
|
||||||
|
}
|
||||||
|
if (block.default_block && cfg.get_visit_order(block.default_block) > post_order)
|
||||||
|
back_edge_dominator = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (back_edge_dominator)
|
||||||
|
dominator = cfg.get_function().entry_block;
|
||||||
|
}
|
||||||
|
} // namespace SPIRV_CROSS_NAMESPACE
|
553
dep/spirv-cross/src/spirv_cpp.cpp
Normal file
553
dep/spirv-cross/src/spirv_cpp.cpp
Normal file
|
@ -0,0 +1,553 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015-2021 Arm Limited
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At your option, you may choose to accept this material under either:
|
||||||
|
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||||
|
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "spirv_cpp.hpp"
|
||||||
|
|
||||||
|
using namespace spv;
|
||||||
|
using namespace SPIRV_CROSS_NAMESPACE;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
void CompilerCPP::emit_buffer_block(const SPIRVariable &var)
|
||||||
|
{
|
||||||
|
add_resource_name(var.self);
|
||||||
|
|
||||||
|
auto &type = get<SPIRType>(var.basetype);
|
||||||
|
auto instance_name = to_name(var.self);
|
||||||
|
|
||||||
|
uint32_t descriptor_set = ir.meta[var.self].decoration.set;
|
||||||
|
uint32_t binding = ir.meta[var.self].decoration.binding;
|
||||||
|
|
||||||
|
emit_block_struct(type);
|
||||||
|
auto buffer_name = to_name(type.self);
|
||||||
|
|
||||||
|
statement("internal::Resource<", buffer_name, type_to_array_glsl(type), "> ", instance_name, "__;");
|
||||||
|
statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
|
||||||
|
resource_registrations.push_back(
|
||||||
|
join("s.register_resource(", instance_name, "__", ", ", descriptor_set, ", ", binding, ");"));
|
||||||
|
statement("");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerCPP::emit_interface_block(const SPIRVariable &var)
|
||||||
|
{
|
||||||
|
add_resource_name(var.self);
|
||||||
|
|
||||||
|
auto &type = get<SPIRType>(var.basetype);
|
||||||
|
|
||||||
|
const char *qual = var.storage == StorageClassInput ? "StageInput" : "StageOutput";
|
||||||
|
const char *lowerqual = var.storage == StorageClassInput ? "stage_input" : "stage_output";
|
||||||
|
auto instance_name = to_name(var.self);
|
||||||
|
uint32_t location = ir.meta[var.self].decoration.location;
|
||||||
|
|
||||||
|
string buffer_name;
|
||||||
|
auto flags = ir.meta[type.self].decoration.decoration_flags;
|
||||||
|
if (flags.get(DecorationBlock))
|
||||||
|
{
|
||||||
|
emit_block_struct(type);
|
||||||
|
buffer_name = to_name(type.self);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
buffer_name = type_to_glsl(type);
|
||||||
|
|
||||||
|
statement("internal::", qual, "<", buffer_name, type_to_array_glsl(type), "> ", instance_name, "__;");
|
||||||
|
statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
|
||||||
|
resource_registrations.push_back(join("s.register_", lowerqual, "(", instance_name, "__", ", ", location, ");"));
|
||||||
|
statement("");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerCPP::emit_shared(const SPIRVariable &var)
|
||||||
|
{
|
||||||
|
add_resource_name(var.self);
|
||||||
|
|
||||||
|
auto instance_name = to_name(var.self);
|
||||||
|
statement(CompilerGLSL::variable_decl(var), ";");
|
||||||
|
statement_no_indent("#define ", instance_name, " __res->", instance_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerCPP::emit_uniform(const SPIRVariable &var)
|
||||||
|
{
|
||||||
|
add_resource_name(var.self);
|
||||||
|
|
||||||
|
auto &type = get<SPIRType>(var.basetype);
|
||||||
|
auto instance_name = to_name(var.self);
|
||||||
|
|
||||||
|
uint32_t descriptor_set = ir.meta[var.self].decoration.set;
|
||||||
|
uint32_t binding = ir.meta[var.self].decoration.binding;
|
||||||
|
uint32_t location = ir.meta[var.self].decoration.location;
|
||||||
|
|
||||||
|
string type_name = type_to_glsl(type);
|
||||||
|
remap_variable_type_name(type, instance_name, type_name);
|
||||||
|
|
||||||
|
if (type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage ||
|
||||||
|
type.basetype == SPIRType::AtomicCounter)
|
||||||
|
{
|
||||||
|
statement("internal::Resource<", type_name, type_to_array_glsl(type), "> ", instance_name, "__;");
|
||||||
|
statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
|
||||||
|
resource_registrations.push_back(
|
||||||
|
join("s.register_resource(", instance_name, "__", ", ", descriptor_set, ", ", binding, ");"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
statement("internal::UniformConstant<", type_name, type_to_array_glsl(type), "> ", instance_name, "__;");
|
||||||
|
statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
|
||||||
|
resource_registrations.push_back(
|
||||||
|
join("s.register_uniform_constant(", instance_name, "__", ", ", location, ");"));
|
||||||
|
}
|
||||||
|
|
||||||
|
statement("");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerCPP::emit_push_constant_block(const SPIRVariable &var)
|
||||||
|
{
|
||||||
|
add_resource_name(var.self);
|
||||||
|
|
||||||
|
auto &type = get<SPIRType>(var.basetype);
|
||||||
|
auto &flags = ir.meta[var.self].decoration.decoration_flags;
|
||||||
|
if (flags.get(DecorationBinding) || flags.get(DecorationDescriptorSet))
|
||||||
|
SPIRV_CROSS_THROW("Push constant blocks cannot be compiled to GLSL with Binding or Set syntax. "
|
||||||
|
"Remap to location with reflection API first or disable these decorations.");
|
||||||
|
|
||||||
|
emit_block_struct(type);
|
||||||
|
auto buffer_name = to_name(type.self);
|
||||||
|
auto instance_name = to_name(var.self);
|
||||||
|
|
||||||
|
statement("internal::PushConstant<", buffer_name, type_to_array_glsl(type), "> ", instance_name, ";");
|
||||||
|
statement_no_indent("#define ", instance_name, " __res->", instance_name, ".get()");
|
||||||
|
resource_registrations.push_back(join("s.register_push_constant(", instance_name, "__", ");"));
|
||||||
|
statement("");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerCPP::emit_block_struct(SPIRType &type)
|
||||||
|
{
|
||||||
|
// C++ can't do interface blocks, so we fake it by emitting a separate struct.
|
||||||
|
// However, these structs are not allowed to alias anything, so remove it before
|
||||||
|
// emitting the struct.
|
||||||
|
//
|
||||||
|
// The type we have here needs to be resolved to the non-pointer type so we can remove aliases.
|
||||||
|
auto &self = get<SPIRType>(type.self);
|
||||||
|
self.type_alias = 0;
|
||||||
|
emit_struct(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerCPP::emit_resources()
|
||||||
|
{
|
||||||
|
for (auto &id : ir.ids)
|
||||||
|
{
|
||||||
|
if (id.get_type() == TypeConstant)
|
||||||
|
{
|
||||||
|
auto &c = id.get<SPIRConstant>();
|
||||||
|
|
||||||
|
bool needs_declaration = c.specialization || c.is_used_as_lut;
|
||||||
|
|
||||||
|
if (needs_declaration)
|
||||||
|
{
|
||||||
|
if (!options.vulkan_semantics && c.specialization)
|
||||||
|
{
|
||||||
|
c.specialization_constant_macro_name =
|
||||||
|
constant_value_macro_name(get_decoration(c.self, DecorationSpecId));
|
||||||
|
}
|
||||||
|
emit_constant(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (id.get_type() == TypeConstantOp)
|
||||||
|
{
|
||||||
|
emit_specialization_constant_op(id.get<SPIRConstantOp>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output all basic struct types which are not Block or BufferBlock as these are declared inplace
|
||||||
|
// when such variables are instantiated.
|
||||||
|
for (auto &id : ir.ids)
|
||||||
|
{
|
||||||
|
if (id.get_type() == TypeType)
|
||||||
|
{
|
||||||
|
auto &type = id.get<SPIRType>();
|
||||||
|
if (type.basetype == SPIRType::Struct && type.array.empty() && !type.pointer &&
|
||||||
|
(!ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) &&
|
||||||
|
!ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock)))
|
||||||
|
{
|
||||||
|
emit_struct(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statement("struct Resources : ", resource_type);
|
||||||
|
begin_scope();
|
||||||
|
|
||||||
|
// Output UBOs and SSBOs
|
||||||
|
for (auto &id : ir.ids)
|
||||||
|
{
|
||||||
|
if (id.get_type() == TypeVariable)
|
||||||
|
{
|
||||||
|
auto &var = id.get<SPIRVariable>();
|
||||||
|
auto &type = get<SPIRType>(var.basetype);
|
||||||
|
|
||||||
|
if (var.storage != StorageClassFunction && type.pointer && type.storage == StorageClassUniform &&
|
||||||
|
!is_hidden_variable(var) &&
|
||||||
|
(ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) ||
|
||||||
|
ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock)))
|
||||||
|
{
|
||||||
|
emit_buffer_block(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output push constant blocks
|
||||||
|
for (auto &id : ir.ids)
|
||||||
|
{
|
||||||
|
if (id.get_type() == TypeVariable)
|
||||||
|
{
|
||||||
|
auto &var = id.get<SPIRVariable>();
|
||||||
|
auto &type = get<SPIRType>(var.basetype);
|
||||||
|
if (!is_hidden_variable(var) && var.storage != StorageClassFunction && type.pointer &&
|
||||||
|
type.storage == StorageClassPushConstant)
|
||||||
|
{
|
||||||
|
emit_push_constant_block(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output in/out interfaces.
|
||||||
|
for (auto &id : ir.ids)
|
||||||
|
{
|
||||||
|
if (id.get_type() == TypeVariable)
|
||||||
|
{
|
||||||
|
auto &var = id.get<SPIRVariable>();
|
||||||
|
auto &type = get<SPIRType>(var.basetype);
|
||||||
|
|
||||||
|
if (var.storage != StorageClassFunction && !is_hidden_variable(var) && type.pointer &&
|
||||||
|
(var.storage == StorageClassInput || var.storage == StorageClassOutput) &&
|
||||||
|
interface_variable_exists_in_entry_point(var.self))
|
||||||
|
{
|
||||||
|
emit_interface_block(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output Uniform Constants (values, samplers, images, etc).
|
||||||
|
for (auto &id : ir.ids)
|
||||||
|
{
|
||||||
|
if (id.get_type() == TypeVariable)
|
||||||
|
{
|
||||||
|
auto &var = id.get<SPIRVariable>();
|
||||||
|
auto &type = get<SPIRType>(var.basetype);
|
||||||
|
|
||||||
|
if (var.storage != StorageClassFunction && !is_hidden_variable(var) && type.pointer &&
|
||||||
|
(type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter))
|
||||||
|
{
|
||||||
|
emit_uniform(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global variables.
|
||||||
|
bool emitted = false;
|
||||||
|
for (auto global : global_variables)
|
||||||
|
{
|
||||||
|
auto &var = get<SPIRVariable>(global);
|
||||||
|
if (var.storage == StorageClassWorkgroup)
|
||||||
|
{
|
||||||
|
emit_shared(var);
|
||||||
|
emitted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emitted)
|
||||||
|
statement("");
|
||||||
|
|
||||||
|
statement("inline void init(spirv_cross_shader& s)");
|
||||||
|
begin_scope();
|
||||||
|
statement(resource_type, "::init(s);");
|
||||||
|
for (auto ® : resource_registrations)
|
||||||
|
statement(reg);
|
||||||
|
end_scope();
|
||||||
|
resource_registrations.clear();
|
||||||
|
|
||||||
|
end_scope_decl();
|
||||||
|
|
||||||
|
statement("");
|
||||||
|
statement("Resources* __res;");
|
||||||
|
if (get_entry_point().model == ExecutionModelGLCompute)
|
||||||
|
statement("ComputePrivateResources __priv_res;");
|
||||||
|
statement("");
|
||||||
|
|
||||||
|
// Emit regular globals which are allocated per invocation.
|
||||||
|
emitted = false;
|
||||||
|
for (auto global : global_variables)
|
||||||
|
{
|
||||||
|
auto &var = get<SPIRVariable>(global);
|
||||||
|
if (var.storage == StorageClassPrivate)
|
||||||
|
{
|
||||||
|
if (var.storage == StorageClassWorkgroup)
|
||||||
|
emit_shared(var);
|
||||||
|
else
|
||||||
|
statement(CompilerGLSL::variable_decl(var), ";");
|
||||||
|
emitted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emitted)
|
||||||
|
statement("");
|
||||||
|
}
|
||||||
|
|
||||||
|
string CompilerCPP::compile()
|
||||||
|
{
|
||||||
|
ir.fixup_reserved_names();
|
||||||
|
|
||||||
|
// Do not deal with ES-isms like precision, older extensions and such.
|
||||||
|
options.es = false;
|
||||||
|
options.version = 450;
|
||||||
|
backend.float_literal_suffix = true;
|
||||||
|
backend.double_literal_suffix = false;
|
||||||
|
backend.long_long_literal_suffix = true;
|
||||||
|
backend.uint32_t_literal_suffix = true;
|
||||||
|
backend.basic_int_type = "int32_t";
|
||||||
|
backend.basic_uint_type = "uint32_t";
|
||||||
|
backend.swizzle_is_function = true;
|
||||||
|
backend.shared_is_implied = true;
|
||||||
|
backend.unsized_array_supported = false;
|
||||||
|
backend.explicit_struct_type = true;
|
||||||
|
backend.use_initializer_list = true;
|
||||||
|
|
||||||
|
fixup_type_alias();
|
||||||
|
reorder_type_alias();
|
||||||
|
build_function_control_flow_graphs_and_analyze();
|
||||||
|
update_active_builtins();
|
||||||
|
|
||||||
|
uint32_t pass_count = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
resource_registrations.clear();
|
||||||
|
reset(pass_count);
|
||||||
|
|
||||||
|
// Move constructor for this type is broken on GCC 4.9 ...
|
||||||
|
buffer.reset();
|
||||||
|
|
||||||
|
emit_header();
|
||||||
|
emit_resources();
|
||||||
|
|
||||||
|
emit_function(get<SPIRFunction>(ir.default_entry_point), Bitset());
|
||||||
|
|
||||||
|
pass_count++;
|
||||||
|
} while (is_forcing_recompilation());
|
||||||
|
|
||||||
|
// Match opening scope of emit_header().
|
||||||
|
end_scope_decl();
|
||||||
|
// namespace
|
||||||
|
end_scope();
|
||||||
|
|
||||||
|
// Emit C entry points
|
||||||
|
emit_c_linkage();
|
||||||
|
|
||||||
|
// Entry point in CPP is always main() for the time being.
|
||||||
|
get_entry_point().name = "main";
|
||||||
|
|
||||||
|
return buffer.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerCPP::emit_c_linkage()
|
||||||
|
{
|
||||||
|
statement("");
|
||||||
|
|
||||||
|
statement("spirv_cross_shader_t *spirv_cross_construct(void)");
|
||||||
|
begin_scope();
|
||||||
|
statement("return new ", impl_type, "();");
|
||||||
|
end_scope();
|
||||||
|
|
||||||
|
statement("");
|
||||||
|
statement("void spirv_cross_destruct(spirv_cross_shader_t *shader)");
|
||||||
|
begin_scope();
|
||||||
|
statement("delete static_cast<", impl_type, "*>(shader);");
|
||||||
|
end_scope();
|
||||||
|
|
||||||
|
statement("");
|
||||||
|
statement("void spirv_cross_invoke(spirv_cross_shader_t *shader)");
|
||||||
|
begin_scope();
|
||||||
|
statement("static_cast<", impl_type, "*>(shader)->invoke();");
|
||||||
|
end_scope();
|
||||||
|
|
||||||
|
statement("");
|
||||||
|
statement("static const struct spirv_cross_interface vtable =");
|
||||||
|
begin_scope();
|
||||||
|
statement("spirv_cross_construct,");
|
||||||
|
statement("spirv_cross_destruct,");
|
||||||
|
statement("spirv_cross_invoke,");
|
||||||
|
end_scope_decl();
|
||||||
|
|
||||||
|
statement("");
|
||||||
|
statement("const struct spirv_cross_interface *",
|
||||||
|
interface_name.empty() ? string("spirv_cross_get_interface") : interface_name, "(void)");
|
||||||
|
begin_scope();
|
||||||
|
statement("return &vtable;");
|
||||||
|
end_scope();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerCPP::emit_function_prototype(SPIRFunction &func, const Bitset &)
|
||||||
|
{
|
||||||
|
if (func.self != ir.default_entry_point)
|
||||||
|
add_function_overload(func);
|
||||||
|
|
||||||
|
local_variable_names = resource_names;
|
||||||
|
string decl;
|
||||||
|
|
||||||
|
auto &type = get<SPIRType>(func.return_type);
|
||||||
|
decl += "inline ";
|
||||||
|
decl += type_to_glsl(type);
|
||||||
|
decl += " ";
|
||||||
|
|
||||||
|
if (func.self == ir.default_entry_point)
|
||||||
|
{
|
||||||
|
decl += "main";
|
||||||
|
processing_entry_point = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
decl += to_name(func.self);
|
||||||
|
|
||||||
|
decl += "(";
|
||||||
|
for (auto &arg : func.arguments)
|
||||||
|
{
|
||||||
|
add_local_variable_name(arg.id);
|
||||||
|
|
||||||
|
decl += argument_decl(arg);
|
||||||
|
if (&arg != &func.arguments.back())
|
||||||
|
decl += ", ";
|
||||||
|
|
||||||
|
// Hold a pointer to the parameter so we can invalidate the readonly field if needed.
|
||||||
|
auto *var = maybe_get<SPIRVariable>(arg.id);
|
||||||
|
if (var)
|
||||||
|
var->parameter = &arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
decl += ")";
|
||||||
|
statement(decl);
|
||||||
|
}
|
||||||
|
|
||||||
|
string CompilerCPP::argument_decl(const SPIRFunction::Parameter &arg)
|
||||||
|
{
|
||||||
|
auto &type = expression_type(arg.id);
|
||||||
|
bool constref = !type.pointer || arg.write_count == 0;
|
||||||
|
|
||||||
|
auto &var = get<SPIRVariable>(arg.id);
|
||||||
|
|
||||||
|
string base = type_to_glsl(type);
|
||||||
|
string variable_name = to_name(var.self);
|
||||||
|
remap_variable_type_name(type, variable_name, base);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < type.array.size(); i++)
|
||||||
|
base = join("std::array<", base, ", ", to_array_size(type, i), ">");
|
||||||
|
|
||||||
|
return join(constref ? "const " : "", base, " &", variable_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
string CompilerCPP::variable_decl(const SPIRType &type, const string &name, uint32_t /* id */)
|
||||||
|
{
|
||||||
|
string base = type_to_glsl(type);
|
||||||
|
remap_variable_type_name(type, name, base);
|
||||||
|
bool runtime = false;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < type.array.size(); i++)
|
||||||
|
{
|
||||||
|
auto &array = type.array[i];
|
||||||
|
if (!array && type.array_size_literal[i])
|
||||||
|
{
|
||||||
|
// Avoid using runtime arrays with std::array since this is undefined.
|
||||||
|
// Runtime arrays cannot be passed around as values, so this is fine.
|
||||||
|
runtime = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
base = join("std::array<", base, ", ", to_array_size(type, i), ">");
|
||||||
|
}
|
||||||
|
base += ' ';
|
||||||
|
return base + name + (runtime ? "[1]" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerCPP::emit_header()
|
||||||
|
{
|
||||||
|
auto &execution = get_entry_point();
|
||||||
|
|
||||||
|
statement("// This C++ shader is autogenerated by spirv-cross.");
|
||||||
|
statement("#include \"spirv_cross/internal_interface.hpp\"");
|
||||||
|
statement("#include \"spirv_cross/external_interface.h\"");
|
||||||
|
// Needed to properly implement GLSL-style arrays.
|
||||||
|
statement("#include <array>");
|
||||||
|
statement("#include <stdint.h>");
|
||||||
|
statement("");
|
||||||
|
statement("using namespace spirv_cross;");
|
||||||
|
statement("using namespace glm;");
|
||||||
|
statement("");
|
||||||
|
|
||||||
|
statement("namespace Impl");
|
||||||
|
begin_scope();
|
||||||
|
|
||||||
|
switch (execution.model)
|
||||||
|
{
|
||||||
|
case ExecutionModelGeometry:
|
||||||
|
case ExecutionModelTessellationControl:
|
||||||
|
case ExecutionModelTessellationEvaluation:
|
||||||
|
case ExecutionModelGLCompute:
|
||||||
|
case ExecutionModelFragment:
|
||||||
|
case ExecutionModelVertex:
|
||||||
|
statement("struct Shader");
|
||||||
|
begin_scope();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
SPIRV_CROSS_THROW("Unsupported execution model.");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (execution.model)
|
||||||
|
{
|
||||||
|
case ExecutionModelGeometry:
|
||||||
|
impl_type = "GeometryShader<Impl::Shader, Impl::Shader::Resources>";
|
||||||
|
resource_type = "GeometryResources";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExecutionModelVertex:
|
||||||
|
impl_type = "VertexShader<Impl::Shader, Impl::Shader::Resources>";
|
||||||
|
resource_type = "VertexResources";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExecutionModelFragment:
|
||||||
|
impl_type = "FragmentShader<Impl::Shader, Impl::Shader::Resources>";
|
||||||
|
resource_type = "FragmentResources";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExecutionModelGLCompute:
|
||||||
|
impl_type = join("ComputeShader<Impl::Shader, Impl::Shader::Resources, ", execution.workgroup_size.x, ", ",
|
||||||
|
execution.workgroup_size.y, ", ", execution.workgroup_size.z, ">");
|
||||||
|
resource_type = "ComputeResources";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExecutionModelTessellationControl:
|
||||||
|
impl_type = "TessControlShader<Impl::Shader, Impl::Shader::Resources>";
|
||||||
|
resource_type = "TessControlResources";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExecutionModelTessellationEvaluation:
|
||||||
|
impl_type = "TessEvaluationShader<Impl::Shader, Impl::Shader::Resources>";
|
||||||
|
resource_type = "TessEvaluationResources";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
SPIRV_CROSS_THROW("Unsupported execution model.");
|
||||||
|
}
|
||||||
|
}
|
5511
dep/spirv-cross/src/spirv_cross.cpp
Normal file
5511
dep/spirv-cross/src/spirv_cross.cpp
Normal file
File diff suppressed because it is too large
Load diff
1074
dep/spirv-cross/src/spirv_cross_parsed_ir.cpp
Normal file
1074
dep/spirv-cross/src/spirv_cross_parsed_ir.cpp
Normal file
File diff suppressed because it is too large
Load diff
77
dep/spirv-cross/src/spirv_cross_util.cpp
Normal file
77
dep/spirv-cross/src/spirv_cross_util.cpp
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015-2021 Arm Limited
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At your option, you may choose to accept this material under either:
|
||||||
|
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||||
|
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "spirv_cross_util.hpp"
|
||||||
|
#include "spirv_common.hpp"
|
||||||
|
|
||||||
|
using namespace spv;
|
||||||
|
using namespace SPIRV_CROSS_NAMESPACE;
|
||||||
|
|
||||||
|
namespace spirv_cross_util
|
||||||
|
{
|
||||||
|
void rename_interface_variable(Compiler &compiler, const SmallVector<Resource> &resources, uint32_t location,
|
||||||
|
const std::string &name)
|
||||||
|
{
|
||||||
|
for (auto &v : resources)
|
||||||
|
{
|
||||||
|
if (!compiler.has_decoration(v.id, spv::DecorationLocation))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto loc = compiler.get_decoration(v.id, spv::DecorationLocation);
|
||||||
|
if (loc != location)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto &type = compiler.get_type(v.base_type_id);
|
||||||
|
|
||||||
|
// This is more of a friendly variant. If we need to rename interface variables, we might have to rename
|
||||||
|
// structs as well and make sure all the names match up.
|
||||||
|
if (type.basetype == SPIRType::Struct)
|
||||||
|
{
|
||||||
|
compiler.set_name(v.base_type_id, join("SPIRV_Cross_Interface_Location", location));
|
||||||
|
for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
|
||||||
|
compiler.set_member_name(v.base_type_id, i, join("InterfaceMember", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
compiler.set_name(v.id, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void inherit_combined_sampler_bindings(Compiler &compiler)
|
||||||
|
{
|
||||||
|
auto &samplers = compiler.get_combined_image_samplers();
|
||||||
|
for (auto &s : samplers)
|
||||||
|
{
|
||||||
|
if (compiler.has_decoration(s.image_id, spv::DecorationDescriptorSet))
|
||||||
|
{
|
||||||
|
uint32_t set = compiler.get_decoration(s.image_id, spv::DecorationDescriptorSet);
|
||||||
|
compiler.set_decoration(s.combined_id, spv::DecorationDescriptorSet, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compiler.has_decoration(s.image_id, spv::DecorationBinding))
|
||||||
|
{
|
||||||
|
uint32_t binding = compiler.get_decoration(s.image_id, spv::DecorationBinding);
|
||||||
|
compiler.set_decoration(s.combined_id, spv::DecorationBinding, binding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace spirv_cross_util
|
18386
dep/spirv-cross/src/spirv_glsl.cpp
Normal file
18386
dep/spirv-cross/src/spirv_glsl.cpp
Normal file
File diff suppressed because it is too large
Load diff
6695
dep/spirv-cross/src/spirv_hlsl.cpp
Normal file
6695
dep/spirv-cross/src/spirv_hlsl.cpp
Normal file
File diff suppressed because it is too large
Load diff
17620
dep/spirv-cross/src/spirv_msl.cpp
Normal file
17620
dep/spirv-cross/src/spirv_msl.cpp
Normal file
File diff suppressed because it is too large
Load diff
1332
dep/spirv-cross/src/spirv_parser.cpp
Normal file
1332
dep/spirv-cross/src/spirv_parser.cpp
Normal file
File diff suppressed because it is too large
Load diff
706
dep/spirv-cross/src/spirv_reflect.cpp
Normal file
706
dep/spirv-cross/src/spirv_reflect.cpp
Normal file
|
@ -0,0 +1,706 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018-2021 Bradley Austin Davis
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At your option, you may choose to accept this material under either:
|
||||||
|
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||||
|
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "spirv_reflect.hpp"
|
||||||
|
#include "spirv_glsl.hpp"
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
using namespace spv;
|
||||||
|
using namespace SPIRV_CROSS_NAMESPACE;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace simple_json
|
||||||
|
{
|
||||||
|
enum class Type
|
||||||
|
{
|
||||||
|
Object,
|
||||||
|
Array,
|
||||||
|
};
|
||||||
|
|
||||||
|
using State = std::pair<Type, bool>;
|
||||||
|
using Stack = std::stack<State>;
|
||||||
|
|
||||||
|
class Stream
|
||||||
|
{
|
||||||
|
Stack stack;
|
||||||
|
StringStream<> buffer;
|
||||||
|
uint32_t indent{ 0 };
|
||||||
|
char current_locale_radix_character = '.';
|
||||||
|
|
||||||
|
public:
|
||||||
|
void set_current_locale_radix_character(char c)
|
||||||
|
{
|
||||||
|
current_locale_radix_character = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin_json_object();
|
||||||
|
void end_json_object();
|
||||||
|
void emit_json_key(const std::string &key);
|
||||||
|
void emit_json_key_value(const std::string &key, const std::string &value);
|
||||||
|
void emit_json_key_value(const std::string &key, bool value);
|
||||||
|
void emit_json_key_value(const std::string &key, uint32_t value);
|
||||||
|
void emit_json_key_value(const std::string &key, int32_t value);
|
||||||
|
void emit_json_key_value(const std::string &key, float value);
|
||||||
|
void emit_json_key_object(const std::string &key);
|
||||||
|
void emit_json_key_array(const std::string &key);
|
||||||
|
|
||||||
|
void begin_json_array();
|
||||||
|
void end_json_array();
|
||||||
|
void emit_json_array_value(const std::string &value);
|
||||||
|
void emit_json_array_value(uint32_t value);
|
||||||
|
void emit_json_array_value(bool value);
|
||||||
|
|
||||||
|
std::string str() const
|
||||||
|
{
|
||||||
|
return buffer.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline void statement_indent()
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < indent; i++)
|
||||||
|
buffer << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void statement_inner(T &&t)
|
||||||
|
{
|
||||||
|
buffer << std::forward<T>(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Ts>
|
||||||
|
inline void statement_inner(T &&t, Ts &&... ts)
|
||||||
|
{
|
||||||
|
buffer << std::forward<T>(t);
|
||||||
|
statement_inner(std::forward<Ts>(ts)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
inline void statement(Ts &&... ts)
|
||||||
|
{
|
||||||
|
statement_indent();
|
||||||
|
statement_inner(std::forward<Ts>(ts)...);
|
||||||
|
buffer << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
void statement_no_return(Ts &&... ts)
|
||||||
|
{
|
||||||
|
statement_indent();
|
||||||
|
statement_inner(std::forward<Ts>(ts)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace simple_json
|
||||||
|
|
||||||
|
using namespace simple_json;
|
||||||
|
|
||||||
|
// Hackery to emit JSON without using nlohmann/json C++ library (which requires a
|
||||||
|
// higher level of compiler compliance than is required by SPIRV-Cross
|
||||||
|
void Stream::begin_json_array()
|
||||||
|
{
|
||||||
|
if (!stack.empty() && stack.top().second)
|
||||||
|
{
|
||||||
|
statement_inner(",\n");
|
||||||
|
}
|
||||||
|
statement("[");
|
||||||
|
++indent;
|
||||||
|
stack.emplace(Type::Array, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::end_json_array()
|
||||||
|
{
|
||||||
|
if (stack.empty() || stack.top().first != Type::Array)
|
||||||
|
SPIRV_CROSS_THROW("Invalid JSON state");
|
||||||
|
if (stack.top().second)
|
||||||
|
{
|
||||||
|
statement_inner("\n");
|
||||||
|
}
|
||||||
|
--indent;
|
||||||
|
statement_no_return("]");
|
||||||
|
stack.pop();
|
||||||
|
if (!stack.empty())
|
||||||
|
{
|
||||||
|
stack.top().second = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::emit_json_array_value(const std::string &value)
|
||||||
|
{
|
||||||
|
if (stack.empty() || stack.top().first != Type::Array)
|
||||||
|
SPIRV_CROSS_THROW("Invalid JSON state");
|
||||||
|
|
||||||
|
if (stack.top().second)
|
||||||
|
statement_inner(",\n");
|
||||||
|
|
||||||
|
statement_no_return("\"", value, "\"");
|
||||||
|
stack.top().second = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::emit_json_array_value(uint32_t value)
|
||||||
|
{
|
||||||
|
if (stack.empty() || stack.top().first != Type::Array)
|
||||||
|
SPIRV_CROSS_THROW("Invalid JSON state");
|
||||||
|
if (stack.top().second)
|
||||||
|
statement_inner(",\n");
|
||||||
|
statement_no_return(std::to_string(value));
|
||||||
|
stack.top().second = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::emit_json_array_value(bool value)
|
||||||
|
{
|
||||||
|
if (stack.empty() || stack.top().first != Type::Array)
|
||||||
|
SPIRV_CROSS_THROW("Invalid JSON state");
|
||||||
|
if (stack.top().second)
|
||||||
|
statement_inner(",\n");
|
||||||
|
statement_no_return(value ? "true" : "false");
|
||||||
|
stack.top().second = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::begin_json_object()
|
||||||
|
{
|
||||||
|
if (!stack.empty() && stack.top().second)
|
||||||
|
{
|
||||||
|
statement_inner(",\n");
|
||||||
|
}
|
||||||
|
statement("{");
|
||||||
|
++indent;
|
||||||
|
stack.emplace(Type::Object, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::end_json_object()
|
||||||
|
{
|
||||||
|
if (stack.empty() || stack.top().first != Type::Object)
|
||||||
|
SPIRV_CROSS_THROW("Invalid JSON state");
|
||||||
|
if (stack.top().second)
|
||||||
|
{
|
||||||
|
statement_inner("\n");
|
||||||
|
}
|
||||||
|
--indent;
|
||||||
|
statement_no_return("}");
|
||||||
|
stack.pop();
|
||||||
|
if (!stack.empty())
|
||||||
|
{
|
||||||
|
stack.top().second = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::emit_json_key(const std::string &key)
|
||||||
|
{
|
||||||
|
if (stack.empty() || stack.top().first != Type::Object)
|
||||||
|
SPIRV_CROSS_THROW("Invalid JSON state");
|
||||||
|
|
||||||
|
if (stack.top().second)
|
||||||
|
statement_inner(",\n");
|
||||||
|
statement_no_return("\"", key, "\" : ");
|
||||||
|
stack.top().second = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::emit_json_key_value(const std::string &key, const std::string &value)
|
||||||
|
{
|
||||||
|
emit_json_key(key);
|
||||||
|
statement_inner("\"", value, "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::emit_json_key_value(const std::string &key, uint32_t value)
|
||||||
|
{
|
||||||
|
emit_json_key(key);
|
||||||
|
statement_inner(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::emit_json_key_value(const std::string &key, int32_t value)
|
||||||
|
{
|
||||||
|
emit_json_key(key);
|
||||||
|
statement_inner(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::emit_json_key_value(const std::string &key, float value)
|
||||||
|
{
|
||||||
|
emit_json_key(key);
|
||||||
|
statement_inner(convert_to_string(value, current_locale_radix_character));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::emit_json_key_value(const std::string &key, bool value)
|
||||||
|
{
|
||||||
|
emit_json_key(key);
|
||||||
|
statement_inner(value ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::emit_json_key_object(const std::string &key)
|
||||||
|
{
|
||||||
|
emit_json_key(key);
|
||||||
|
statement_inner("{\n");
|
||||||
|
++indent;
|
||||||
|
stack.emplace(Type::Object, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::emit_json_key_array(const std::string &key)
|
||||||
|
{
|
||||||
|
emit_json_key(key);
|
||||||
|
statement_inner("[\n");
|
||||||
|
++indent;
|
||||||
|
stack.emplace(Type::Array, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerReflection::set_format(const std::string &format)
|
||||||
|
{
|
||||||
|
if (format != "json")
|
||||||
|
{
|
||||||
|
SPIRV_CROSS_THROW("Unsupported format");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string CompilerReflection::compile()
|
||||||
|
{
|
||||||
|
json_stream = std::make_shared<simple_json::Stream>();
|
||||||
|
json_stream->set_current_locale_radix_character(current_locale_radix_character);
|
||||||
|
json_stream->begin_json_object();
|
||||||
|
reorder_type_alias();
|
||||||
|
emit_entry_points();
|
||||||
|
emit_types();
|
||||||
|
emit_resources();
|
||||||
|
emit_specialization_constants();
|
||||||
|
json_stream->end_json_object();
|
||||||
|
return json_stream->str();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool naturally_emit_type(const SPIRType &type)
|
||||||
|
{
|
||||||
|
return type.basetype == SPIRType::Struct && !type.pointer && type.array.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CompilerReflection::type_is_reference(const SPIRType &type) const
|
||||||
|
{
|
||||||
|
// Physical pointers and arrays of physical pointers need to refer to the pointee's type.
|
||||||
|
return type_is_top_level_physical_pointer(type) ||
|
||||||
|
(type_is_array_of_pointers(type) && type.storage == StorageClassPhysicalStorageBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerReflection::emit_types()
|
||||||
|
{
|
||||||
|
bool emitted_open_tag = false;
|
||||||
|
|
||||||
|
SmallVector<uint32_t> physical_pointee_types;
|
||||||
|
|
||||||
|
// If we have physical pointers or arrays of physical pointers, it's also helpful to emit the pointee type
|
||||||
|
// and chain the type hierarchy. For POD, arrays can emit the entire type in-place.
|
||||||
|
ir.for_each_typed_id<SPIRType>([&](uint32_t self, SPIRType &type) {
|
||||||
|
if (naturally_emit_type(type))
|
||||||
|
{
|
||||||
|
emit_type(self, emitted_open_tag);
|
||||||
|
}
|
||||||
|
else if (type_is_reference(type))
|
||||||
|
{
|
||||||
|
if (!naturally_emit_type(this->get<SPIRType>(type.parent_type)) &&
|
||||||
|
find(physical_pointee_types.begin(), physical_pointee_types.end(), type.parent_type) ==
|
||||||
|
physical_pointee_types.end())
|
||||||
|
{
|
||||||
|
physical_pointee_types.push_back(type.parent_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (uint32_t pointee_type : physical_pointee_types)
|
||||||
|
emit_type(pointee_type, emitted_open_tag);
|
||||||
|
|
||||||
|
if (emitted_open_tag)
|
||||||
|
{
|
||||||
|
json_stream->end_json_object();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerReflection::emit_type(uint32_t type_id, bool &emitted_open_tag)
|
||||||
|
{
|
||||||
|
auto &type = get<SPIRType>(type_id);
|
||||||
|
auto name = type_to_glsl(type);
|
||||||
|
|
||||||
|
if (!emitted_open_tag)
|
||||||
|
{
|
||||||
|
json_stream->emit_json_key_object("types");
|
||||||
|
emitted_open_tag = true;
|
||||||
|
}
|
||||||
|
json_stream->emit_json_key_object("_" + std::to_string(type_id));
|
||||||
|
json_stream->emit_json_key_value("name", name);
|
||||||
|
|
||||||
|
if (type_is_top_level_physical_pointer(type))
|
||||||
|
{
|
||||||
|
json_stream->emit_json_key_value("type", "_" + std::to_string(type.parent_type));
|
||||||
|
json_stream->emit_json_key_value("physical_pointer", true);
|
||||||
|
}
|
||||||
|
else if (!type.array.empty())
|
||||||
|
{
|
||||||
|
emit_type_array(type);
|
||||||
|
json_stream->emit_json_key_value("type", "_" + std::to_string(type.parent_type));
|
||||||
|
json_stream->emit_json_key_value("array_stride", get_decoration(type_id, DecorationArrayStride));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
json_stream->emit_json_key_array("members");
|
||||||
|
// FIXME ideally we'd like to emit the size of a structure as a
|
||||||
|
// convenience to people parsing the reflected JSON. The problem
|
||||||
|
// is that there's no implicit size for a type. It's final size
|
||||||
|
// will be determined by the top level declaration in which it's
|
||||||
|
// included. So there might be one size for the struct if it's
|
||||||
|
// included in a std140 uniform block and another if it's included
|
||||||
|
// in a std430 uniform block.
|
||||||
|
// The solution is to include *all* potential sizes as a map of
|
||||||
|
// layout type name to integer, but that will probably require
|
||||||
|
// some additional logic being written in this class, or in the
|
||||||
|
// parent CompilerGLSL class.
|
||||||
|
auto size = type.member_types.size();
|
||||||
|
for (uint32_t i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
emit_type_member(type, i);
|
||||||
|
}
|
||||||
|
json_stream->end_json_array();
|
||||||
|
}
|
||||||
|
|
||||||
|
json_stream->end_json_object();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerReflection::emit_type_member(const SPIRType &type, uint32_t index)
|
||||||
|
{
|
||||||
|
auto &membertype = get<SPIRType>(type.member_types[index]);
|
||||||
|
json_stream->begin_json_object();
|
||||||
|
auto name = to_member_name(type, index);
|
||||||
|
// FIXME we'd like to emit the offset of each member, but such offsets are
|
||||||
|
// context dependent. See the comment above regarding structure sizes
|
||||||
|
json_stream->emit_json_key_value("name", name);
|
||||||
|
|
||||||
|
if (type_is_reference(membertype))
|
||||||
|
{
|
||||||
|
json_stream->emit_json_key_value("type", "_" + std::to_string(membertype.parent_type));
|
||||||
|
}
|
||||||
|
else if (membertype.basetype == SPIRType::Struct)
|
||||||
|
{
|
||||||
|
json_stream->emit_json_key_value("type", "_" + std::to_string(membertype.self));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
json_stream->emit_json_key_value("type", type_to_glsl(membertype));
|
||||||
|
}
|
||||||
|
emit_type_member_qualifiers(type, index);
|
||||||
|
json_stream->end_json_object();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerReflection::emit_type_array(const SPIRType &type)
|
||||||
|
{
|
||||||
|
if (!type_is_top_level_physical_pointer(type) && !type.array.empty())
|
||||||
|
{
|
||||||
|
json_stream->emit_json_key_array("array");
|
||||||
|
// Note that we emit the zeros here as a means of identifying
|
||||||
|
// unbounded arrays. This is necessary as otherwise there would
|
||||||
|
// be no way of differentiating between float[4] and float[4][]
|
||||||
|
for (const auto &value : type.array)
|
||||||
|
json_stream->emit_json_array_value(value);
|
||||||
|
json_stream->end_json_array();
|
||||||
|
|
||||||
|
json_stream->emit_json_key_array("array_size_is_literal");
|
||||||
|
for (const auto &value : type.array_size_literal)
|
||||||
|
json_stream->emit_json_array_value(value);
|
||||||
|
json_stream->end_json_array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerReflection::emit_type_member_qualifiers(const SPIRType &type, uint32_t index)
|
||||||
|
{
|
||||||
|
auto &membertype = get<SPIRType>(type.member_types[index]);
|
||||||
|
emit_type_array(membertype);
|
||||||
|
auto &memb = ir.meta[type.self].members;
|
||||||
|
if (index < memb.size())
|
||||||
|
{
|
||||||
|
auto &dec = memb[index];
|
||||||
|
if (dec.decoration_flags.get(DecorationLocation))
|
||||||
|
json_stream->emit_json_key_value("location", dec.location);
|
||||||
|
if (dec.decoration_flags.get(DecorationOffset))
|
||||||
|
json_stream->emit_json_key_value("offset", dec.offset);
|
||||||
|
|
||||||
|
// Array stride is a property of the array type, not the struct.
|
||||||
|
if (has_decoration(type.member_types[index], DecorationArrayStride))
|
||||||
|
json_stream->emit_json_key_value("array_stride",
|
||||||
|
get_decoration(type.member_types[index], DecorationArrayStride));
|
||||||
|
|
||||||
|
if (dec.decoration_flags.get(DecorationMatrixStride))
|
||||||
|
json_stream->emit_json_key_value("matrix_stride", dec.matrix_stride);
|
||||||
|
if (dec.decoration_flags.get(DecorationRowMajor))
|
||||||
|
json_stream->emit_json_key_value("row_major", true);
|
||||||
|
|
||||||
|
if (type_is_top_level_physical_pointer(membertype))
|
||||||
|
json_stream->emit_json_key_value("physical_pointer", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string CompilerReflection::execution_model_to_str(spv::ExecutionModel model)
|
||||||
|
{
|
||||||
|
switch (model)
|
||||||
|
{
|
||||||
|
case ExecutionModelVertex:
|
||||||
|
return "vert";
|
||||||
|
case ExecutionModelTessellationControl:
|
||||||
|
return "tesc";
|
||||||
|
case ExecutionModelTessellationEvaluation:
|
||||||
|
return "tese";
|
||||||
|
case ExecutionModelGeometry:
|
||||||
|
return "geom";
|
||||||
|
case ExecutionModelFragment:
|
||||||
|
return "frag";
|
||||||
|
case ExecutionModelGLCompute:
|
||||||
|
return "comp";
|
||||||
|
case ExecutionModelRayGenerationNV:
|
||||||
|
return "rgen";
|
||||||
|
case ExecutionModelIntersectionNV:
|
||||||
|
return "rint";
|
||||||
|
case ExecutionModelAnyHitNV:
|
||||||
|
return "rahit";
|
||||||
|
case ExecutionModelClosestHitNV:
|
||||||
|
return "rchit";
|
||||||
|
case ExecutionModelMissNV:
|
||||||
|
return "rmiss";
|
||||||
|
case ExecutionModelCallableNV:
|
||||||
|
return "rcall";
|
||||||
|
default:
|
||||||
|
return "???";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME include things like the local_size dimensions, geometry output vertex count, etc
|
||||||
|
void CompilerReflection::emit_entry_points()
|
||||||
|
{
|
||||||
|
auto entries = get_entry_points_and_stages();
|
||||||
|
if (!entries.empty())
|
||||||
|
{
|
||||||
|
// Needed to make output deterministic.
|
||||||
|
sort(begin(entries), end(entries), [](const EntryPoint &a, const EntryPoint &b) -> bool {
|
||||||
|
if (a.execution_model < b.execution_model)
|
||||||
|
return true;
|
||||||
|
else if (a.execution_model > b.execution_model)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return a.name < b.name;
|
||||||
|
});
|
||||||
|
|
||||||
|
json_stream->emit_json_key_array("entryPoints");
|
||||||
|
for (auto &e : entries)
|
||||||
|
{
|
||||||
|
json_stream->begin_json_object();
|
||||||
|
json_stream->emit_json_key_value("name", e.name);
|
||||||
|
json_stream->emit_json_key_value("mode", execution_model_to_str(e.execution_model));
|
||||||
|
if (e.execution_model == ExecutionModelGLCompute)
|
||||||
|
{
|
||||||
|
const auto &spv_entry = get_entry_point(e.name, e.execution_model);
|
||||||
|
|
||||||
|
SpecializationConstant spec_x, spec_y, spec_z;
|
||||||
|
get_work_group_size_specialization_constants(spec_x, spec_y, spec_z);
|
||||||
|
|
||||||
|
json_stream->emit_json_key_array("workgroup_size");
|
||||||
|
json_stream->emit_json_array_value(spec_x.id != ID(0) ? spec_x.constant_id :
|
||||||
|
spv_entry.workgroup_size.x);
|
||||||
|
json_stream->emit_json_array_value(spec_y.id != ID(0) ? spec_y.constant_id :
|
||||||
|
spv_entry.workgroup_size.y);
|
||||||
|
json_stream->emit_json_array_value(spec_z.id != ID(0) ? spec_z.constant_id :
|
||||||
|
spv_entry.workgroup_size.z);
|
||||||
|
json_stream->end_json_array();
|
||||||
|
|
||||||
|
json_stream->emit_json_key_array("workgroup_size_is_spec_constant_id");
|
||||||
|
json_stream->emit_json_array_value(spec_x.id != ID(0));
|
||||||
|
json_stream->emit_json_array_value(spec_y.id != ID(0));
|
||||||
|
json_stream->emit_json_array_value(spec_z.id != ID(0));
|
||||||
|
json_stream->end_json_array();
|
||||||
|
}
|
||||||
|
json_stream->end_json_object();
|
||||||
|
}
|
||||||
|
json_stream->end_json_array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerReflection::emit_resources()
|
||||||
|
{
|
||||||
|
auto res = get_shader_resources();
|
||||||
|
emit_resources("subpass_inputs", res.subpass_inputs);
|
||||||
|
emit_resources("inputs", res.stage_inputs);
|
||||||
|
emit_resources("outputs", res.stage_outputs);
|
||||||
|
emit_resources("textures", res.sampled_images);
|
||||||
|
emit_resources("separate_images", res.separate_images);
|
||||||
|
emit_resources("separate_samplers", res.separate_samplers);
|
||||||
|
emit_resources("images", res.storage_images);
|
||||||
|
emit_resources("ssbos", res.storage_buffers);
|
||||||
|
emit_resources("ubos", res.uniform_buffers);
|
||||||
|
emit_resources("push_constants", res.push_constant_buffers);
|
||||||
|
emit_resources("counters", res.atomic_counters);
|
||||||
|
emit_resources("acceleration_structures", res.acceleration_structures);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerReflection::emit_resources(const char *tag, const SmallVector<Resource> &resources)
|
||||||
|
{
|
||||||
|
if (resources.empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_stream->emit_json_key_array(tag);
|
||||||
|
for (auto &res : resources)
|
||||||
|
{
|
||||||
|
auto &type = get_type(res.type_id);
|
||||||
|
auto typeflags = ir.meta[type.self].decoration.decoration_flags;
|
||||||
|
auto &mask = get_decoration_bitset(res.id);
|
||||||
|
|
||||||
|
// If we don't have a name, use the fallback for the type instead of the variable
|
||||||
|
// for SSBOs and UBOs since those are the only meaningful names to use externally.
|
||||||
|
// Push constant blocks are still accessed by name and not block name, even though they are technically Blocks.
|
||||||
|
bool is_push_constant = get_storage_class(res.id) == StorageClassPushConstant;
|
||||||
|
bool is_block = get_decoration_bitset(type.self).get(DecorationBlock) ||
|
||||||
|
get_decoration_bitset(type.self).get(DecorationBufferBlock);
|
||||||
|
|
||||||
|
ID fallback_id = !is_push_constant && is_block ? ID(res.base_type_id) : ID(res.id);
|
||||||
|
|
||||||
|
json_stream->begin_json_object();
|
||||||
|
|
||||||
|
if (type.basetype == SPIRType::Struct)
|
||||||
|
{
|
||||||
|
json_stream->emit_json_key_value("type", "_" + std::to_string(res.base_type_id));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
json_stream->emit_json_key_value("type", type_to_glsl(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
json_stream->emit_json_key_value("name", !res.name.empty() ? res.name : get_fallback_name(fallback_id));
|
||||||
|
{
|
||||||
|
bool ssbo_block = type.storage == StorageClassStorageBuffer ||
|
||||||
|
(type.storage == StorageClassUniform && typeflags.get(DecorationBufferBlock));
|
||||||
|
Bitset qualifier_mask = ssbo_block ? get_buffer_block_flags(res.id) : mask;
|
||||||
|
|
||||||
|
if (qualifier_mask.get(DecorationNonReadable))
|
||||||
|
json_stream->emit_json_key_value("writeonly", true);
|
||||||
|
if (qualifier_mask.get(DecorationNonWritable))
|
||||||
|
json_stream->emit_json_key_value("readonly", true);
|
||||||
|
if (qualifier_mask.get(DecorationRestrict))
|
||||||
|
json_stream->emit_json_key_value("restrict", true);
|
||||||
|
if (qualifier_mask.get(DecorationCoherent))
|
||||||
|
json_stream->emit_json_key_value("coherent", true);
|
||||||
|
if (qualifier_mask.get(DecorationVolatile))
|
||||||
|
json_stream->emit_json_key_value("volatile", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit_type_array(type);
|
||||||
|
|
||||||
|
{
|
||||||
|
bool is_sized_block = is_block && (get_storage_class(res.id) == StorageClassUniform ||
|
||||||
|
get_storage_class(res.id) == StorageClassUniformConstant ||
|
||||||
|
get_storage_class(res.id) == StorageClassStorageBuffer);
|
||||||
|
if (is_sized_block)
|
||||||
|
{
|
||||||
|
uint32_t block_size = uint32_t(get_declared_struct_size(get_type(res.base_type_id)));
|
||||||
|
json_stream->emit_json_key_value("block_size", block_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.storage == StorageClassPushConstant)
|
||||||
|
json_stream->emit_json_key_value("push_constant", true);
|
||||||
|
if (mask.get(DecorationLocation))
|
||||||
|
json_stream->emit_json_key_value("location", get_decoration(res.id, DecorationLocation));
|
||||||
|
if (mask.get(DecorationRowMajor))
|
||||||
|
json_stream->emit_json_key_value("row_major", true);
|
||||||
|
if (mask.get(DecorationColMajor))
|
||||||
|
json_stream->emit_json_key_value("column_major", true);
|
||||||
|
if (mask.get(DecorationIndex))
|
||||||
|
json_stream->emit_json_key_value("index", get_decoration(res.id, DecorationIndex));
|
||||||
|
if (type.storage != StorageClassPushConstant && mask.get(DecorationDescriptorSet))
|
||||||
|
json_stream->emit_json_key_value("set", get_decoration(res.id, DecorationDescriptorSet));
|
||||||
|
if (mask.get(DecorationBinding))
|
||||||
|
json_stream->emit_json_key_value("binding", get_decoration(res.id, DecorationBinding));
|
||||||
|
if (mask.get(DecorationInputAttachmentIndex))
|
||||||
|
json_stream->emit_json_key_value("input_attachment_index",
|
||||||
|
get_decoration(res.id, DecorationInputAttachmentIndex));
|
||||||
|
if (mask.get(DecorationOffset))
|
||||||
|
json_stream->emit_json_key_value("offset", get_decoration(res.id, DecorationOffset));
|
||||||
|
|
||||||
|
// For images, the type itself adds a layout qualifer.
|
||||||
|
// Only emit the format for storage images.
|
||||||
|
if (type.basetype == SPIRType::Image && type.image.sampled == 2)
|
||||||
|
{
|
||||||
|
const char *fmt = format_to_glsl(type.image.format);
|
||||||
|
if (fmt != nullptr)
|
||||||
|
json_stream->emit_json_key_value("format", std::string(fmt));
|
||||||
|
}
|
||||||
|
json_stream->end_json_object();
|
||||||
|
}
|
||||||
|
json_stream->end_json_array();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerReflection::emit_specialization_constants()
|
||||||
|
{
|
||||||
|
auto specialization_constants = get_specialization_constants();
|
||||||
|
if (specialization_constants.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
json_stream->emit_json_key_array("specialization_constants");
|
||||||
|
for (const auto &spec_const : specialization_constants)
|
||||||
|
{
|
||||||
|
auto &c = get<SPIRConstant>(spec_const.id);
|
||||||
|
auto type = get<SPIRType>(c.constant_type);
|
||||||
|
json_stream->begin_json_object();
|
||||||
|
json_stream->emit_json_key_value("name", get_name(spec_const.id));
|
||||||
|
json_stream->emit_json_key_value("id", spec_const.constant_id);
|
||||||
|
json_stream->emit_json_key_value("type", type_to_glsl(type));
|
||||||
|
json_stream->emit_json_key_value("variable_id", spec_const.id);
|
||||||
|
switch (type.basetype)
|
||||||
|
{
|
||||||
|
case SPIRType::UInt:
|
||||||
|
json_stream->emit_json_key_value("default_value", c.scalar());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPIRType::Int:
|
||||||
|
json_stream->emit_json_key_value("default_value", c.scalar_i32());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPIRType::Float:
|
||||||
|
json_stream->emit_json_key_value("default_value", c.scalar_f32());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPIRType::Boolean:
|
||||||
|
json_stream->emit_json_key_value("default_value", c.scalar() != 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
json_stream->end_json_object();
|
||||||
|
}
|
||||||
|
json_stream->end_json_array();
|
||||||
|
}
|
||||||
|
|
||||||
|
string CompilerReflection::to_member_name(const SPIRType &type, uint32_t index) const
|
||||||
|
{
|
||||||
|
auto *type_meta = ir.find_meta(type.self);
|
||||||
|
|
||||||
|
if (type_meta)
|
||||||
|
{
|
||||||
|
auto &memb = type_meta->members;
|
||||||
|
if (index < memb.size() && !memb[index].alias.empty())
|
||||||
|
return memb[index].alias;
|
||||||
|
else
|
||||||
|
return join("_m", index);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return join("_m", index);
|
||||||
|
}
|
Loading…
Reference in a new issue