feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View 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

202
engine/thirdparty/spirv-cross/LICENSE vendored Normal file
View 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.

View file

@ -0,0 +1,208 @@
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.

View file

@ -0,0 +1,23 @@
Copyright (c) 2014-2020 The Khronos Group Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and/or associated documentation files (the "Materials"),
to deal in the Materials without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Materials, and to permit persons to whom the
Materials are furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Materials.
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/
THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
IN THE MATERIALS.

View file

@ -0,0 +1,19 @@
MIT License Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,80 @@
/*
* Copyright 2015-2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#ifndef SPIRV_CROSS_BARRIER_HPP
#define SPIRV_CROSS_BARRIER_HPP
#include <atomic>
#include <thread>
namespace spirv_cross
{
class Barrier
{
public:
Barrier()
{
count.store(0);
iteration.store(0);
}
void set_release_divisor(unsigned divisor)
{
this->divisor = divisor;
}
static inline void memoryBarrier()
{
std::atomic_thread_fence(std::memory_order_seq_cst);
}
void reset_counter()
{
count.store(0);
iteration.store(0);
}
void wait()
{
unsigned target_iteration = iteration.load(std::memory_order_relaxed) + 1;
// Overflows cleanly.
unsigned target_count = divisor * target_iteration;
// Barriers don't enforce memory ordering.
// Be as relaxed about the barrier as we possibly can!
unsigned c = count.fetch_add(1u, std::memory_order_relaxed);
if (c + 1 == target_count)
{
iteration.store(target_iteration, std::memory_order_relaxed);
}
else
{
// If we have more threads than the CPU, don't hog the CPU for very long periods of time.
while (iteration.load(std::memory_order_relaxed) != target_iteration)
std::this_thread::yield();
}
}
private:
unsigned divisor = 1;
std::atomic<unsigned> count;
std::atomic<unsigned> iteration;
};
}
#endif

View file

@ -0,0 +1,127 @@
/*
* Copyright 2015-2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#ifndef SPIRV_CROSS_EXTERNAL_INTERFACE_H
#define SPIRV_CROSS_EXTERNAL_INTERFACE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
typedef struct spirv_cross_shader spirv_cross_shader_t;
struct spirv_cross_interface
{
spirv_cross_shader_t *(*construct)(void);
void (*destruct)(spirv_cross_shader_t *thiz);
void (*invoke)(spirv_cross_shader_t *thiz);
};
void spirv_cross_set_stage_input(spirv_cross_shader_t *thiz, unsigned location, void *data, size_t size);
void spirv_cross_set_stage_output(spirv_cross_shader_t *thiz, unsigned location, void *data, size_t size);
void spirv_cross_set_push_constant(spirv_cross_shader_t *thiz, void *data, size_t size);
void spirv_cross_set_uniform_constant(spirv_cross_shader_t *thiz, unsigned location, void *data, size_t size);
void spirv_cross_set_resource(spirv_cross_shader_t *thiz, unsigned set, unsigned binding, void **data, size_t size);
const struct spirv_cross_interface *spirv_cross_get_interface(void);
typedef enum spirv_cross_builtin {
SPIRV_CROSS_BUILTIN_POSITION = 0,
SPIRV_CROSS_BUILTIN_FRAG_COORD = 1,
SPIRV_CROSS_BUILTIN_WORK_GROUP_ID = 2,
SPIRV_CROSS_BUILTIN_NUM_WORK_GROUPS = 3,
SPIRV_CROSS_NUM_BUILTINS
} spirv_cross_builtin;
void spirv_cross_set_builtin(spirv_cross_shader_t *thiz, spirv_cross_builtin builtin, void *data, size_t size);
#define SPIRV_CROSS_NUM_DESCRIPTOR_SETS 4
#define SPIRV_CROSS_NUM_DESCRIPTOR_BINDINGS 16
#define SPIRV_CROSS_NUM_STAGE_INPUTS 16
#define SPIRV_CROSS_NUM_STAGE_OUTPUTS 16
#define SPIRV_CROSS_NUM_UNIFORM_CONSTANTS 32
enum spirv_cross_format
{
SPIRV_CROSS_FORMAT_R8_UNORM = 0,
SPIRV_CROSS_FORMAT_R8G8_UNORM = 1,
SPIRV_CROSS_FORMAT_R8G8B8_UNORM = 2,
SPIRV_CROSS_FORMAT_R8G8B8A8_UNORM = 3,
SPIRV_CROSS_NUM_FORMATS
};
enum spirv_cross_wrap
{
SPIRV_CROSS_WRAP_CLAMP_TO_EDGE = 0,
SPIRV_CROSS_WRAP_REPEAT = 1,
SPIRV_CROSS_NUM_WRAP
};
enum spirv_cross_filter
{
SPIRV_CROSS_FILTER_NEAREST = 0,
SPIRV_CROSS_FILTER_LINEAR = 1,
SPIRV_CROSS_NUM_FILTER
};
enum spirv_cross_mipfilter
{
SPIRV_CROSS_MIPFILTER_BASE = 0,
SPIRV_CROSS_MIPFILTER_NEAREST = 1,
SPIRV_CROSS_MIPFILTER_LINEAR = 2,
SPIRV_CROSS_NUM_MIPFILTER
};
struct spirv_cross_miplevel
{
const void *data;
unsigned width, height;
size_t stride;
};
struct spirv_cross_sampler_info
{
const struct spirv_cross_miplevel *mipmaps;
unsigned num_mipmaps;
enum spirv_cross_format format;
enum spirv_cross_wrap wrap_s;
enum spirv_cross_wrap wrap_t;
enum spirv_cross_filter min_filter;
enum spirv_cross_filter mag_filter;
enum spirv_cross_mipfilter mip_filter;
};
typedef struct spirv_cross_sampler_2d spirv_cross_sampler_2d_t;
spirv_cross_sampler_2d_t *spirv_cross_create_sampler_2d(const struct spirv_cross_sampler_info *info);
void spirv_cross_destroy_sampler_2d(spirv_cross_sampler_2d_t *samp);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,63 @@
/*
* Copyright 2015-2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#ifndef SPIRV_CROSS_IMAGE_HPP
#define SPIRV_CROSS_IMAGE_HPP
#ifndef GLM_SWIZZLE
#define GLM_SWIZZLE
#endif
#ifndef GLM_FORCE_RADIANS
#define GLM_FORCE_RADIANS
#endif
#include <glm/glm.hpp>
namespace spirv_cross
{
template <typename T>
struct image2DBase
{
virtual ~image2DBase() = default;
inline virtual T load(glm::ivec2 coord) const
{
return T(0, 0, 0, 1);
}
inline virtual void store(glm::ivec2 coord, const T &v)
{
}
};
typedef image2DBase<glm::vec4> image2D;
typedef image2DBase<glm::ivec4> iimage2D;
typedef image2DBase<glm::uvec4> uimage2D;
template <typename T>
inline T imageLoad(const image2DBase<T> &image, glm::ivec2 coord)
{
return image.load(coord);
}
template <typename T>
void imageStore(image2DBase<T> &image, glm::ivec2 coord, const T &value)
{
image.store(coord, value);
}
}
#endif

View file

@ -0,0 +1,604 @@
/*
* Copyright 2015-2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#ifndef SPIRV_CROSS_INTERNAL_INTERFACE_HPP
#define SPIRV_CROSS_INTERNAL_INTERFACE_HPP
// This file must only be included by the shader generated by spirv-cross!
#ifndef GLM_FORCE_SWIZZLE
#define GLM_FORCE_SWIZZLE
#endif
#ifndef GLM_FORCE_RADIANS
#define GLM_FORCE_RADIANS
#endif
#include <glm/glm.hpp>
#include "barrier.hpp"
#include "external_interface.h"
#include "image.hpp"
#include "sampler.hpp"
#include "thread_group.hpp"
#include <assert.h>
#include <stdint.h>
namespace internal
{
// Adaptor helpers to adapt GLSL access chain syntax to C++.
// Don't bother with arrays of arrays on uniforms ...
// Would likely need horribly complex variadic template munging.
template <typename T>
struct Interface
{
enum
{
ArraySize = 1,
Size = sizeof(T)
};
Interface()
: ptr(0)
{
}
T &get()
{
assert(ptr);
return *ptr;
}
T *ptr;
};
// For array types, return a pointer instead.
template <typename T, unsigned U>
struct Interface<T[U]>
{
enum
{
ArraySize = U,
Size = U * sizeof(T)
};
Interface()
: ptr(0)
{
}
T *get()
{
assert(ptr);
return ptr;
}
T *ptr;
};
// For case when array size is 1, avoid double dereference.
template <typename T>
struct PointerInterface
{
enum
{
ArraySize = 1,
Size = sizeof(T *)
};
enum
{
PreDereference = true
};
PointerInterface()
: ptr(0)
{
}
T &get()
{
assert(ptr);
return *ptr;
}
T *ptr;
};
// Automatically converts a pointer down to reference to match GLSL syntax.
template <typename T>
struct DereferenceAdaptor
{
DereferenceAdaptor(T **ptr)
: ptr(ptr)
{
}
T &operator[](unsigned index) const
{
return *(ptr[index]);
}
T **ptr;
};
// We can't have a linear array of T* since T* can be an abstract type in case of samplers.
// We also need a list of pointers since we can have run-time length SSBOs.
template <typename T, unsigned U>
struct PointerInterface<T[U]>
{
enum
{
ArraySize = U,
Size = sizeof(T *) * U
};
enum
{
PreDereference = false
};
PointerInterface()
: ptr(0)
{
}
DereferenceAdaptor<T> get()
{
assert(ptr);
return DereferenceAdaptor<T>(ptr);
}
T **ptr;
};
// Resources can be more abstract and be unsized,
// so we need to have an array of pointers for those cases.
template <typename T>
struct Resource : PointerInterface<T>
{
};
// POD with no unknown sizes, so we can express these as flat arrays.
template <typename T>
struct UniformConstant : Interface<T>
{
};
template <typename T>
struct StageInput : Interface<T>
{
};
template <typename T>
struct StageOutput : Interface<T>
{
};
template <typename T>
struct PushConstant : Interface<T>
{
};
}
struct spirv_cross_shader
{
struct PPSize
{
PPSize()
: ptr(0)
, size(0)
{
}
void **ptr;
size_t size;
};
struct PPSizeResource
{
PPSizeResource()
: ptr(0)
, size(0)
, pre_dereference(false)
{
}
void **ptr;
size_t size;
bool pre_dereference;
};
PPSizeResource resources[SPIRV_CROSS_NUM_DESCRIPTOR_SETS][SPIRV_CROSS_NUM_DESCRIPTOR_BINDINGS];
PPSize stage_inputs[SPIRV_CROSS_NUM_STAGE_INPUTS];
PPSize stage_outputs[SPIRV_CROSS_NUM_STAGE_OUTPUTS];
PPSize uniform_constants[SPIRV_CROSS_NUM_UNIFORM_CONSTANTS];
PPSize push_constant;
PPSize builtins[SPIRV_CROSS_NUM_BUILTINS];
template <typename U>
void register_builtin(spirv_cross_builtin builtin, const U &value)
{
assert(!builtins[builtin].ptr);
builtins[builtin].ptr = (void **)&value.ptr;
builtins[builtin].size = sizeof(*value.ptr) * U::ArraySize;
}
void set_builtin(spirv_cross_builtin builtin, void *data, size_t size)
{
assert(builtins[builtin].ptr);
assert(size >= builtins[builtin].size);
*builtins[builtin].ptr = data;
}
template <typename U>
void register_resource(const internal::Resource<U> &value, unsigned set, unsigned binding)
{
assert(set < SPIRV_CROSS_NUM_DESCRIPTOR_SETS);
assert(binding < SPIRV_CROSS_NUM_DESCRIPTOR_BINDINGS);
assert(!resources[set][binding].ptr);
resources[set][binding].ptr = (void **)&value.ptr;
resources[set][binding].size = internal::Resource<U>::Size;
resources[set][binding].pre_dereference = internal::Resource<U>::PreDereference;
}
template <typename U>
void register_stage_input(const internal::StageInput<U> &value, unsigned location)
{
assert(location < SPIRV_CROSS_NUM_STAGE_INPUTS);
assert(!stage_inputs[location].ptr);
stage_inputs[location].ptr = (void **)&value.ptr;
stage_inputs[location].size = internal::StageInput<U>::Size;
}
template <typename U>
void register_stage_output(const internal::StageOutput<U> &value, unsigned location)
{
assert(location < SPIRV_CROSS_NUM_STAGE_OUTPUTS);
assert(!stage_outputs[location].ptr);
stage_outputs[location].ptr = (void **)&value.ptr;
stage_outputs[location].size = internal::StageOutput<U>::Size;
}
template <typename U>
void register_uniform_constant(const internal::UniformConstant<U> &value, unsigned location)
{
assert(location < SPIRV_CROSS_NUM_UNIFORM_CONSTANTS);
assert(!uniform_constants[location].ptr);
uniform_constants[location].ptr = (void **)&value.ptr;
uniform_constants[location].size = internal::UniformConstant<U>::Size;
}
template <typename U>
void register_push_constant(const internal::PushConstant<U> &value)
{
assert(!push_constant.ptr);
push_constant.ptr = (void **)&value.ptr;
push_constant.size = internal::PushConstant<U>::Size;
}
void set_stage_input(unsigned location, void *data, size_t size)
{
assert(location < SPIRV_CROSS_NUM_STAGE_INPUTS);
assert(stage_inputs[location].ptr);
assert(size >= stage_inputs[location].size);
*stage_inputs[location].ptr = data;
}
void set_stage_output(unsigned location, void *data, size_t size)
{
assert(location < SPIRV_CROSS_NUM_STAGE_OUTPUTS);
assert(stage_outputs[location].ptr);
assert(size >= stage_outputs[location].size);
*stage_outputs[location].ptr = data;
}
void set_uniform_constant(unsigned location, void *data, size_t size)
{
assert(location < SPIRV_CROSS_NUM_UNIFORM_CONSTANTS);
assert(uniform_constants[location].ptr);
assert(size >= uniform_constants[location].size);
*uniform_constants[location].ptr = data;
}
void set_push_constant(void *data, size_t size)
{
assert(push_constant.ptr);
assert(size >= push_constant.size);
*push_constant.ptr = data;
}
void set_resource(unsigned set, unsigned binding, void **data, size_t size)
{
assert(set < SPIRV_CROSS_NUM_DESCRIPTOR_SETS);
assert(binding < SPIRV_CROSS_NUM_DESCRIPTOR_BINDINGS);
assert(resources[set][binding].ptr);
assert(size >= resources[set][binding].size);
// We're using the regular PointerInterface, dereference ahead of time.
if (resources[set][binding].pre_dereference)
*resources[set][binding].ptr = *data;
else
*resources[set][binding].ptr = data;
}
};
namespace spirv_cross
{
template <typename T>
struct BaseShader : spirv_cross_shader
{
void invoke()
{
static_cast<T *>(this)->main();
}
};
struct FragmentResources
{
internal::StageOutput<glm::vec4> gl_FragCoord;
void init(spirv_cross_shader &s)
{
s.register_builtin(SPIRV_CROSS_BUILTIN_FRAG_COORD, gl_FragCoord);
}
#define gl_FragCoord __res->gl_FragCoord.get()
};
template <typename T, typename Res>
struct FragmentShader : BaseShader<FragmentShader<T, Res>>
{
inline void main()
{
impl.main();
}
FragmentShader()
{
resources.init(*this);
impl.__res = &resources;
}
T impl;
Res resources;
};
struct VertexResources
{
internal::StageOutput<glm::vec4> gl_Position;
void init(spirv_cross_shader &s)
{
s.register_builtin(SPIRV_CROSS_BUILTIN_POSITION, gl_Position);
}
#define gl_Position __res->gl_Position.get()
};
template <typename T, typename Res>
struct VertexShader : BaseShader<VertexShader<T, Res>>
{
inline void main()
{
impl.main();
}
VertexShader()
{
resources.init(*this);
impl.__res = &resources;
}
T impl;
Res resources;
};
struct TessEvaluationResources
{
inline void init(spirv_cross_shader &)
{
}
};
template <typename T, typename Res>
struct TessEvaluationShader : BaseShader<TessEvaluationShader<T, Res>>
{
inline void main()
{
impl.main();
}
TessEvaluationShader()
{
resources.init(*this);
impl.__res = &resources;
}
T impl;
Res resources;
};
struct TessControlResources
{
inline void init(spirv_cross_shader &)
{
}
};
template <typename T, typename Res>
struct TessControlShader : BaseShader<TessControlShader<T, Res>>
{
inline void main()
{
impl.main();
}
TessControlShader()
{
resources.init(*this);
impl.__res = &resources;
}
T impl;
Res resources;
};
struct GeometryResources
{
inline void init(spirv_cross_shader &)
{
}
};
template <typename T, typename Res>
struct GeometryShader : BaseShader<GeometryShader<T, Res>>
{
inline void main()
{
impl.main();
}
GeometryShader()
{
resources.init(*this);
impl.__res = &resources;
}
T impl;
Res resources;
};
struct ComputeResources
{
internal::StageInput<glm::uvec3> gl_WorkGroupID__;
internal::StageInput<glm::uvec3> gl_NumWorkGroups__;
void init(spirv_cross_shader &s)
{
s.register_builtin(SPIRV_CROSS_BUILTIN_WORK_GROUP_ID, gl_WorkGroupID__);
s.register_builtin(SPIRV_CROSS_BUILTIN_NUM_WORK_GROUPS, gl_NumWorkGroups__);
}
#define gl_WorkGroupID __res->gl_WorkGroupID__.get()
#define gl_NumWorkGroups __res->gl_NumWorkGroups__.get()
Barrier barrier__;
#define barrier() __res->barrier__.wait()
};
struct ComputePrivateResources
{
uint32_t gl_LocalInvocationIndex__;
#define gl_LocalInvocationIndex __priv_res.gl_LocalInvocationIndex__
glm::uvec3 gl_LocalInvocationID__;
#define gl_LocalInvocationID __priv_res.gl_LocalInvocationID__
glm::uvec3 gl_GlobalInvocationID__;
#define gl_GlobalInvocationID __priv_res.gl_GlobalInvocationID__
};
template <typename T, typename Res, unsigned WorkGroupX, unsigned WorkGroupY, unsigned WorkGroupZ>
struct ComputeShader : BaseShader<ComputeShader<T, Res, WorkGroupX, WorkGroupY, WorkGroupZ>>
{
inline void main()
{
resources.barrier__.reset_counter();
for (unsigned z = 0; z < WorkGroupZ; z++)
for (unsigned y = 0; y < WorkGroupY; y++)
for (unsigned x = 0; x < WorkGroupX; x++)
impl[z][y][x].__priv_res.gl_GlobalInvocationID__ =
glm::uvec3(WorkGroupX, WorkGroupY, WorkGroupZ) * resources.gl_WorkGroupID__.get() +
glm::uvec3(x, y, z);
group.run();
group.wait();
}
ComputeShader()
: group(&impl[0][0][0])
{
resources.init(*this);
resources.barrier__.set_release_divisor(WorkGroupX * WorkGroupY * WorkGroupZ);
unsigned i = 0;
for (unsigned z = 0; z < WorkGroupZ; z++)
{
for (unsigned y = 0; y < WorkGroupY; y++)
{
for (unsigned x = 0; x < WorkGroupX; x++)
{
impl[z][y][x].__priv_res.gl_LocalInvocationID__ = glm::uvec3(x, y, z);
impl[z][y][x].__priv_res.gl_LocalInvocationIndex__ = i++;
impl[z][y][x].__res = &resources;
}
}
}
}
T impl[WorkGroupZ][WorkGroupY][WorkGroupX];
ThreadGroup<T, WorkGroupX * WorkGroupY * WorkGroupZ> group;
Res resources;
};
inline void memoryBarrierShared()
{
Barrier::memoryBarrier();
}
inline void memoryBarrier()
{
Barrier::memoryBarrier();
}
// TODO: Rest of the barriers.
// Atomics
template <typename T>
inline T atomicAdd(T &v, T a)
{
static_assert(sizeof(std::atomic<T>) == sizeof(T), "Cannot cast properly to std::atomic<T>.");
// We need explicit memory barriers in GLSL to enfore any ordering.
// FIXME: Can we really cast this? There is no other way I think ...
return std::atomic_fetch_add_explicit(reinterpret_cast<std::atomic<T> *>(&v), a, std::memory_order_relaxed);
}
}
void spirv_cross_set_stage_input(spirv_cross_shader_t *shader, unsigned location, void *data, size_t size)
{
shader->set_stage_input(location, data, size);
}
void spirv_cross_set_stage_output(spirv_cross_shader_t *shader, unsigned location, void *data, size_t size)
{
shader->set_stage_output(location, data, size);
}
void spirv_cross_set_uniform_constant(spirv_cross_shader_t *shader, unsigned location, void *data, size_t size)
{
shader->set_uniform_constant(location, data, size);
}
void spirv_cross_set_resource(spirv_cross_shader_t *shader, unsigned set, unsigned binding, void **data, size_t size)
{
shader->set_resource(set, binding, data, size);
}
void spirv_cross_set_push_constant(spirv_cross_shader_t *shader, void *data, size_t size)
{
shader->set_push_constant(data, size);
}
void spirv_cross_set_builtin(spirv_cross_shader_t *shader, spirv_cross_builtin builtin, void *data, size_t size)
{
shader->set_builtin(builtin, data, size);
}
#endif

View file

@ -0,0 +1,106 @@
/*
* Copyright 2015-2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#ifndef SPIRV_CROSS_SAMPLER_HPP
#define SPIRV_CROSS_SAMPLER_HPP
#include <vector>
namespace spirv_cross
{
struct spirv_cross_sampler_2d
{
inline virtual ~spirv_cross_sampler_2d()
{
}
};
template <typename T>
struct sampler2DBase : spirv_cross_sampler_2d
{
sampler2DBase(const spirv_cross_sampler_info *info)
{
mips.insert(mips.end(), info->mipmaps, info->mipmaps + info->num_mipmaps);
format = info->format;
wrap_s = info->wrap_s;
wrap_t = info->wrap_t;
min_filter = info->min_filter;
mag_filter = info->mag_filter;
mip_filter = info->mip_filter;
}
inline virtual T sample(glm::vec2 uv, float bias)
{
return sampleLod(uv, bias);
}
inline virtual T sampleLod(glm::vec2 uv, float lod)
{
if (mag_filter == SPIRV_CROSS_FILTER_NEAREST)
{
uv.x = wrap(uv.x, wrap_s, mips[0].width);
uv.y = wrap(uv.y, wrap_t, mips[0].height);
glm::vec2 uv_full = uv * glm::vec2(mips[0].width, mips[0].height);
int x = int(uv_full.x);
int y = int(uv_full.y);
return sample(x, y, 0);
}
else
{
return T(0, 0, 0, 1);
}
}
inline float wrap(float v, spirv_cross_wrap wrap, unsigned size)
{
switch (wrap)
{
case SPIRV_CROSS_WRAP_REPEAT:
return v - glm::floor(v);
case SPIRV_CROSS_WRAP_CLAMP_TO_EDGE:
{
float half = 0.5f / size;
return glm::clamp(v, half, 1.0f - half);
}
default:
return 0.0f;
}
}
std::vector<spirv_cross_miplevel> mips;
spirv_cross_format format;
spirv_cross_wrap wrap_s;
spirv_cross_wrap wrap_t;
spirv_cross_filter min_filter;
spirv_cross_filter mag_filter;
spirv_cross_mipfilter mip_filter;
};
typedef sampler2DBase<glm::vec4> sampler2D;
typedef sampler2DBase<glm::ivec4> isampler2D;
typedef sampler2DBase<glm::uvec4> usampler2D;
template <typename T>
inline T texture(const sampler2DBase<T> &samp, const glm::vec2 &uv, float bias = 0.0f)
{
return samp.sample(uv, bias);
}
}
#endif

View file

@ -0,0 +1,114 @@
/*
* Copyright 2015-2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#ifndef SPIRV_CROSS_THREAD_GROUP_HPP
#define SPIRV_CROSS_THREAD_GROUP_HPP
#include <condition_variable>
#include <mutex>
#include <thread>
namespace spirv_cross
{
template <typename T, unsigned Size>
class ThreadGroup
{
public:
ThreadGroup(T *impl)
{
for (unsigned i = 0; i < Size; i++)
workers[i].start(&impl[i]);
}
void run()
{
for (auto &worker : workers)
worker.run();
}
void wait()
{
for (auto &worker : workers)
worker.wait();
}
private:
struct Thread
{
enum State
{
Idle,
Running,
Dying
};
State state = Idle;
void start(T *impl)
{
worker = std::thread([impl, this] {
for (;;)
{
{
std::unique_lock<std::mutex> l{ lock };
cond.wait(l, [this] { return state != Idle; });
if (state == Dying)
break;
}
impl->main();
std::lock_guard<std::mutex> l{ lock };
state = Idle;
cond.notify_one();
}
});
}
void wait()
{
std::unique_lock<std::mutex> l{ lock };
cond.wait(l, [this] { return state == Idle; });
}
void run()
{
std::lock_guard<std::mutex> l{ lock };
state = Running;
cond.notify_one();
}
~Thread()
{
if (worker.joinable())
{
{
std::lock_guard<std::mutex> l{ lock };
state = Dying;
cond.notify_one();
}
worker.join();
}
}
std::thread worker;
std::condition_variable cond;
std::mutex lock;
};
Thread workers[Size];
};
}
#endif

2602
engine/thirdparty/spirv-cross/spirv.hpp vendored Normal file

File diff suppressed because it is too large Load diff

View 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

View 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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,756 @@
/*
* 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;
vacants.reserve(num_objects);
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

View file

@ -0,0 +1,99 @@
/*
* 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)
{
}
explicit CompilerError(const char *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

File diff suppressed because it is too large Load diff

View 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 = nullptr;
};
// 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

View 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

View 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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

19401
engine/thirdparty/spirv-cross/spirv_msl.cpp vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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

View file

@ -0,0 +1,712 @@
/*
* 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 is_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 (is_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 (!is_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 (is_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));
if (mask.get(DecorationWeightTextureQCOM))
json_stream->emit_json_key_value("WeightTextureQCOM", get_decoration(res.id, DecorationWeightTextureQCOM));
if (mask.get(DecorationBlockMatchTextureQCOM))
json_stream->emit_json_key_value("BlockMatchTextureQCOM", get_decoration(res.id, DecorationBlockMatchTextureQCOM));
if (mask.get(DecorationBlockMatchSamplerQCOM))
json_stream->emit_json_key_value("BlockMatchSamplerQCOM", get_decoration(res.id, DecorationBlockMatchSamplerQCOM));
// 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);
}

View 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