mirror of
https://github.com/godotengine/godot.git
synced 2024-11-21 19:42:43 +00:00
Add Metal support for macOS (arm64) and iOS
This commit is contained in:
parent
826de7976a
commit
2d0165574d
3
.github/actions/godot-build/action.yml
vendored
3
.github/actions/godot-build/action.yml
vendored
@ -7,11 +7,14 @@ inputs:
|
||||
tests:
|
||||
description: Unit tests.
|
||||
default: false
|
||||
required: false
|
||||
platform:
|
||||
description: Target platform.
|
||||
required: false
|
||||
sconsflags:
|
||||
description: Additional SCons flags.
|
||||
default: ""
|
||||
required: false
|
||||
scons-cache:
|
||||
description: The SCons cache path.
|
||||
default: "${{ github.workspace }}/.scons-cache/"
|
||||
|
@ -475,6 +475,11 @@ Comment: RVO2
|
||||
Copyright: 2016, University of North Carolina at Chapel Hill
|
||||
License: Apache-2.0
|
||||
|
||||
Files: ./thirdparty/spirv-cross/
|
||||
Comment: SPIRV-Cross
|
||||
Copyright: 2015-2021, Arm Limited
|
||||
License: Apache-2.0 or Expat
|
||||
|
||||
Files: ./thirdparty/spirv-reflect/
|
||||
Comment: SPIRV-Reflect
|
||||
Copyright: 2017-2022, Google Inc.
|
||||
|
@ -222,6 +222,7 @@ opts.Add(BoolVariable("xaudio2", "Enable the XAudio2 audio driver", False))
|
||||
opts.Add(BoolVariable("vulkan", "Enable the vulkan rendering driver", True))
|
||||
opts.Add(BoolVariable("opengl3", "Enable the OpenGL/GLES3 rendering driver", True))
|
||||
opts.Add(BoolVariable("d3d12", "Enable the Direct3D 12 rendering driver", False))
|
||||
opts.Add(BoolVariable("metal", "Enable the Metal rendering driver (Apple arm64 only)", False))
|
||||
opts.Add(BoolVariable("openxr", "Enable the OpenXR driver", True))
|
||||
opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loader dynamically", True))
|
||||
opts.Add(BoolVariable("disable_exceptions", "Force disabling exception handling code", True))
|
||||
|
@ -692,6 +692,7 @@ void OS::_bind_methods() {
|
||||
BIND_ENUM_CONSTANT(RENDERING_DRIVER_VULKAN);
|
||||
BIND_ENUM_CONSTANT(RENDERING_DRIVER_OPENGL3);
|
||||
BIND_ENUM_CONSTANT(RENDERING_DRIVER_D3D12);
|
||||
BIND_ENUM_CONSTANT(RENDERING_DRIVER_METAL);
|
||||
|
||||
BIND_ENUM_CONSTANT(SYSTEM_DIR_DESKTOP);
|
||||
BIND_ENUM_CONSTANT(SYSTEM_DIR_DCIM);
|
||||
|
@ -132,6 +132,7 @@ public:
|
||||
RENDERING_DRIVER_VULKAN,
|
||||
RENDERING_DRIVER_OPENGL3,
|
||||
RENDERING_DRIVER_D3D12,
|
||||
RENDERING_DRIVER_METAL,
|
||||
};
|
||||
|
||||
PackedByteArray get_entropy(int p_bytes);
|
||||
|
@ -802,6 +802,9 @@
|
||||
<constant name="RENDERING_DRIVER_D3D12" value="2" enum="RenderingDriver">
|
||||
The Direct3D 12 rendering driver.
|
||||
</constant>
|
||||
<constant name="RENDERING_DRIVER_METAL" value="3" enum="RenderingDriver">
|
||||
The Metal rendering driver.
|
||||
</constant>
|
||||
<constant name="SYSTEM_DIR_DESKTOP" value="0" enum="SystemDir">
|
||||
Refers to the Desktop directory path.
|
||||
</constant>
|
||||
|
@ -33,6 +33,8 @@ if env["opengl3"]:
|
||||
SConscript("gl_context/SCsub")
|
||||
SConscript("gles3/SCsub")
|
||||
SConscript("egl/SCsub")
|
||||
if env["metal"]:
|
||||
SConscript("metal/SCsub")
|
||||
|
||||
# Core dependencies
|
||||
SConscript("png/SCsub")
|
||||
|
39
drivers/metal/README.md
Normal file
39
drivers/metal/README.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Metal Rendering Device
|
||||
|
||||
This document aims to describe the Metal rendering device implementation in Godot.
|
||||
|
||||
## Future work / ideas
|
||||
|
||||
* Use placement heaps
|
||||
* Explicit hazard tracking
|
||||
* [MetalFX] upscaling support?
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
The Metal rendering owes a lot to the work of the [MoltenVK] project, which is a Vulkan implementation on top of Metal.
|
||||
In accordance with the Apache 2.0 license, the following copyright notices have been included where applicable:
|
||||
|
||||
```
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* Portions of this code were derived from MoltenVK. */
|
||||
/* */
|
||||
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
|
||||
/* (http://www.brenwill.com) */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
```
|
||||
|
||||
[MoltenVK]: https://github.com/KhronosGroup/MoltenVK
|
||||
[MetalFX]: https://developer.apple.com/documentation/metalfx?language=objc
|
49
drivers/metal/SCsub
Normal file
49
drivers/metal/SCsub
Normal file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env_metal = env.Clone()
|
||||
|
||||
# Thirdparty source files
|
||||
|
||||
thirdparty_obj = []
|
||||
|
||||
thirdparty_dir = "#thirdparty/spirv-cross/"
|
||||
thirdparty_sources = [
|
||||
"spirv_cfg.cpp",
|
||||
"spirv_cross_util.cpp",
|
||||
"spirv_cross.cpp",
|
||||
"spirv_parser.cpp",
|
||||
"spirv_msl.cpp",
|
||||
"spirv_reflect.cpp",
|
||||
"spirv_glsl.cpp",
|
||||
"spirv_cross_parsed_ir.cpp",
|
||||
]
|
||||
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
|
||||
|
||||
env_metal.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include"])
|
||||
|
||||
# Must enable exceptions for SPIRV-Cross; otherwise, it will abort the process on errors.
|
||||
if "-fno-exceptions" in env_metal["CXXFLAGS"]:
|
||||
env_metal["CXXFLAGS"].remove("-fno-exceptions")
|
||||
env_metal.Append(CXXFLAGS=["-fexceptions"])
|
||||
|
||||
env_thirdparty = env_metal.Clone()
|
||||
env_thirdparty.disable_warnings()
|
||||
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
|
||||
env_metal.drivers_sources += thirdparty_obj
|
||||
|
||||
# Enable C++20 for the Objective-C++ Metal code, which uses C++20 concepts.
|
||||
if "-std=gnu++17" in env_metal["CXXFLAGS"]:
|
||||
env_metal["CXXFLAGS"].remove("-std=gnu++17")
|
||||
env_metal.Append(CXXFLAGS=["-std=c++20"])
|
||||
|
||||
# Driver source files
|
||||
|
||||
driver_obj = []
|
||||
|
||||
env_metal.add_source_files(driver_obj, "*.mm")
|
||||
env.drivers_sources += driver_obj
|
||||
|
||||
# Needed to force rebuilding the driver files when the thirdparty library is updated.
|
||||
env.Depends(driver_obj, thirdparty_obj)
|
141
drivers/metal/metal_device_properties.h
Normal file
141
drivers/metal/metal_device_properties.h
Normal file
@ -0,0 +1,141 @@
|
||||
/**************************************************************************/
|
||||
/* metal_device_properties.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* Portions of this code were derived from MoltenVK. */
|
||||
/* */
|
||||
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
|
||||
/* (http://www.brenwill.com) */
|
||||
/* */
|
||||
/* 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 METAL_DEVICE_PROPERTIES_H
|
||||
#define METAL_DEVICE_PROPERTIES_H
|
||||
|
||||
#import "servers/rendering/rendering_device.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Metal/Metal.h>
|
||||
|
||||
/** The buffer index to use for vertex content. */
|
||||
const static uint32_t VERT_CONTENT_BUFFER_INDEX = 0;
|
||||
const static uint32_t MAX_COLOR_ATTACHMENT_COUNT = 8;
|
||||
|
||||
typedef NS_OPTIONS(NSUInteger, SampleCount) {
|
||||
SampleCount1 = (1UL << 0),
|
||||
SampleCount2 = (1UL << 1),
|
||||
SampleCount4 = (1UL << 2),
|
||||
SampleCount8 = (1UL << 3),
|
||||
SampleCount16 = (1UL << 4),
|
||||
SampleCount32 = (1UL << 5),
|
||||
SampleCount64 = (1UL << 6),
|
||||
};
|
||||
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0)) MetalFeatures {
|
||||
uint32_t mslVersion;
|
||||
MTLGPUFamily highestFamily;
|
||||
MTLLanguageVersion mslVersionEnum;
|
||||
SampleCount supportedSampleCounts;
|
||||
long hostMemoryPageSize;
|
||||
bool layeredRendering;
|
||||
bool multisampleLayeredRendering;
|
||||
bool quadPermute; /**< If true, quadgroup permutation functions (vote, ballot, shuffle) are supported in shaders. */
|
||||
bool simdPermute; /**< If true, SIMD-group permutation functions (vote, ballot, shuffle) are supported in shaders. */
|
||||
bool simdReduction; /**< If true, SIMD-group reduction functions (arithmetic) are supported in shaders. */
|
||||
bool tessellationShader; /**< If true, tessellation shaders are supported. */
|
||||
bool imageCubeArray; /**< If true, image cube arrays are supported. */
|
||||
};
|
||||
|
||||
struct MetalLimits {
|
||||
uint64_t maxImageArrayLayers;
|
||||
uint64_t maxFramebufferHeight;
|
||||
uint64_t maxFramebufferWidth;
|
||||
uint64_t maxImageDimension1D;
|
||||
uint64_t maxImageDimension2D;
|
||||
uint64_t maxImageDimension3D;
|
||||
uint64_t maxImageDimensionCube;
|
||||
uint64_t maxViewportDimensionX;
|
||||
uint64_t maxViewportDimensionY;
|
||||
MTLSize maxThreadsPerThreadGroup;
|
||||
MTLSize maxComputeWorkGroupCount;
|
||||
uint64_t maxBoundDescriptorSets;
|
||||
uint64_t maxColorAttachments;
|
||||
uint64_t maxTexturesPerArgumentBuffer;
|
||||
uint64_t maxSamplersPerArgumentBuffer;
|
||||
uint64_t maxBuffersPerArgumentBuffer;
|
||||
uint64_t maxBufferLength;
|
||||
uint64_t minUniformBufferOffsetAlignment;
|
||||
uint64_t maxVertexDescriptorLayoutStride;
|
||||
uint16_t maxViewports;
|
||||
uint32_t maxPerStageBufferCount; /**< The total number of per-stage Metal buffers available for shader uniform content and attributes. */
|
||||
uint32_t maxPerStageTextureCount; /**< The total number of per-stage Metal textures available for shader uniform content. */
|
||||
uint32_t maxPerStageSamplerCount; /**< The total number of per-stage Metal samplers available for shader uniform content. */
|
||||
uint32_t maxVertexInputAttributes;
|
||||
uint32_t maxVertexInputBindings;
|
||||
uint32_t maxVertexInputBindingStride;
|
||||
uint32_t maxDrawIndexedIndexValue;
|
||||
|
||||
uint32_t minSubgroupSize; /**< The minimum number of threads in a SIMD-group. */
|
||||
uint32_t maxSubgroupSize; /**< The maximum number of threads in a SIMD-group. */
|
||||
BitField<RDD::ShaderStage> subgroupSupportedShaderStages;
|
||||
BitField<RD::SubgroupOperations> subgroupSupportedOperations; /**< The subgroup operations supported by the device. */
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MetalDeviceProperties {
|
||||
private:
|
||||
void init_features(id<MTLDevice> p_device);
|
||||
void init_limits(id<MTLDevice> p_device);
|
||||
|
||||
public:
|
||||
MetalFeatures features;
|
||||
MetalLimits limits;
|
||||
|
||||
SampleCount find_nearest_supported_sample_count(RenderingDevice::TextureSamples p_samples) const;
|
||||
|
||||
MetalDeviceProperties(id<MTLDevice> p_device);
|
||||
~MetalDeviceProperties();
|
||||
|
||||
private:
|
||||
static const SampleCount sample_count[RenderingDevice::TextureSamples::TEXTURE_SAMPLES_MAX];
|
||||
};
|
||||
|
||||
#endif // METAL_DEVICE_PROPERTIES_H
|
327
drivers/metal/metal_device_properties.mm
Normal file
327
drivers/metal/metal_device_properties.mm
Normal file
@ -0,0 +1,327 @@
|
||||
/**************************************************************************/
|
||||
/* metal_device_properties.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* Portions of this code were derived from MoltenVK. */
|
||||
/* */
|
||||
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
|
||||
/* (http://www.brenwill.com) */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "metal_device_properties.h"
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <spirv_cross.hpp>
|
||||
#import <spirv_msl.hpp>
|
||||
|
||||
// Common scaling multipliers.
|
||||
#define KIBI (1024)
|
||||
#define MEBI (KIBI * KIBI)
|
||||
|
||||
#if (TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED < 140000) || (TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED < 170000)
|
||||
#define MTLGPUFamilyApple9 (MTLGPUFamily)1009
|
||||
#endif
|
||||
|
||||
API_AVAILABLE(macos(11.0), ios(14.0))
|
||||
MTLGPUFamily &operator--(MTLGPUFamily &p_family) {
|
||||
p_family = static_cast<MTLGPUFamily>(static_cast<int>(p_family) - 1);
|
||||
if (p_family < MTLGPUFamilyApple1) {
|
||||
p_family = MTLGPUFamilyApple9;
|
||||
}
|
||||
|
||||
return p_family;
|
||||
}
|
||||
|
||||
void MetalDeviceProperties::init_features(id<MTLDevice> p_device) {
|
||||
features = {};
|
||||
|
||||
features.highestFamily = MTLGPUFamilyApple1;
|
||||
for (MTLGPUFamily family = MTLGPUFamilyApple9; family >= MTLGPUFamilyApple1; --family) {
|
||||
if ([p_device supportsFamily:family]) {
|
||||
features.highestFamily = family;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
features.hostMemoryPageSize = sysconf(_SC_PAGESIZE);
|
||||
|
||||
for (SampleCount sc = SampleCount1; sc <= SampleCount64; sc <<= 1) {
|
||||
if ([p_device supportsTextureSampleCount:sc]) {
|
||||
features.supportedSampleCounts |= sc;
|
||||
}
|
||||
}
|
||||
|
||||
features.layeredRendering = [p_device supportsFamily:MTLGPUFamilyApple5];
|
||||
features.multisampleLayeredRendering = [p_device supportsFamily:MTLGPUFamilyApple7];
|
||||
features.tessellationShader = [p_device supportsFamily:MTLGPUFamilyApple3];
|
||||
features.imageCubeArray = [p_device supportsFamily:MTLGPUFamilyApple3];
|
||||
features.quadPermute = [p_device supportsFamily:MTLGPUFamilyApple4];
|
||||
features.simdPermute = [p_device supportsFamily:MTLGPUFamilyApple6];
|
||||
features.simdReduction = [p_device supportsFamily:MTLGPUFamilyApple7];
|
||||
|
||||
MTLCompileOptions *opts = [MTLCompileOptions new];
|
||||
features.mslVersionEnum = opts.languageVersion; // By default, Metal uses the most recent language version.
|
||||
|
||||
#define setMSLVersion(m_maj, m_min) \
|
||||
features.mslVersion = SPIRV_CROSS_NAMESPACE::CompilerMSL::Options::make_msl_version(m_maj, m_min)
|
||||
|
||||
switch (features.mslVersionEnum) {
|
||||
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 150000 || __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000
|
||||
case MTLLanguageVersion3_2:
|
||||
setMSLVersion(3, 2);
|
||||
break;
|
||||
#endif
|
||||
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 140000 || __IPHONE_OS_VERSION_MAX_ALLOWED >= 170000
|
||||
case MTLLanguageVersion3_1:
|
||||
setMSLVersion(3, 1);
|
||||
break;
|
||||
#endif
|
||||
case MTLLanguageVersion3_0:
|
||||
setMSLVersion(3, 0);
|
||||
break;
|
||||
case MTLLanguageVersion2_4:
|
||||
setMSLVersion(2, 4);
|
||||
break;
|
||||
case MTLLanguageVersion2_3:
|
||||
setMSLVersion(2, 3);
|
||||
break;
|
||||
case MTLLanguageVersion2_2:
|
||||
setMSLVersion(2, 2);
|
||||
break;
|
||||
case MTLLanguageVersion2_1:
|
||||
setMSLVersion(2, 1);
|
||||
break;
|
||||
case MTLLanguageVersion2_0:
|
||||
setMSLVersion(2, 0);
|
||||
break;
|
||||
case MTLLanguageVersion1_2:
|
||||
setMSLVersion(1, 2);
|
||||
break;
|
||||
case MTLLanguageVersion1_1:
|
||||
setMSLVersion(1, 1);
|
||||
break;
|
||||
#if TARGET_OS_IPHONE && !TARGET_OS_MACCATALYST
|
||||
case MTLLanguageVersion1_0:
|
||||
setMSLVersion(1, 0);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
|
||||
using std::max;
|
||||
using std::min;
|
||||
|
||||
// FST: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
|
||||
|
||||
// FST: Maximum number of layers per 1D texture array, 2D texture array, or 3D texture.
|
||||
limits.maxImageArrayLayers = 2048;
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple3]) {
|
||||
// FST: Maximum 2D texture width and height.
|
||||
limits.maxFramebufferWidth = 16384;
|
||||
limits.maxFramebufferHeight = 16384;
|
||||
limits.maxViewportDimensionX = 16384;
|
||||
limits.maxViewportDimensionY = 16384;
|
||||
// FST: Maximum 1D texture width.
|
||||
limits.maxImageDimension1D = 16384;
|
||||
// FST: Maximum 2D texture width and height.
|
||||
limits.maxImageDimension2D = 16384;
|
||||
// FST: Maximum cube map texture width and height.
|
||||
limits.maxImageDimensionCube = 16384;
|
||||
} else {
|
||||
// FST: Maximum 2D texture width and height.
|
||||
limits.maxFramebufferWidth = 8192;
|
||||
limits.maxFramebufferHeight = 8192;
|
||||
limits.maxViewportDimensionX = 8192;
|
||||
limits.maxViewportDimensionY = 8192;
|
||||
// FST: Maximum 1D texture width.
|
||||
limits.maxImageDimension1D = 8192;
|
||||
// FST: Maximum 2D texture width and height.
|
||||
limits.maxImageDimension2D = 8192;
|
||||
// FST: Maximum cube map texture width and height.
|
||||
limits.maxImageDimensionCube = 8192;
|
||||
}
|
||||
// FST: Maximum 3D texture width, height, and depth.
|
||||
limits.maxImageDimension3D = 2048;
|
||||
|
||||
limits.maxThreadsPerThreadGroup = p_device.maxThreadsPerThreadgroup;
|
||||
// No effective limits.
|
||||
limits.maxComputeWorkGroupCount = { std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max() };
|
||||
// https://github.com/KhronosGroup/MoltenVK/blob/568cc3acc0e2299931fdaecaaa1fc3ec5b4af281/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h#L85
|
||||
limits.maxBoundDescriptorSets = SPIRV_CROSS_NAMESPACE::kMaxArgumentBuffers;
|
||||
// FST: Maximum number of color render targets per render pass descriptor.
|
||||
limits.maxColorAttachments = 8;
|
||||
|
||||
// Maximum number of textures the device can access, per stage, from an argument buffer.
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
|
||||
limits.maxTexturesPerArgumentBuffer = 1'000'000;
|
||||
} else if ([p_device supportsFamily:MTLGPUFamilyApple4]) {
|
||||
limits.maxTexturesPerArgumentBuffer = 96;
|
||||
} else {
|
||||
limits.maxTexturesPerArgumentBuffer = 31;
|
||||
}
|
||||
|
||||
// Maximum number of samplers the device can access, per stage, from an argument buffer.
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
|
||||
limits.maxSamplersPerArgumentBuffer = 1024;
|
||||
} else {
|
||||
limits.maxSamplersPerArgumentBuffer = 16;
|
||||
}
|
||||
|
||||
// Maximum number of buffers the device can access, per stage, from an argument buffer.
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
|
||||
limits.maxBuffersPerArgumentBuffer = std::numeric_limits<uint64_t>::max();
|
||||
} else if ([p_device supportsFamily:MTLGPUFamilyApple4]) {
|
||||
limits.maxBuffersPerArgumentBuffer = 96;
|
||||
} else {
|
||||
limits.maxBuffersPerArgumentBuffer = 31;
|
||||
}
|
||||
|
||||
limits.minSubgroupSize = limits.maxSubgroupSize = 1;
|
||||
// These values were taken from MoltenVK.
|
||||
if (features.simdPermute) {
|
||||
limits.minSubgroupSize = 4;
|
||||
limits.maxSubgroupSize = 32;
|
||||
} else if (features.quadPermute) {
|
||||
limits.minSubgroupSize = limits.maxSubgroupSize = 4;
|
||||
}
|
||||
|
||||
limits.subgroupSupportedShaderStages.set_flag(RDD::ShaderStage::SHADER_STAGE_COMPUTE_BIT);
|
||||
if (features.tessellationShader) {
|
||||
limits.subgroupSupportedShaderStages.set_flag(RDD::ShaderStage::SHADER_STAGE_TESSELATION_CONTROL_BIT);
|
||||
}
|
||||
limits.subgroupSupportedShaderStages.set_flag(RDD::ShaderStage::SHADER_STAGE_FRAGMENT_BIT);
|
||||
|
||||
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_BASIC_BIT);
|
||||
if (features.simdPermute || features.quadPermute) {
|
||||
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_VOTE_BIT);
|
||||
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_BALLOT_BIT);
|
||||
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_SHUFFLE_BIT);
|
||||
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_SHUFFLE_RELATIVE_BIT);
|
||||
}
|
||||
|
||||
if (features.simdReduction) {
|
||||
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_ARITHMETIC_BIT);
|
||||
}
|
||||
|
||||
if (features.quadPermute) {
|
||||
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_QUAD_BIT);
|
||||
}
|
||||
|
||||
limits.maxBufferLength = p_device.maxBufferLength;
|
||||
|
||||
// FST: Maximum size of vertex descriptor layout stride.
|
||||
limits.maxVertexDescriptorLayoutStride = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
// Maximum number of viewports.
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple5]) {
|
||||
limits.maxViewports = 16;
|
||||
} else {
|
||||
limits.maxViewports = 1;
|
||||
}
|
||||
|
||||
limits.maxPerStageBufferCount = 31;
|
||||
limits.maxPerStageSamplerCount = 16;
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
|
||||
limits.maxPerStageTextureCount = 128;
|
||||
} else if ([p_device supportsFamily:MTLGPUFamilyApple4]) {
|
||||
limits.maxPerStageTextureCount = 96;
|
||||
} else {
|
||||
limits.maxPerStageTextureCount = 31;
|
||||
}
|
||||
|
||||
limits.maxVertexInputAttributes = 31;
|
||||
limits.maxVertexInputBindings = 31;
|
||||
limits.maxVertexInputBindingStride = (2 * KIBI);
|
||||
|
||||
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
|
||||
limits.minUniformBufferOffsetAlignment = 64;
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
// This is Apple Silicon specific.
|
||||
limits.minUniformBufferOffsetAlignment = 16;
|
||||
#endif
|
||||
|
||||
limits.maxDrawIndexedIndexValue = std::numeric_limits<uint32_t>::max() - 1;
|
||||
}
|
||||
|
||||
MetalDeviceProperties::MetalDeviceProperties(id<MTLDevice> p_device) {
|
||||
init_features(p_device);
|
||||
init_limits(p_device);
|
||||
}
|
||||
|
||||
MetalDeviceProperties::~MetalDeviceProperties() {
|
||||
}
|
||||
|
||||
SampleCount MetalDeviceProperties::find_nearest_supported_sample_count(RenderingDevice::TextureSamples p_samples) const {
|
||||
SampleCount supported = features.supportedSampleCounts;
|
||||
if (supported & sample_count[p_samples]) {
|
||||
return sample_count[p_samples];
|
||||
}
|
||||
|
||||
SampleCount requested_sample_count = sample_count[p_samples];
|
||||
// Find the nearest supported sample count.
|
||||
while (requested_sample_count > SampleCount1) {
|
||||
if (supported & requested_sample_count) {
|
||||
return requested_sample_count;
|
||||
}
|
||||
requested_sample_count = (SampleCount)(requested_sample_count >> 1);
|
||||
}
|
||||
|
||||
return SampleCount1;
|
||||
}
|
||||
|
||||
// region static members
|
||||
|
||||
const SampleCount MetalDeviceProperties::sample_count[RenderingDevice::TextureSamples::TEXTURE_SAMPLES_MAX] = {
|
||||
SampleCount1,
|
||||
SampleCount2,
|
||||
SampleCount4,
|
||||
SampleCount8,
|
||||
SampleCount16,
|
||||
SampleCount32,
|
||||
SampleCount64,
|
||||
};
|
||||
|
||||
// endregion
|
838
drivers/metal/metal_objects.h
Normal file
838
drivers/metal/metal_objects.h
Normal file
@ -0,0 +1,838 @@
|
||||
/**************************************************************************/
|
||||
/* metal_objects.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* Portions of this code were derived from MoltenVK. */
|
||||
/* */
|
||||
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
|
||||
/* (http://www.brenwill.com) */
|
||||
/* */
|
||||
/* 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 METAL_OBJECTS_H
|
||||
#define METAL_OBJECTS_H
|
||||
|
||||
#import "metal_device_properties.h"
|
||||
#import "metal_utils.h"
|
||||
#import "pixel_formats.h"
|
||||
|
||||
#import "servers/rendering/rendering_device_driver.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
#import <simd/simd.h>
|
||||
#import <initializer_list>
|
||||
#import <optional>
|
||||
#import <spirv.hpp>
|
||||
|
||||
// These types can be used in Vector and other containers that use
|
||||
// pointer operations not supported by ARC.
|
||||
namespace MTL {
|
||||
#define MTL_CLASS(name) \
|
||||
class name { \
|
||||
public: \
|
||||
name(id<MTL##name> obj = nil) : m_obj(obj) {} \
|
||||
operator id<MTL##name>() const { return m_obj; } \
|
||||
id<MTL##name> m_obj; \
|
||||
};
|
||||
|
||||
MTL_CLASS(Texture)
|
||||
|
||||
} //namespace MTL
|
||||
|
||||
enum ShaderStageUsage : uint32_t {
|
||||
None = 0,
|
||||
Vertex = RDD::SHADER_STAGE_VERTEX_BIT,
|
||||
Fragment = RDD::SHADER_STAGE_FRAGMENT_BIT,
|
||||
TesselationControl = RDD::SHADER_STAGE_TESSELATION_CONTROL_BIT,
|
||||
TesselationEvaluation = RDD::SHADER_STAGE_TESSELATION_EVALUATION_BIT,
|
||||
Compute = RDD::SHADER_STAGE_COMPUTE_BIT,
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ ShaderStageUsage &operator|=(ShaderStageUsage &p_a, int p_b) {
|
||||
p_a = ShaderStageUsage(uint32_t(p_a) | uint32_t(p_b));
|
||||
return p_a;
|
||||
}
|
||||
|
||||
enum class MDCommandBufferStateType {
|
||||
None,
|
||||
Render,
|
||||
Compute,
|
||||
Blit,
|
||||
};
|
||||
|
||||
enum class MDPipelineType {
|
||||
None,
|
||||
Render,
|
||||
Compute,
|
||||
};
|
||||
|
||||
class MDRenderPass;
|
||||
class MDPipeline;
|
||||
class MDRenderPipeline;
|
||||
class MDComputePipeline;
|
||||
class MDFrameBuffer;
|
||||
class RenderingDeviceDriverMetal;
|
||||
class MDUniformSet;
|
||||
class MDShader;
|
||||
|
||||
#pragma mark - Resource Factory
|
||||
|
||||
struct ClearAttKey {
|
||||
const static uint32_t COLOR_COUNT = MAX_COLOR_ATTACHMENT_COUNT;
|
||||
const static uint32_t DEPTH_INDEX = COLOR_COUNT;
|
||||
const static uint32_t STENCIL_INDEX = DEPTH_INDEX + 1;
|
||||
const static uint32_t ATTACHMENT_COUNT = STENCIL_INDEX + 1;
|
||||
|
||||
uint16_t sample_count = 0;
|
||||
uint16_t pixel_formats[ATTACHMENT_COUNT] = { 0 };
|
||||
|
||||
_FORCE_INLINE_ void set_color_format(uint32_t p_idx, MTLPixelFormat p_fmt) { pixel_formats[p_idx] = p_fmt; }
|
||||
_FORCE_INLINE_ void set_depth_format(MTLPixelFormat p_fmt) { pixel_formats[DEPTH_INDEX] = p_fmt; }
|
||||
_FORCE_INLINE_ void set_stencil_format(MTLPixelFormat p_fmt) { pixel_formats[STENCIL_INDEX] = p_fmt; }
|
||||
_FORCE_INLINE_ MTLPixelFormat depth_format() const { return (MTLPixelFormat)pixel_formats[DEPTH_INDEX]; }
|
||||
_FORCE_INLINE_ MTLPixelFormat stencil_format() const { return (MTLPixelFormat)pixel_formats[STENCIL_INDEX]; }
|
||||
|
||||
_FORCE_INLINE_ bool is_enabled(uint32_t p_idx) const { return pixel_formats[p_idx] != 0; }
|
||||
_FORCE_INLINE_ bool is_depth_enabled() const { return pixel_formats[DEPTH_INDEX] != 0; }
|
||||
_FORCE_INLINE_ bool is_stencil_enabled() const { return pixel_formats[STENCIL_INDEX] != 0; }
|
||||
|
||||
_FORCE_INLINE_ bool operator==(const ClearAttKey &p_rhs) const {
|
||||
return memcmp(this, &p_rhs, sizeof(ClearAttKey)) == 0;
|
||||
}
|
||||
|
||||
uint32_t hash() const {
|
||||
uint32_t h = hash_murmur3_one_32(sample_count);
|
||||
h = hash_murmur3_buffer(pixel_formats, ATTACHMENT_COUNT * sizeof(pixel_formats[0]), h);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDResourceFactory {
|
||||
private:
|
||||
RenderingDeviceDriverMetal *device_driver;
|
||||
|
||||
id<MTLFunction> new_func(NSString *p_source, NSString *p_name, NSError **p_error);
|
||||
id<MTLFunction> new_clear_vert_func(ClearAttKey &p_key);
|
||||
id<MTLFunction> new_clear_frag_func(ClearAttKey &p_key);
|
||||
NSString *get_format_type_string(MTLPixelFormat p_fmt);
|
||||
|
||||
public:
|
||||
id<MTLRenderPipelineState> new_clear_pipeline_state(ClearAttKey &p_key, NSError **p_error);
|
||||
id<MTLDepthStencilState> new_depth_stencil_state(bool p_use_depth, bool p_use_stencil);
|
||||
|
||||
MDResourceFactory(RenderingDeviceDriverMetal *p_device_driver) :
|
||||
device_driver(p_device_driver) {}
|
||||
~MDResourceFactory() = default;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDResourceCache {
|
||||
private:
|
||||
typedef HashMap<ClearAttKey, id<MTLRenderPipelineState>, HashableHasher<ClearAttKey>> HashMap;
|
||||
std::unique_ptr<MDResourceFactory> resource_factory;
|
||||
HashMap clear_states;
|
||||
|
||||
struct {
|
||||
id<MTLDepthStencilState> all;
|
||||
id<MTLDepthStencilState> depth_only;
|
||||
id<MTLDepthStencilState> stencil_only;
|
||||
id<MTLDepthStencilState> none;
|
||||
} clear_depth_stencil_state;
|
||||
|
||||
public:
|
||||
id<MTLRenderPipelineState> get_clear_render_pipeline_state(ClearAttKey &p_key, NSError **p_error);
|
||||
id<MTLDepthStencilState> get_depth_stencil_state(bool p_use_depth, bool p_use_stencil);
|
||||
|
||||
explicit MDResourceCache(RenderingDeviceDriverMetal *p_device_driver) :
|
||||
resource_factory(new MDResourceFactory(p_device_driver)) {}
|
||||
~MDResourceCache() = default;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDCommandBuffer {
|
||||
private:
|
||||
RenderingDeviceDriverMetal *device_driver = nullptr;
|
||||
id<MTLCommandQueue> queue = nil;
|
||||
id<MTLCommandBuffer> commandBuffer = nil;
|
||||
|
||||
void _end_compute_dispatch();
|
||||
void _end_blit();
|
||||
|
||||
#pragma mark - Render
|
||||
|
||||
void _render_set_dirty_state();
|
||||
void _render_bind_uniform_sets();
|
||||
|
||||
static void _populate_vertices(simd::float4 *p_vertices, Size2i p_fb_size, VectorView<Rect2i> p_rects);
|
||||
static uint32_t _populate_vertices(simd::float4 *p_vertices, uint32_t p_index, Rect2i const &p_rect, Size2i p_fb_size);
|
||||
void _end_render_pass();
|
||||
void _render_clear_render_area();
|
||||
|
||||
public:
|
||||
MDCommandBufferStateType type = MDCommandBufferStateType::None;
|
||||
|
||||
struct RenderState {
|
||||
MDRenderPass *pass = nullptr;
|
||||
MDFrameBuffer *frameBuffer = nullptr;
|
||||
MDRenderPipeline *pipeline = nullptr;
|
||||
LocalVector<RDD::RenderPassClearValue> clear_values;
|
||||
LocalVector<MTLViewport> viewports;
|
||||
LocalVector<MTLScissorRect> scissors;
|
||||
std::optional<Color> blend_constants;
|
||||
uint32_t current_subpass = UINT32_MAX;
|
||||
Rect2i render_area = {};
|
||||
bool is_rendering_entire_area = false;
|
||||
MTLRenderPassDescriptor *desc = nil;
|
||||
id<MTLRenderCommandEncoder> encoder = nil;
|
||||
id<MTLBuffer> __unsafe_unretained index_buffer = nil; // Buffer is owned by RDD.
|
||||
MTLIndexType index_type = MTLIndexTypeUInt16;
|
||||
LocalVector<id<MTLBuffer> __unsafe_unretained> vertex_buffers;
|
||||
LocalVector<NSUInteger> vertex_offsets;
|
||||
// clang-format off
|
||||
enum DirtyFlag: uint8_t {
|
||||
DIRTY_NONE = 0b0000'0000,
|
||||
DIRTY_PIPELINE = 0b0000'0001, //! pipeline state
|
||||
DIRTY_UNIFORMS = 0b0000'0010, //! uniform sets
|
||||
DIRTY_DEPTH = 0b0000'0100, //! depth / stenci state
|
||||
DIRTY_VERTEX = 0b0000'1000, //! vertex buffers
|
||||
DIRTY_VIEWPORT = 0b0001'0000, //! viewport rectangles
|
||||
DIRTY_SCISSOR = 0b0010'0000, //! scissor rectangles
|
||||
DIRTY_BLEND = 0b0100'0000, //! blend state
|
||||
DIRTY_RASTER = 0b1000'0000, //! encoder state like cull mode
|
||||
|
||||
DIRTY_ALL = 0xff,
|
||||
};
|
||||
// clang-format on
|
||||
BitField<DirtyFlag> dirty = DIRTY_NONE;
|
||||
|
||||
LocalVector<MDUniformSet *> uniform_sets;
|
||||
// Bit mask of the uniform sets that are dirty, to prevent redundant binding.
|
||||
uint64_t uniform_set_mask = 0;
|
||||
|
||||
_FORCE_INLINE_ void reset() {
|
||||
pass = nil;
|
||||
frameBuffer = nil;
|
||||
pipeline = nil;
|
||||
current_subpass = UINT32_MAX;
|
||||
render_area = {};
|
||||
is_rendering_entire_area = false;
|
||||
desc = nil;
|
||||
encoder = nil;
|
||||
index_buffer = nil;
|
||||
index_type = MTLIndexTypeUInt16;
|
||||
dirty = DIRTY_NONE;
|
||||
uniform_sets.clear();
|
||||
uniform_set_mask = 0;
|
||||
clear_values.clear();
|
||||
viewports.clear();
|
||||
scissors.clear();
|
||||
blend_constants.reset();
|
||||
vertex_buffers.clear();
|
||||
vertex_offsets.clear();
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void mark_viewport_dirty() {
|
||||
if (viewports.is_empty()) {
|
||||
return;
|
||||
}
|
||||
dirty.set_flag(DirtyFlag::DIRTY_VIEWPORT);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void mark_scissors_dirty() {
|
||||
if (scissors.is_empty()) {
|
||||
return;
|
||||
}
|
||||
dirty.set_flag(DirtyFlag::DIRTY_SCISSOR);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void mark_vertex_dirty() {
|
||||
if (vertex_buffers.is_empty()) {
|
||||
return;
|
||||
}
|
||||
dirty.set_flag(DirtyFlag::DIRTY_VERTEX);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void mark_uniforms_dirty(std::initializer_list<uint32_t> l) {
|
||||
if (uniform_sets.is_empty()) {
|
||||
return;
|
||||
}
|
||||
for (uint32_t i : l) {
|
||||
if (i < uniform_sets.size() && uniform_sets[i] != nullptr) {
|
||||
uniform_set_mask |= 1 << i;
|
||||
}
|
||||
}
|
||||
dirty.set_flag(DirtyFlag::DIRTY_UNIFORMS);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void mark_uniforms_dirty(void) {
|
||||
if (uniform_sets.is_empty()) {
|
||||
return;
|
||||
}
|
||||
for (uint32_t i = 0; i < uniform_sets.size(); i++) {
|
||||
if (uniform_sets[i] != nullptr) {
|
||||
uniform_set_mask |= 1 << i;
|
||||
}
|
||||
}
|
||||
dirty.set_flag(DirtyFlag::DIRTY_UNIFORMS);
|
||||
}
|
||||
|
||||
MTLScissorRect clip_to_render_area(MTLScissorRect p_rect) const {
|
||||
uint32_t raLeft = render_area.position.x;
|
||||
uint32_t raRight = raLeft + render_area.size.width;
|
||||
uint32_t raBottom = render_area.position.y;
|
||||
uint32_t raTop = raBottom + render_area.size.height;
|
||||
|
||||
p_rect.x = CLAMP(p_rect.x, raLeft, MAX(raRight - 1, raLeft));
|
||||
p_rect.y = CLAMP(p_rect.y, raBottom, MAX(raTop - 1, raBottom));
|
||||
p_rect.width = MIN(p_rect.width, raRight - p_rect.x);
|
||||
p_rect.height = MIN(p_rect.height, raTop - p_rect.y);
|
||||
|
||||
return p_rect;
|
||||
}
|
||||
|
||||
Rect2i clip_to_render_area(Rect2i p_rect) const {
|
||||
int32_t raLeft = render_area.position.x;
|
||||
int32_t raRight = raLeft + render_area.size.width;
|
||||
int32_t raBottom = render_area.position.y;
|
||||
int32_t raTop = raBottom + render_area.size.height;
|
||||
|
||||
p_rect.position.x = CLAMP(p_rect.position.x, raLeft, MAX(raRight - 1, raLeft));
|
||||
p_rect.position.y = CLAMP(p_rect.position.y, raBottom, MAX(raTop - 1, raBottom));
|
||||
p_rect.size.width = MIN(p_rect.size.width, raRight - p_rect.position.x);
|
||||
p_rect.size.height = MIN(p_rect.size.height, raTop - p_rect.position.y);
|
||||
|
||||
return p_rect;
|
||||
}
|
||||
|
||||
} render;
|
||||
|
||||
// State specific for a compute pass.
|
||||
struct {
|
||||
MDComputePipeline *pipeline = nullptr;
|
||||
id<MTLComputeCommandEncoder> encoder = nil;
|
||||
_FORCE_INLINE_ void reset() {
|
||||
pipeline = nil;
|
||||
encoder = nil;
|
||||
}
|
||||
} compute;
|
||||
|
||||
// State specific to a blit pass.
|
||||
struct {
|
||||
id<MTLBlitCommandEncoder> encoder = nil;
|
||||
_FORCE_INLINE_ void reset() {
|
||||
encoder = nil;
|
||||
}
|
||||
} blit;
|
||||
|
||||
_FORCE_INLINE_ id<MTLCommandBuffer> get_command_buffer() const {
|
||||
return commandBuffer;
|
||||
}
|
||||
|
||||
void begin();
|
||||
void commit();
|
||||
void end();
|
||||
|
||||
id<MTLBlitCommandEncoder> blit_command_encoder();
|
||||
void encodeRenderCommandEncoderWithDescriptor(MTLRenderPassDescriptor *p_desc, NSString *p_label);
|
||||
|
||||
void bind_pipeline(RDD::PipelineID p_pipeline);
|
||||
|
||||
#pragma mark - Render Commands
|
||||
|
||||
void render_bind_uniform_set(RDD::UniformSetID p_uniform_set, RDD::ShaderID p_shader, uint32_t p_set_index);
|
||||
void render_clear_attachments(VectorView<RDD::AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects);
|
||||
void render_set_viewport(VectorView<Rect2i> p_viewports);
|
||||
void render_set_scissor(VectorView<Rect2i> p_scissors);
|
||||
void render_set_blend_constants(const Color &p_constants);
|
||||
void render_begin_pass(RDD::RenderPassID p_render_pass,
|
||||
RDD::FramebufferID p_frameBuffer,
|
||||
RDD::CommandBufferType p_cmd_buffer_type,
|
||||
const Rect2i &p_rect,
|
||||
VectorView<RDD::RenderPassClearValue> p_clear_values);
|
||||
void render_next_subpass();
|
||||
void render_draw(uint32_t p_vertex_count,
|
||||
uint32_t p_instance_count,
|
||||
uint32_t p_base_vertex,
|
||||
uint32_t p_first_instance);
|
||||
void render_bind_vertex_buffers(uint32_t p_binding_count, const RDD::BufferID *p_buffers, const uint64_t *p_offsets);
|
||||
void render_bind_index_buffer(RDD::BufferID p_buffer, RDD::IndexBufferFormat p_format, uint64_t p_offset);
|
||||
|
||||
void render_draw_indexed(uint32_t p_index_count,
|
||||
uint32_t p_instance_count,
|
||||
uint32_t p_first_index,
|
||||
int32_t p_vertex_offset,
|
||||
uint32_t p_first_instance);
|
||||
|
||||
void render_draw_indexed_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride);
|
||||
void render_draw_indexed_indirect_count(RDD::BufferID p_indirect_buffer, uint64_t p_offset, RDD::BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride);
|
||||
void render_draw_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride);
|
||||
void render_draw_indirect_count(RDD::BufferID p_indirect_buffer, uint64_t p_offset, RDD::BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride);
|
||||
|
||||
void render_end_pass();
|
||||
|
||||
#pragma mark - Compute Commands
|
||||
|
||||
void compute_bind_uniform_set(RDD::UniformSetID p_uniform_set, RDD::ShaderID p_shader, uint32_t p_set_index);
|
||||
void compute_dispatch(uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups);
|
||||
void compute_dispatch_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset);
|
||||
|
||||
MDCommandBuffer(id<MTLCommandQueue> p_queue, RenderingDeviceDriverMetal *p_device_driver) :
|
||||
device_driver(p_device_driver), queue(p_queue) {
|
||||
type = MDCommandBufferStateType::None;
|
||||
}
|
||||
|
||||
MDCommandBuffer() = default;
|
||||
};
|
||||
|
||||
#if (TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED < 140000) || (TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED < 170000)
|
||||
#define MTLBindingAccess MTLArgumentAccess
|
||||
#define MTLBindingAccessReadOnly MTLArgumentAccessReadOnly
|
||||
#define MTLBindingAccessReadWrite MTLArgumentAccessReadWrite
|
||||
#define MTLBindingAccessWriteOnly MTLArgumentAccessWriteOnly
|
||||
#endif
|
||||
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0)) BindingInfo {
|
||||
MTLDataType dataType = MTLDataTypeNone;
|
||||
uint32_t index = 0;
|
||||
MTLBindingAccess access = MTLBindingAccessReadOnly;
|
||||
MTLResourceUsage usage = 0;
|
||||
MTLTextureType textureType = MTLTextureType2D;
|
||||
spv::ImageFormat imageFormat = spv::ImageFormatUnknown;
|
||||
uint32_t arrayLength = 0;
|
||||
bool isMultisampled = false;
|
||||
|
||||
inline MTLArgumentDescriptor *new_argument_descriptor() const {
|
||||
MTLArgumentDescriptor *desc = MTLArgumentDescriptor.argumentDescriptor;
|
||||
desc.dataType = dataType;
|
||||
desc.index = index;
|
||||
desc.access = access;
|
||||
desc.textureType = textureType;
|
||||
desc.arrayLength = arrayLength;
|
||||
return desc;
|
||||
}
|
||||
|
||||
size_t serialize_size() const {
|
||||
return sizeof(uint32_t) * 8 /* 8 uint32_t fields */;
|
||||
}
|
||||
|
||||
template <typename W>
|
||||
void serialize(W &p_writer) const {
|
||||
p_writer.write((uint32_t)dataType);
|
||||
p_writer.write(index);
|
||||
p_writer.write((uint32_t)access);
|
||||
p_writer.write((uint32_t)usage);
|
||||
p_writer.write((uint32_t)textureType);
|
||||
p_writer.write(imageFormat);
|
||||
p_writer.write(arrayLength);
|
||||
p_writer.write(isMultisampled);
|
||||
}
|
||||
|
||||
template <typename R>
|
||||
void deserialize(R &p_reader) {
|
||||
p_reader.read((uint32_t &)dataType);
|
||||
p_reader.read(index);
|
||||
p_reader.read((uint32_t &)access);
|
||||
p_reader.read((uint32_t &)usage);
|
||||
p_reader.read((uint32_t &)textureType);
|
||||
p_reader.read((uint32_t &)imageFormat);
|
||||
p_reader.read(arrayLength);
|
||||
p_reader.read(isMultisampled);
|
||||
}
|
||||
};
|
||||
|
||||
using RDC = RenderingDeviceCommons;
|
||||
|
||||
typedef API_AVAILABLE(macos(11.0), ios(14.0)) HashMap<RDC::ShaderStage, BindingInfo> BindingInfoMap;
|
||||
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0)) UniformInfo {
|
||||
uint32_t binding;
|
||||
ShaderStageUsage active_stages = None;
|
||||
BindingInfoMap bindings;
|
||||
BindingInfoMap bindings_secondary;
|
||||
};
|
||||
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0)) UniformSet {
|
||||
LocalVector<UniformInfo> uniforms;
|
||||
uint32_t buffer_size = 0;
|
||||
HashMap<RDC::ShaderStage, uint32_t> offsets;
|
||||
HashMap<RDC::ShaderStage, id<MTLArgumentEncoder>> encoders;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDShader {
|
||||
public:
|
||||
CharString name;
|
||||
Vector<UniformSet> sets;
|
||||
|
||||
virtual void encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) = 0;
|
||||
|
||||
MDShader(CharString p_name, Vector<UniformSet> p_sets) :
|
||||
name(p_name), sets(p_sets) {}
|
||||
virtual ~MDShader() = default;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDComputeShader final : public MDShader {
|
||||
public:
|
||||
struct {
|
||||
uint32_t binding = -1;
|
||||
uint32_t size = 0;
|
||||
} push_constants;
|
||||
MTLSize local = {};
|
||||
|
||||
id<MTLLibrary> kernel;
|
||||
#if DEV_ENABLED
|
||||
CharString kernel_source;
|
||||
#endif
|
||||
|
||||
void encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) final;
|
||||
|
||||
MDComputeShader(CharString p_name, Vector<UniformSet> p_sets, id<MTLLibrary> p_kernel);
|
||||
~MDComputeShader() override = default;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDRenderShader final : public MDShader {
|
||||
public:
|
||||
struct {
|
||||
struct {
|
||||
int32_t binding = -1;
|
||||
uint32_t size = 0;
|
||||
} vert;
|
||||
struct {
|
||||
int32_t binding = -1;
|
||||
uint32_t size = 0;
|
||||
} frag;
|
||||
} push_constants;
|
||||
|
||||
id<MTLLibrary> vert;
|
||||
id<MTLLibrary> frag;
|
||||
#if DEV_ENABLED
|
||||
CharString vert_source;
|
||||
CharString frag_source;
|
||||
#endif
|
||||
|
||||
void encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) final;
|
||||
|
||||
MDRenderShader(CharString p_name, Vector<UniformSet> p_sets, id<MTLLibrary> p_vert, id<MTLLibrary> p_frag);
|
||||
~MDRenderShader() override = default;
|
||||
};
|
||||
|
||||
enum StageResourceUsage : uint32_t {
|
||||
VertexRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_VERTEX * 2),
|
||||
VertexWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_VERTEX * 2),
|
||||
FragmentRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_FRAGMENT * 2),
|
||||
FragmentWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_FRAGMENT * 2),
|
||||
TesselationControlRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
|
||||
TesselationControlWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
|
||||
TesselationEvaluationRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
|
||||
TesselationEvaluationWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
|
||||
ComputeRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_COMPUTE * 2),
|
||||
ComputeWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_COMPUTE * 2),
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ StageResourceUsage &operator|=(StageResourceUsage &p_a, uint32_t p_b) {
|
||||
p_a = StageResourceUsage(uint32_t(p_a) | p_b);
|
||||
return p_a;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ StageResourceUsage stage_resource_usage(RDC::ShaderStage p_stage, MTLResourceUsage p_usage) {
|
||||
return StageResourceUsage(p_usage << (p_stage * 2));
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ MTLResourceUsage resource_usage_for_stage(StageResourceUsage p_usage, RDC::ShaderStage p_stage) {
|
||||
return MTLResourceUsage((p_usage >> (p_stage * 2)) & 0b11);
|
||||
}
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<RDD::ShaderID> {
|
||||
static bool compare(const RDD::ShaderID &p_lhs, const RDD::ShaderID &p_rhs) {
|
||||
return p_lhs.id == p_rhs.id;
|
||||
}
|
||||
};
|
||||
|
||||
struct BoundUniformSet {
|
||||
id<MTLBuffer> buffer;
|
||||
HashMap<id<MTLResource>, StageResourceUsage> bound_resources;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDUniformSet {
|
||||
public:
|
||||
uint32_t index;
|
||||
LocalVector<RDD::BoundUniform> uniforms;
|
||||
HashMap<MDShader *, BoundUniformSet> bound_uniforms;
|
||||
|
||||
BoundUniformSet &boundUniformSetForShader(MDShader *p_shader, id<MTLDevice> p_device);
|
||||
};
|
||||
|
||||
enum class MDAttachmentType : uint8_t {
|
||||
None = 0,
|
||||
Color = 1 << 0,
|
||||
Depth = 1 << 1,
|
||||
Stencil = 1 << 2,
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ MDAttachmentType &operator|=(MDAttachmentType &p_a, MDAttachmentType p_b) {
|
||||
flags::set(p_a, p_b);
|
||||
return p_a;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool operator&(MDAttachmentType p_a, MDAttachmentType p_b) {
|
||||
return uint8_t(p_a) & uint8_t(p_b);
|
||||
}
|
||||
|
||||
struct MDSubpass {
|
||||
uint32_t subpass_index = 0;
|
||||
LocalVector<RDD::AttachmentReference> input_references;
|
||||
LocalVector<RDD::AttachmentReference> color_references;
|
||||
RDD::AttachmentReference depth_stencil_reference;
|
||||
LocalVector<RDD::AttachmentReference> resolve_references;
|
||||
|
||||
MTLFmtCaps getRequiredFmtCapsForAttachmentAt(uint32_t p_index) const;
|
||||
};
|
||||
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0)) MDAttachment {
|
||||
private:
|
||||
uint32_t index = 0;
|
||||
uint32_t firstUseSubpassIndex = 0;
|
||||
uint32_t lastUseSubpassIndex = 0;
|
||||
|
||||
public:
|
||||
MTLPixelFormat format = MTLPixelFormatInvalid;
|
||||
MDAttachmentType type = MDAttachmentType::None;
|
||||
MTLLoadAction loadAction = MTLLoadActionDontCare;
|
||||
MTLStoreAction storeAction = MTLStoreActionDontCare;
|
||||
MTLLoadAction stencilLoadAction = MTLLoadActionDontCare;
|
||||
MTLStoreAction stencilStoreAction = MTLStoreActionDontCare;
|
||||
uint32_t samples = 1;
|
||||
|
||||
/*!
|
||||
* @brief Returns true if this attachment is first used in the given subpass.
|
||||
* @param p_subpass
|
||||
* @return
|
||||
*/
|
||||
_FORCE_INLINE_ bool isFirstUseOf(MDSubpass const &p_subpass) const {
|
||||
return p_subpass.subpass_index == firstUseSubpassIndex;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Returns true if this attachment is last used in the given subpass.
|
||||
* @param p_subpass
|
||||
* @return
|
||||
*/
|
||||
_FORCE_INLINE_ bool isLastUseOf(MDSubpass const &p_subpass) const {
|
||||
return p_subpass.subpass_index == lastUseSubpassIndex;
|
||||
}
|
||||
|
||||
void linkToSubpass(MDRenderPass const &p_pass);
|
||||
|
||||
MTLStoreAction getMTLStoreAction(MDSubpass const &p_subpass,
|
||||
bool p_is_rendering_entire_area,
|
||||
bool p_has_resolve,
|
||||
bool p_can_resolve,
|
||||
bool p_is_stencil) const;
|
||||
bool configureDescriptor(MTLRenderPassAttachmentDescriptor *p_desc,
|
||||
PixelFormats &p_pf,
|
||||
MDSubpass const &p_subpass,
|
||||
id<MTLTexture> p_attachment,
|
||||
bool p_is_rendering_entire_area,
|
||||
bool p_has_resolve,
|
||||
bool p_can_resolve,
|
||||
bool p_is_stencil) const;
|
||||
/** Returns whether this attachment should be cleared in the subpass. */
|
||||
bool shouldClear(MDSubpass const &p_subpass, bool p_is_stencil) const;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDRenderPass {
|
||||
public:
|
||||
Vector<MDAttachment> attachments;
|
||||
Vector<MDSubpass> subpasses;
|
||||
|
||||
uint32_t get_sample_count() const {
|
||||
return attachments.is_empty() ? 1 : attachments[0].samples;
|
||||
}
|
||||
|
||||
MDRenderPass(Vector<MDAttachment> &p_attachments, Vector<MDSubpass> &p_subpasses);
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDPipeline {
|
||||
public:
|
||||
MDPipelineType type;
|
||||
|
||||
explicit MDPipeline(MDPipelineType p_type) :
|
||||
type(p_type) {}
|
||||
virtual ~MDPipeline() = default;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDRenderPipeline final : public MDPipeline {
|
||||
public:
|
||||
id<MTLRenderPipelineState> state = nil;
|
||||
id<MTLDepthStencilState> depth_stencil = nil;
|
||||
uint32_t push_constant_size = 0;
|
||||
uint32_t push_constant_stages_mask = 0;
|
||||
SampleCount sample_count = SampleCount1;
|
||||
|
||||
struct {
|
||||
MTLCullMode cull_mode = MTLCullModeNone;
|
||||
MTLTriangleFillMode fill_mode = MTLTriangleFillModeFill;
|
||||
MTLDepthClipMode clip_mode = MTLDepthClipModeClip;
|
||||
MTLWinding winding = MTLWindingClockwise;
|
||||
MTLPrimitiveType render_primitive = MTLPrimitiveTypePoint;
|
||||
|
||||
struct {
|
||||
bool enabled = false;
|
||||
} depth_test;
|
||||
|
||||
struct {
|
||||
bool enabled = false;
|
||||
float depth_bias = 0.0;
|
||||
float slope_scale = 0.0;
|
||||
float clamp = 0.0;
|
||||
_FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
[p_enc setDepthBias:depth_bias slopeScale:slope_scale clamp:clamp];
|
||||
}
|
||||
} depth_bias;
|
||||
|
||||
struct {
|
||||
bool enabled = false;
|
||||
uint32_t front_reference = 0;
|
||||
uint32_t back_reference = 0;
|
||||
_FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
|
||||
if (!enabled)
|
||||
return;
|
||||
[p_enc setStencilFrontReferenceValue:front_reference backReferenceValue:back_reference];
|
||||
};
|
||||
} stencil;
|
||||
|
||||
struct {
|
||||
bool enabled = false;
|
||||
float r = 0.0;
|
||||
float g = 0.0;
|
||||
float b = 0.0;
|
||||
float a = 0.0;
|
||||
|
||||
_FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
|
||||
//if (!enabled)
|
||||
// return;
|
||||
[p_enc setBlendColorRed:r green:g blue:b alpha:a];
|
||||
};
|
||||
} blend;
|
||||
|
||||
_FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
|
||||
[p_enc setCullMode:cull_mode];
|
||||
[p_enc setTriangleFillMode:fill_mode];
|
||||
[p_enc setDepthClipMode:clip_mode];
|
||||
[p_enc setFrontFacingWinding:winding];
|
||||
depth_bias.apply(p_enc);
|
||||
stencil.apply(p_enc);
|
||||
blend.apply(p_enc);
|
||||
}
|
||||
|
||||
} raster_state;
|
||||
|
||||
MDRenderShader *shader = nil;
|
||||
|
||||
MDRenderPipeline() :
|
||||
MDPipeline(MDPipelineType::Render) {}
|
||||
~MDRenderPipeline() final = default;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDComputePipeline final : public MDPipeline {
|
||||
public:
|
||||
id<MTLComputePipelineState> state = nil;
|
||||
struct {
|
||||
MTLSize local = {};
|
||||
} compute_state;
|
||||
|
||||
MDComputeShader *shader = nil;
|
||||
|
||||
explicit MDComputePipeline(id<MTLComputePipelineState> p_state) :
|
||||
MDPipeline(MDPipelineType::Compute), state(p_state) {}
|
||||
~MDComputePipeline() final = default;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDFrameBuffer {
|
||||
public:
|
||||
Vector<MTL::Texture> textures;
|
||||
Size2i size;
|
||||
MDFrameBuffer(Vector<MTL::Texture> p_textures, Size2i p_size) :
|
||||
textures(p_textures), size(p_size) {}
|
||||
MDFrameBuffer() {}
|
||||
|
||||
virtual ~MDFrameBuffer() = default;
|
||||
};
|
||||
|
||||
// These functions are used to convert between Objective-C objects and
|
||||
// the RIDs used by Godot, respecting automatic reference counting.
|
||||
namespace rid {
|
||||
|
||||
// Converts an Objective-C object to a pointer, and incrementing the
|
||||
// reference count.
|
||||
_FORCE_INLINE_
|
||||
void *owned(id p_id) {
|
||||
return (__bridge_retained void *)p_id;
|
||||
}
|
||||
|
||||
#define MAKE_ID(FROM, TO) \
|
||||
_FORCE_INLINE_ TO make(FROM p_obj) { return TO(owned(p_obj)); }
|
||||
|
||||
MAKE_ID(id<MTLTexture>, RDD::TextureID)
|
||||
MAKE_ID(id<MTLBuffer>, RDD::BufferID)
|
||||
MAKE_ID(id<MTLSamplerState>, RDD::SamplerID)
|
||||
MAKE_ID(MTLVertexDescriptor *, RDD::VertexFormatID)
|
||||
MAKE_ID(id<MTLCommandQueue>, RDD::CommandPoolID)
|
||||
|
||||
// Converts a pointer to an Objective-C object without changing the reference count.
|
||||
_FORCE_INLINE_
|
||||
auto get(RDD::ID p_id) {
|
||||
return (p_id.id) ? (__bridge ::id)(void *)p_id.id : nil;
|
||||
}
|
||||
|
||||
// Converts a pointer to an Objective-C object, and decrements the reference count.
|
||||
_FORCE_INLINE_
|
||||
auto release(RDD::ID p_id) {
|
||||
return (__bridge_transfer ::id)(void *)p_id.id;
|
||||
}
|
||||
|
||||
} // namespace rid
|
||||
|
||||
#endif // METAL_OBJECTS_H
|
1380
drivers/metal/metal_objects.mm
Normal file
1380
drivers/metal/metal_objects.mm
Normal file
File diff suppressed because it is too large
Load Diff
81
drivers/metal/metal_utils.h
Normal file
81
drivers/metal/metal_utils.h
Normal file
@ -0,0 +1,81 @@
|
||||
/**************************************************************************/
|
||||
/* metal_utils.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef METAL_UTILS_H
|
||||
#define METAL_UTILS_H
|
||||
|
||||
#pragma mark - Boolean flags
|
||||
|
||||
namespace flags {
|
||||
|
||||
/*! Sets the flags within the value parameter specified by the mask parameter. */
|
||||
template <typename Tv, typename Tm>
|
||||
void set(Tv &p_value, Tm p_mask) {
|
||||
using T = std::underlying_type_t<Tv>;
|
||||
p_value = static_cast<Tv>(static_cast<T>(p_value) | static_cast<T>(p_mask));
|
||||
}
|
||||
|
||||
/*! Clears the flags within the value parameter specified by the mask parameter. */
|
||||
template <typename Tv, typename Tm>
|
||||
void clear(Tv &p_value, Tm p_mask) {
|
||||
using T = std::underlying_type_t<Tv>;
|
||||
p_value = static_cast<Tv>(static_cast<T>(p_value) & ~static_cast<T>(p_mask));
|
||||
}
|
||||
|
||||
/*! Returns whether the specified value has any of the bits specified in mask set to 1. */
|
||||
template <typename Tv, typename Tm>
|
||||
static constexpr bool any(Tv p_value, const Tm p_mask) { return ((p_value & p_mask) != 0); }
|
||||
|
||||
/*! Returns whether the specified value has all of the bits specified in mask set to 1. */
|
||||
template <typename Tv, typename Tm>
|
||||
static constexpr bool all(Tv p_value, const Tm p_mask) { return ((p_value & p_mask) == p_mask); }
|
||||
|
||||
} //namespace flags
|
||||
|
||||
#pragma mark - Alignment and Offsets
|
||||
|
||||
static constexpr bool is_power_of_two(uint64_t p_value) {
|
||||
return p_value && ((p_value & (p_value - 1)) == 0);
|
||||
}
|
||||
|
||||
static constexpr uint64_t round_up_to_alignment(uint64_t p_value, uint64_t p_alignment) {
|
||||
DEV_ASSERT(is_power_of_two(p_alignment));
|
||||
|
||||
if (p_alignment == 0) {
|
||||
return p_value;
|
||||
}
|
||||
|
||||
uint64_t mask = p_alignment - 1;
|
||||
uint64_t aligned_value = (p_value + mask) & ~mask;
|
||||
|
||||
return aligned_value;
|
||||
}
|
||||
|
||||
#endif // METAL_UTILS_H
|
416
drivers/metal/pixel_formats.h
Normal file
416
drivers/metal/pixel_formats.h
Normal file
@ -0,0 +1,416 @@
|
||||
/**************************************************************************/
|
||||
/* pixel_formats.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* Portions of this code were derived from MoltenVK. */
|
||||
/* */
|
||||
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
|
||||
/* (http://www.brenwill.com) */
|
||||
/* */
|
||||
/* 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 PIXEL_FORMATS_H
|
||||
#define PIXEL_FORMATS_H
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
#import "servers/rendering/rendering_device.h"
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
|
||||
static const uint32_t _mtlPixelFormatCount = 256;
|
||||
static const uint32_t _mtlPixelFormatCoreCount = MTLPixelFormatX32_Stencil8 + 2; // The actual last enum value is not available on iOS.
|
||||
static const uint32_t _mtlVertexFormatCount = MTLVertexFormatHalf + 1;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Metal format capabilities
|
||||
|
||||
typedef enum : uint16_t {
|
||||
|
||||
kMTLFmtCapsNone = 0,
|
||||
/*! The format can be used in a shader read operation. */
|
||||
kMTLFmtCapsRead = (1 << 0),
|
||||
/*! The format can be used in a shader filter operation during sampling. */
|
||||
kMTLFmtCapsFilter = (1 << 1),
|
||||
/*! The format can be used in a shader write operation. */
|
||||
kMTLFmtCapsWrite = (1 << 2),
|
||||
/*! The format can be used with atomic operations. */
|
||||
kMTLFmtCapsAtomic = (1 << 3),
|
||||
/*! The format can be used as a color attachment. */
|
||||
kMTLFmtCapsColorAtt = (1 << 4),
|
||||
/*! The format can be used as a depth-stencil attachment. */
|
||||
kMTLFmtCapsDSAtt = (1 << 5),
|
||||
/*! The format can be used with blend operations. */
|
||||
kMTLFmtCapsBlend = (1 << 6),
|
||||
/*! The format can be used as a destination for multisample antialias (MSAA) data. */
|
||||
kMTLFmtCapsMSAA = (1 << 7),
|
||||
/*! The format can be used as a resolve attachment. */
|
||||
kMTLFmtCapsResolve = (1 << 8),
|
||||
kMTLFmtCapsVertex = (1 << 9),
|
||||
|
||||
kMTLFmtCapsRF = (kMTLFmtCapsRead | kMTLFmtCapsFilter),
|
||||
kMTLFmtCapsRC = (kMTLFmtCapsRead | kMTLFmtCapsColorAtt),
|
||||
kMTLFmtCapsRCB = (kMTLFmtCapsRC | kMTLFmtCapsBlend),
|
||||
kMTLFmtCapsRCM = (kMTLFmtCapsRC | kMTLFmtCapsMSAA),
|
||||
kMTLFmtCapsRCMB = (kMTLFmtCapsRCM | kMTLFmtCapsBlend),
|
||||
kMTLFmtCapsRWC = (kMTLFmtCapsRC | kMTLFmtCapsWrite),
|
||||
kMTLFmtCapsRWCB = (kMTLFmtCapsRWC | kMTLFmtCapsBlend),
|
||||
kMTLFmtCapsRWCM = (kMTLFmtCapsRWC | kMTLFmtCapsMSAA),
|
||||
kMTLFmtCapsRWCMB = (kMTLFmtCapsRWCM | kMTLFmtCapsBlend),
|
||||
kMTLFmtCapsRFCMRB = (kMTLFmtCapsRCMB | kMTLFmtCapsFilter | kMTLFmtCapsResolve),
|
||||
kMTLFmtCapsRFWCMB = (kMTLFmtCapsRWCMB | kMTLFmtCapsFilter),
|
||||
kMTLFmtCapsAll = (kMTLFmtCapsRFWCMB | kMTLFmtCapsResolve),
|
||||
|
||||
kMTLFmtCapsDRM = (kMTLFmtCapsDSAtt | kMTLFmtCapsRead | kMTLFmtCapsMSAA),
|
||||
kMTLFmtCapsDRFM = (kMTLFmtCapsDRM | kMTLFmtCapsFilter),
|
||||
kMTLFmtCapsDRMR = (kMTLFmtCapsDRM | kMTLFmtCapsResolve),
|
||||
kMTLFmtCapsDRFMR = (kMTLFmtCapsDRFM | kMTLFmtCapsResolve),
|
||||
|
||||
kMTLFmtCapsChromaSubsampling = kMTLFmtCapsRF,
|
||||
kMTLFmtCapsMultiPlanar = kMTLFmtCapsChromaSubsampling,
|
||||
} MTLFmtCaps;
|
||||
|
||||
inline MTLFmtCaps operator|(MTLFmtCaps p_left, MTLFmtCaps p_right) {
|
||||
return static_cast<MTLFmtCaps>(static_cast<uint32_t>(p_left) | p_right);
|
||||
}
|
||||
|
||||
inline MTLFmtCaps &operator|=(MTLFmtCaps &p_left, MTLFmtCaps p_right) {
|
||||
return (p_left = p_left | p_right);
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Metal view classes
|
||||
|
||||
enum class MTLViewClass : uint8_t {
|
||||
None,
|
||||
Color8,
|
||||
Color16,
|
||||
Color32,
|
||||
Color64,
|
||||
Color128,
|
||||
PVRTC_RGB_2BPP,
|
||||
PVRTC_RGB_4BPP,
|
||||
PVRTC_RGBA_2BPP,
|
||||
PVRTC_RGBA_4BPP,
|
||||
EAC_R11,
|
||||
EAC_RG11,
|
||||
EAC_RGBA8,
|
||||
ETC2_RGB8,
|
||||
ETC2_RGB8A1,
|
||||
ASTC_4x4,
|
||||
ASTC_5x4,
|
||||
ASTC_5x5,
|
||||
ASTC_6x5,
|
||||
ASTC_6x6,
|
||||
ASTC_8x5,
|
||||
ASTC_8x6,
|
||||
ASTC_8x8,
|
||||
ASTC_10x5,
|
||||
ASTC_10x6,
|
||||
ASTC_10x8,
|
||||
ASTC_10x10,
|
||||
ASTC_12x10,
|
||||
ASTC_12x12,
|
||||
BC1_RGBA,
|
||||
BC2_RGBA,
|
||||
BC3_RGBA,
|
||||
BC4_R,
|
||||
BC5_RG,
|
||||
BC6H_RGB,
|
||||
BC7_RGBA,
|
||||
Depth24_Stencil8,
|
||||
Depth32_Stencil8,
|
||||
BGRA10_XR,
|
||||
BGR10_XR
|
||||
};
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Format descriptors
|
||||
|
||||
/** Enumerates the data type of a format. */
|
||||
enum class MTLFormatType {
|
||||
None, /**< Format type is unknown. */
|
||||
ColorHalf, /**< A 16-bit floating point color. */
|
||||
ColorFloat, /**< A 32-bit floating point color. */
|
||||
ColorInt8, /**< A signed 8-bit integer color. */
|
||||
ColorUInt8, /**< An unsigned 8-bit integer color. */
|
||||
ColorInt16, /**< A signed 16-bit integer color. */
|
||||
ColorUInt16, /**< An unsigned 16-bit integer color. */
|
||||
ColorInt32, /**< A signed 32-bit integer color. */
|
||||
ColorUInt32, /**< An unsigned 32-bit integer color. */
|
||||
DepthStencil, /**< A depth and stencil value. */
|
||||
Compressed, /**< A block-compressed color. */
|
||||
};
|
||||
|
||||
typedef struct Extent2D {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
} Extent2D;
|
||||
|
||||
/** Describes the properties of a DataFormat, including the corresponding Metal pixel and vertex format. */
|
||||
typedef struct DataFormatDesc {
|
||||
RD::DataFormat dataFormat;
|
||||
MTLPixelFormat mtlPixelFormat;
|
||||
MTLPixelFormat mtlPixelFormatSubstitute;
|
||||
MTLVertexFormat mtlVertexFormat;
|
||||
MTLVertexFormat mtlVertexFormatSubstitute;
|
||||
uint8_t chromaSubsamplingPlaneCount;
|
||||
uint8_t chromaSubsamplingComponentBits;
|
||||
Extent2D blockTexelSize;
|
||||
uint32_t bytesPerBlock;
|
||||
MTLFormatType formatType;
|
||||
const char *name;
|
||||
bool hasReportedSubstitution;
|
||||
|
||||
inline double bytesPerTexel() const { return (double)bytesPerBlock / (double)(blockTexelSize.width * blockTexelSize.height); }
|
||||
|
||||
inline bool isSupported() const { return (mtlPixelFormat != MTLPixelFormatInvalid || chromaSubsamplingPlaneCount > 1); }
|
||||
inline bool isSupportedOrSubstitutable() const { return isSupported() || (mtlPixelFormatSubstitute != MTLPixelFormatInvalid); }
|
||||
|
||||
inline bool vertexIsSupported() const { return (mtlVertexFormat != MTLVertexFormatInvalid); }
|
||||
inline bool vertexIsSupportedOrSubstitutable() const { return vertexIsSupported() || (mtlVertexFormatSubstitute != MTLVertexFormatInvalid); }
|
||||
} DataFormatDesc;
|
||||
|
||||
/** Describes the properties of a MTLPixelFormat or MTLVertexFormat. */
|
||||
typedef struct MTLFormatDesc {
|
||||
union {
|
||||
MTLPixelFormat mtlPixelFormat;
|
||||
MTLVertexFormat mtlVertexFormat;
|
||||
};
|
||||
RD::DataFormat dataFormat;
|
||||
MTLFmtCaps mtlFmtCaps;
|
||||
MTLViewClass mtlViewClass;
|
||||
MTLPixelFormat mtlPixelFormatLinear;
|
||||
const char *name = nullptr;
|
||||
|
||||
inline bool isSupported() const { return (mtlPixelFormat != MTLPixelFormatInvalid) && (mtlFmtCaps != kMTLFmtCapsNone); }
|
||||
} MTLFormatDesc;
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) PixelFormats {
|
||||
using DataFormat = RD::DataFormat;
|
||||
|
||||
public:
|
||||
/** Returns whether the DataFormat is supported by the GPU bound to this instance. */
|
||||
bool isSupported(DataFormat p_format);
|
||||
|
||||
/** Returns whether the DataFormat is supported by this implementation, or can be substituted by one that is. */
|
||||
bool isSupportedOrSubstitutable(DataFormat p_format);
|
||||
|
||||
/** Returns whether the specified Metal MTLPixelFormat can be used as a depth format. */
|
||||
_FORCE_INLINE_ bool isDepthFormat(MTLPixelFormat p_format) {
|
||||
switch (p_format) {
|
||||
case MTLPixelFormatDepth32Float:
|
||||
case MTLPixelFormatDepth16Unorm:
|
||||
case MTLPixelFormatDepth32Float_Stencil8:
|
||||
#if TARGET_OS_OSX
|
||||
case MTLPixelFormatDepth24Unorm_Stencil8:
|
||||
#endif
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns whether the specified Metal MTLPixelFormat can be used as a stencil format. */
|
||||
_FORCE_INLINE_ bool isStencilFormat(MTLPixelFormat p_format) {
|
||||
switch (p_format) {
|
||||
case MTLPixelFormatStencil8:
|
||||
#if TARGET_OS_OSX
|
||||
case MTLPixelFormatDepth24Unorm_Stencil8:
|
||||
case MTLPixelFormatX24_Stencil8:
|
||||
#endif
|
||||
case MTLPixelFormatDepth32Float_Stencil8:
|
||||
case MTLPixelFormatX32_Stencil8:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns whether the specified Metal MTLPixelFormat is a PVRTC format. */
|
||||
bool isPVRTCFormat(MTLPixelFormat p_format);
|
||||
|
||||
/** Returns the format type corresponding to the specified Godot pixel format, */
|
||||
MTLFormatType getFormatType(DataFormat p_format);
|
||||
|
||||
/** Returns the format type corresponding to the specified Metal MTLPixelFormat, */
|
||||
MTLFormatType getFormatType(MTLPixelFormat p_formt);
|
||||
|
||||
/**
|
||||
* Returns the Metal MTLPixelFormat corresponding to the specified Godot pixel
|
||||
* or returns MTLPixelFormatInvalid if no corresponding MTLPixelFormat exists.
|
||||
*/
|
||||
MTLPixelFormat getMTLPixelFormat(DataFormat p_format);
|
||||
|
||||
/**
|
||||
* Returns the DataFormat corresponding to the specified Metal MTLPixelFormat,
|
||||
* or returns DATA_FORMAT_MAX if no corresponding DataFormat exists.
|
||||
*/
|
||||
DataFormat getDataFormat(MTLPixelFormat p_format);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a texel block of the specified Godot pixel.
|
||||
* For uncompressed formats, the returned value corresponds to the size in bytes of a single texel.
|
||||
*/
|
||||
uint32_t getBytesPerBlock(DataFormat p_format);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a texel block of the specified Metal format.
|
||||
* For uncompressed formats, the returned value corresponds to the size in bytes of a single texel.
|
||||
*/
|
||||
uint32_t getBytesPerBlock(MTLPixelFormat p_format);
|
||||
|
||||
/** Returns the number of planes of the specified chroma-subsampling (YCbCr) DataFormat */
|
||||
uint8_t getChromaSubsamplingPlaneCount(DataFormat p_format);
|
||||
|
||||
/** Returns the number of bits per channel of the specified chroma-subsampling (YCbCr) DataFormat */
|
||||
uint8_t getChromaSubsamplingComponentBits(DataFormat p_format);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a texel of the specified Godot format.
|
||||
* The returned value may be fractional for certain compressed formats.
|
||||
*/
|
||||
float getBytesPerTexel(DataFormat p_format);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a texel of the specified Metal format.
|
||||
* The returned value may be fractional for certain compressed formats.
|
||||
*/
|
||||
float getBytesPerTexel(MTLPixelFormat p_format);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a row of texels of the specified Godot pixel format.
|
||||
*
|
||||
* For compressed formats, this takes into consideration the compression block size,
|
||||
* and p_texels_per_row should specify the width in texels, not blocks. The result is rounded
|
||||
* up if p_texels_per_row is not an integer multiple of the compression block width.
|
||||
*/
|
||||
size_t getBytesPerRow(DataFormat p_format, uint32_t p_texels_per_row);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a row of texels of the specified Metal format.
|
||||
*
|
||||
* For compressed formats, this takes into consideration the compression block size,
|
||||
* and texelsPerRow should specify the width in texels, not blocks. The result is rounded
|
||||
* up if texelsPerRow is not an integer multiple of the compression block width.
|
||||
*/
|
||||
size_t getBytesPerRow(MTLPixelFormat p_format, uint32_t p_texels_per_row);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a texture layer of the specified Godot pixel format.
|
||||
*
|
||||
* For compressed formats, this takes into consideration the compression block size,
|
||||
* and p_texel_rows_per_layer should specify the height in texels, not blocks. The result is
|
||||
* rounded up if p_texel_rows_per_layer is not an integer multiple of the compression block height.
|
||||
*/
|
||||
size_t getBytesPerLayer(DataFormat p_format, size_t p_bytes_per_row, uint32_t p_texel_rows_per_layer);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a texture layer of the specified Metal format.
|
||||
* For compressed formats, this takes into consideration the compression block size,
|
||||
* and p_texel_rows_per_layer should specify the height in texels, not blocks. The result is
|
||||
* rounded up if p_texel_rows_per_layer is not an integer multiple of the compression block height.
|
||||
*/
|
||||
size_t getBytesPerLayer(MTLPixelFormat p_format, size_t p_bytes_per_row, uint32_t p_texel_rows_per_layer);
|
||||
|
||||
/** Returns the Metal format capabilities supported by the specified Godot format, without substitution. */
|
||||
MTLFmtCaps getCapabilities(DataFormat p_format, bool p_extended = false);
|
||||
|
||||
/** Returns the Metal format capabilities supported by the specified Metal format. */
|
||||
MTLFmtCaps getCapabilities(MTLPixelFormat p_format, bool p_extended = false);
|
||||
|
||||
/**
|
||||
* Returns the Metal MTLVertexFormat corresponding to the specified
|
||||
* DataFormat as used as a vertex attribute format.
|
||||
*/
|
||||
MTLVertexFormat getMTLVertexFormat(DataFormat p_format);
|
||||
|
||||
#pragma mark Construction
|
||||
|
||||
explicit PixelFormats(id<MTLDevice> p_device);
|
||||
|
||||
protected:
|
||||
id<MTLDevice> device;
|
||||
|
||||
DataFormatDesc &getDataFormatDesc(DataFormat p_format);
|
||||
DataFormatDesc &getDataFormatDesc(MTLPixelFormat p_format);
|
||||
MTLFormatDesc &getMTLPixelFormatDesc(MTLPixelFormat p_format);
|
||||
MTLFormatDesc &getMTLVertexFormatDesc(MTLVertexFormat p_format);
|
||||
void initDataFormatCapabilities();
|
||||
void initMTLPixelFormatCapabilities();
|
||||
void initMTLVertexFormatCapabilities();
|
||||
void buildMTLFormatMaps();
|
||||
void buildDFFormatMaps();
|
||||
void modifyMTLFormatCapabilities();
|
||||
void modifyMTLFormatCapabilities(id<MTLDevice> p_device);
|
||||
void addMTLPixelFormatCapabilities(id<MTLDevice> p_device,
|
||||
MTLFeatureSet p_feature_set,
|
||||
MTLPixelFormat p_format,
|
||||
MTLFmtCaps p_caps);
|
||||
void addMTLPixelFormatCapabilities(id<MTLDevice> p_device,
|
||||
MTLGPUFamily p_family,
|
||||
MTLPixelFormat p_format,
|
||||
MTLFmtCaps p_caps);
|
||||
void disableMTLPixelFormatCapabilities(MTLPixelFormat p_format,
|
||||
MTLFmtCaps p_caps);
|
||||
void disableAllMTLPixelFormatCapabilities(MTLPixelFormat p_format);
|
||||
void addMTLVertexFormatCapabilities(id<MTLDevice> p_device,
|
||||
MTLFeatureSet p_feature_set,
|
||||
MTLVertexFormat p_format,
|
||||
MTLFmtCaps p_caps);
|
||||
|
||||
DataFormatDesc _dataFormatDescriptions[RD::DATA_FORMAT_MAX];
|
||||
MTLFormatDesc _mtlPixelFormatDescriptions[_mtlPixelFormatCount];
|
||||
MTLFormatDesc _mtlVertexFormatDescriptions[_mtlVertexFormatCount];
|
||||
|
||||
// Most Metal formats have small values and are mapped by simple lookup array.
|
||||
// Outliers are mapped by a map.
|
||||
uint16_t _mtlFormatDescIndicesByMTLPixelFormatsCore[_mtlPixelFormatCoreCount];
|
||||
HashMap<uint32_t, uint32_t> _mtlFormatDescIndicesByMTLPixelFormatsExt;
|
||||
|
||||
uint16_t _mtlFormatDescIndicesByMTLVertexFormats[_mtlVertexFormatCount];
|
||||
};
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#endif // PIXEL_FORMATS_H
|
1298
drivers/metal/pixel_formats.mm
Normal file
1298
drivers/metal/pixel_formats.mm
Normal file
File diff suppressed because it is too large
Load Diff
206
drivers/metal/rendering_context_driver_metal.h
Normal file
206
drivers/metal/rendering_context_driver_metal.h
Normal file
@ -0,0 +1,206 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_context_driver_metal.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef RENDERING_CONTEXT_DRIVER_METAL_H
|
||||
#define RENDERING_CONTEXT_DRIVER_METAL_H
|
||||
|
||||
#ifdef METAL_ENABLED
|
||||
|
||||
#import "rendering_device_driver_metal.h"
|
||||
|
||||
#import "servers/rendering/rendering_context_driver.h"
|
||||
|
||||
#import <CoreGraphics/CGGeometry.h>
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CALayer.h>
|
||||
|
||||
@class CAMetalLayer;
|
||||
@protocol CAMetalDrawable;
|
||||
class PixelFormats;
|
||||
class MDResourceCache;
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) RenderingContextDriverMetal : public RenderingContextDriver {
|
||||
protected:
|
||||
id<MTLDevice> metal_device = nil;
|
||||
Device device; // There is only one device on Apple Silicon.
|
||||
|
||||
public:
|
||||
Error initialize() final override;
|
||||
const Device &device_get(uint32_t p_device_index) const final override;
|
||||
uint32_t device_get_count() const final override;
|
||||
bool device_supports_present(uint32_t p_device_index, SurfaceID p_surface) const final override { return true; }
|
||||
RenderingDeviceDriver *driver_create() final override;
|
||||
void driver_free(RenderingDeviceDriver *p_driver) final override;
|
||||
SurfaceID surface_create(const void *p_platform_data) final override;
|
||||
void surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) final override;
|
||||
void surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) final override;
|
||||
DisplayServer::VSyncMode surface_get_vsync_mode(SurfaceID p_surface) const final override;
|
||||
uint32_t surface_get_width(SurfaceID p_surface) const final override;
|
||||
uint32_t surface_get_height(SurfaceID p_surface) const final override;
|
||||
void surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) final override;
|
||||
bool surface_get_needs_resize(SurfaceID p_surface) const final override;
|
||||
void surface_destroy(SurfaceID p_surface) final override;
|
||||
bool is_debug_utils_enabled() const final override { return true; }
|
||||
|
||||
#pragma mark - Metal-specific methods
|
||||
|
||||
// Platform-specific data for the Windows embedded in this driver.
|
||||
struct WindowPlatformData {
|
||||
CAMetalLayer *__unsafe_unretained layer;
|
||||
};
|
||||
|
||||
class Surface {
|
||||
protected:
|
||||
id<MTLDevice> device;
|
||||
|
||||
public:
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED;
|
||||
bool needs_resize = false;
|
||||
|
||||
Surface(id<MTLDevice> p_device) :
|
||||
device(p_device) {}
|
||||
virtual ~Surface() = default;
|
||||
|
||||
MTLPixelFormat get_pixel_format() const { return MTLPixelFormatBGRA8Unorm; }
|
||||
virtual Error resize(uint32_t p_desired_framebuffer_count) = 0;
|
||||
virtual RDD::FramebufferID acquire_next_frame_buffer() = 0;
|
||||
virtual void present(MDCommandBuffer *p_cmd_buffer) = 0;
|
||||
};
|
||||
|
||||
class SurfaceLayer : public Surface {
|
||||
CAMetalLayer *__unsafe_unretained layer = nil;
|
||||
LocalVector<MDFrameBuffer> frame_buffers;
|
||||
LocalVector<id<MTLDrawable>> drawables;
|
||||
uint32_t rear = -1;
|
||||
uint32_t front = 0;
|
||||
uint32_t count = 0;
|
||||
|
||||
public:
|
||||
SurfaceLayer(CAMetalLayer *p_layer, id<MTLDevice> p_device) :
|
||||
Surface(p_device), layer(p_layer) {
|
||||
layer.allowsNextDrawableTimeout = YES;
|
||||
layer.framebufferOnly = YES;
|
||||
layer.opaque = OS::get_singleton()->is_layered_allowed() ? NO : YES;
|
||||
layer.pixelFormat = get_pixel_format();
|
||||
layer.device = p_device;
|
||||
}
|
||||
|
||||
~SurfaceLayer() override {
|
||||
layer = nil;
|
||||
}
|
||||
|
||||
Error resize(uint32_t p_desired_framebuffer_count) override final {
|
||||
if (width == 0 || height == 0) {
|
||||
// Very likely the window is minimized, don't create a swap chain.
|
||||
return ERR_SKIP;
|
||||
}
|
||||
|
||||
CGSize drawableSize = CGSizeMake(width, height);
|
||||
CGSize current = layer.drawableSize;
|
||||
if (!CGSizeEqualToSize(current, drawableSize)) {
|
||||
layer.drawableSize = drawableSize;
|
||||
}
|
||||
|
||||
// Metal supports a maximum of 3 drawables.
|
||||
p_desired_framebuffer_count = MIN(3U, p_desired_framebuffer_count);
|
||||
layer.maximumDrawableCount = p_desired_framebuffer_count;
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
// Display sync is only supported on macOS.
|
||||
switch (vsync_mode) {
|
||||
case DisplayServer::VSYNC_MAILBOX:
|
||||
case DisplayServer::VSYNC_ADAPTIVE:
|
||||
case DisplayServer::VSYNC_ENABLED:
|
||||
layer.displaySyncEnabled = YES;
|
||||
break;
|
||||
case DisplayServer::VSYNC_DISABLED:
|
||||
layer.displaySyncEnabled = NO;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
drawables.resize(p_desired_framebuffer_count);
|
||||
frame_buffers.resize(p_desired_framebuffer_count);
|
||||
for (uint32_t i = 0; i < p_desired_framebuffer_count; i++) {
|
||||
// Reserve space for the drawable texture.
|
||||
frame_buffers[i].textures.resize(1);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
RDD::FramebufferID acquire_next_frame_buffer() override final {
|
||||
if (count == frame_buffers.size()) {
|
||||
return RDD::FramebufferID();
|
||||
}
|
||||
|
||||
rear = (rear + 1) % frame_buffers.size();
|
||||
count++;
|
||||
|
||||
MDFrameBuffer &frame_buffer = frame_buffers[rear];
|
||||
frame_buffer.size = Size2i(width, height);
|
||||
|
||||
id<CAMetalDrawable> drawable = layer.nextDrawable;
|
||||
ERR_FAIL_NULL_V_MSG(drawable, RDD::FramebufferID(), "no drawable available");
|
||||
drawables[rear] = drawable;
|
||||
frame_buffer.textures.write[0] = drawable.texture;
|
||||
|
||||
return RDD::FramebufferID(&frame_buffer);
|
||||
}
|
||||
|
||||
void present(MDCommandBuffer *p_cmd_buffer) override final {
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Release texture and drawable.
|
||||
frame_buffers[front].textures.write[0] = nil;
|
||||
id<MTLDrawable> drawable = drawables[front];
|
||||
drawables[front] = nil;
|
||||
|
||||
count--;
|
||||
front = (front + 1) % frame_buffers.size();
|
||||
|
||||
[p_cmd_buffer->get_command_buffer() presentDrawable:drawable];
|
||||
}
|
||||
};
|
||||
|
||||
id<MTLDevice> get_metal_device() const { return metal_device; }
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
RenderingContextDriverMetal();
|
||||
~RenderingContextDriverMetal() override;
|
||||
};
|
||||
|
||||
#endif // METAL_ENABLED
|
||||
|
||||
#endif // RENDERING_CONTEXT_DRIVER_METAL_H
|
134
drivers/metal/rendering_context_driver_metal.mm
Normal file
134
drivers/metal/rendering_context_driver_metal.mm
Normal file
@ -0,0 +1,134 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_context_driver_metal.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "rendering_context_driver_metal.h"
|
||||
|
||||
@protocol MTLDeviceEx <MTLDevice>
|
||||
#if TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED < 130300
|
||||
- (void)setShouldMaximizeConcurrentCompilation:(BOOL)v;
|
||||
#endif
|
||||
@end
|
||||
|
||||
RenderingContextDriverMetal::RenderingContextDriverMetal() {
|
||||
}
|
||||
|
||||
RenderingContextDriverMetal::~RenderingContextDriverMetal() {
|
||||
}
|
||||
|
||||
Error RenderingContextDriverMetal::initialize() {
|
||||
metal_device = MTLCreateSystemDefaultDevice();
|
||||
#if TARGET_OS_OSX
|
||||
if (@available(macOS 13.3, *)) {
|
||||
[id<MTLDeviceEx>(metal_device) setShouldMaximizeConcurrentCompilation:YES];
|
||||
}
|
||||
#endif
|
||||
device.type = DEVICE_TYPE_INTEGRATED_GPU;
|
||||
device.vendor = VENDOR_APPLE;
|
||||
device.workarounds = Workarounds();
|
||||
|
||||
MetalDeviceProperties props(metal_device);
|
||||
int version = (int)props.features.highestFamily - (int)MTLGPUFamilyApple1 + 1;
|
||||
device.name = vformat("%s (Apple%d)", metal_device.name.UTF8String, version);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
const RenderingContextDriver::Device &RenderingContextDriverMetal::device_get(uint32_t p_device_index) const {
|
||||
DEV_ASSERT(p_device_index < 1);
|
||||
return device;
|
||||
}
|
||||
|
||||
uint32_t RenderingContextDriverMetal::device_get_count() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
RenderingDeviceDriver *RenderingContextDriverMetal::driver_create() {
|
||||
return memnew(RenderingDeviceDriverMetal(this));
|
||||
}
|
||||
|
||||
void RenderingContextDriverMetal::driver_free(RenderingDeviceDriver *p_driver) {
|
||||
memdelete(p_driver);
|
||||
}
|
||||
|
||||
RenderingContextDriver::SurfaceID RenderingContextDriverMetal::surface_create(const void *p_platform_data) {
|
||||
const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data);
|
||||
Surface *surface = memnew(SurfaceLayer(wpd->layer, metal_device));
|
||||
|
||||
return SurfaceID(surface);
|
||||
}
|
||||
|
||||
void RenderingContextDriverMetal::surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
if (surface->width == p_width && surface->height == p_height) {
|
||||
return;
|
||||
}
|
||||
surface->width = p_width;
|
||||
surface->height = p_height;
|
||||
surface->needs_resize = true;
|
||||
}
|
||||
|
||||
void RenderingContextDriverMetal::surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
if (surface->vsync_mode == p_vsync_mode) {
|
||||
return;
|
||||
}
|
||||
surface->vsync_mode = p_vsync_mode;
|
||||
surface->needs_resize = true;
|
||||
}
|
||||
|
||||
DisplayServer::VSyncMode RenderingContextDriverMetal::surface_get_vsync_mode(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return surface->vsync_mode;
|
||||
}
|
||||
|
||||
uint32_t RenderingContextDriverMetal::surface_get_width(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return surface->width;
|
||||
}
|
||||
|
||||
uint32_t RenderingContextDriverMetal::surface_get_height(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return surface->height;
|
||||
}
|
||||
|
||||
void RenderingContextDriverMetal::surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
surface->needs_resize = p_needs_resize;
|
||||
}
|
||||
|
||||
bool RenderingContextDriverMetal::surface_get_needs_resize(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return surface->needs_resize;
|
||||
}
|
||||
|
||||
void RenderingContextDriverMetal::surface_destroy(SurfaceID p_surface) {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
memdelete(surface);
|
||||
}
|
417
drivers/metal/rendering_device_driver_metal.h
Normal file
417
drivers/metal/rendering_device_driver_metal.h
Normal file
@ -0,0 +1,417 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_device_driver_metal.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef RENDERING_DEVICE_DRIVER_METAL_H
|
||||
#define RENDERING_DEVICE_DRIVER_METAL_H
|
||||
|
||||
#import "metal_objects.h"
|
||||
|
||||
#import "servers/rendering/rendering_device_driver.h"
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <spirv.hpp>
|
||||
#import <variant>
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
#ifndef _DEBUG
|
||||
#define _DEBUG
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class RenderingContextDriverMetal;
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) RenderingDeviceDriverMetal : public RenderingDeviceDriver {
|
||||
template <typename T>
|
||||
using Result = std::variant<T, Error>;
|
||||
|
||||
#pragma mark - Generic
|
||||
|
||||
RenderingContextDriverMetal *context_driver = nullptr;
|
||||
RenderingContextDriver::Device context_device;
|
||||
id<MTLDevice> device = nil;
|
||||
|
||||
uint32_t version_major = 2;
|
||||
uint32_t version_minor = 0;
|
||||
MetalDeviceProperties *metal_device_properties = nullptr;
|
||||
PixelFormats *pixel_formats = nullptr;
|
||||
std::unique_ptr<MDResourceCache> resource_cache;
|
||||
|
||||
RDD::Capabilities capabilities;
|
||||
RDD::MultiviewCapabilities multiview_capabilities;
|
||||
|
||||
id<MTLBinaryArchive> archive = nil;
|
||||
uint32_t archive_count = 0;
|
||||
|
||||
id<MTLCommandQueue> device_queue = nil;
|
||||
id<MTLCaptureScope> device_scope = nil;
|
||||
|
||||
String pipeline_cache_id;
|
||||
|
||||
Error _create_device();
|
||||
Error _check_capabilities();
|
||||
|
||||
public:
|
||||
Error initialize(uint32_t p_device_index, uint32_t p_frame_count) override final;
|
||||
|
||||
#pragma mark - Memory
|
||||
|
||||
#pragma mark - Buffers
|
||||
|
||||
public:
|
||||
virtual BufferID buffer_create(uint64_t p_size, BitField<BufferUsageBits> p_usage, MemoryAllocationType p_allocation_type) override final;
|
||||
virtual bool buffer_set_texel_format(BufferID p_buffer, DataFormat p_format) override final;
|
||||
virtual void buffer_free(BufferID p_buffer) override final;
|
||||
virtual uint64_t buffer_get_allocation_size(BufferID p_buffer) override final;
|
||||
virtual uint8_t *buffer_map(BufferID p_buffer) override final;
|
||||
virtual void buffer_unmap(BufferID p_buffer) override final;
|
||||
|
||||
#pragma mark - Texture
|
||||
|
||||
private:
|
||||
// Returns true if the texture is a valid linear format.
|
||||
Result<bool> is_valid_linear(TextureFormat const &p_format) const;
|
||||
void _get_sub_resource(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) const;
|
||||
|
||||
public:
|
||||
virtual TextureID texture_create(const TextureFormat &p_format, const TextureView &p_view) override final;
|
||||
virtual TextureID texture_create_from_extension(uint64_t p_native_texture, TextureType p_type, DataFormat p_format, uint32_t p_array_layers, bool p_depth_stencil) override final;
|
||||
virtual TextureID texture_create_shared(TextureID p_original_texture, const TextureView &p_view) override final;
|
||||
virtual TextureID texture_create_shared_from_slice(TextureID p_original_texture, const TextureView &p_view, TextureSliceType p_slice_type, uint32_t p_layer, uint32_t p_layers, uint32_t p_mipmap, uint32_t p_mipmaps) override final;
|
||||
virtual void texture_free(TextureID p_texture) override final;
|
||||
virtual uint64_t texture_get_allocation_size(TextureID p_texture) override final;
|
||||
virtual void texture_get_copyable_layout(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) override final;
|
||||
virtual uint8_t *texture_map(TextureID p_texture, const TextureSubresource &p_subresource) override final;
|
||||
virtual void texture_unmap(TextureID p_texture) override final;
|
||||
virtual BitField<TextureUsageBits> texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) override final;
|
||||
virtual bool texture_can_make_shared_with_format(TextureID p_texture, DataFormat p_format, bool &r_raw_reinterpretation) override final;
|
||||
|
||||
#pragma mark - Sampler
|
||||
|
||||
public:
|
||||
virtual SamplerID sampler_create(const SamplerState &p_state) final override;
|
||||
virtual void sampler_free(SamplerID p_sampler) final override;
|
||||
virtual bool sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_filter) override final;
|
||||
|
||||
#pragma mark - Vertex Array
|
||||
|
||||
private:
|
||||
public:
|
||||
virtual VertexFormatID vertex_format_create(VectorView<VertexAttribute> p_vertex_attribs) override final;
|
||||
virtual void vertex_format_free(VertexFormatID p_vertex_format) override final;
|
||||
|
||||
#pragma mark - Barriers
|
||||
|
||||
virtual void command_pipeline_barrier(
|
||||
CommandBufferID p_cmd_buffer,
|
||||
BitField<PipelineStageBits> p_src_stages,
|
||||
BitField<PipelineStageBits> p_dst_stages,
|
||||
VectorView<MemoryBarrier> p_memory_barriers,
|
||||
VectorView<BufferBarrier> p_buffer_barriers,
|
||||
VectorView<TextureBarrier> p_texture_barriers) override final;
|
||||
|
||||
#pragma mark - Fences
|
||||
|
||||
private:
|
||||
struct Fence {
|
||||
dispatch_semaphore_t semaphore;
|
||||
Fence() :
|
||||
semaphore(dispatch_semaphore_create(0)) {}
|
||||
};
|
||||
|
||||
public:
|
||||
virtual FenceID fence_create() override final;
|
||||
virtual Error fence_wait(FenceID p_fence) override final;
|
||||
virtual void fence_free(FenceID p_fence) override final;
|
||||
|
||||
#pragma mark - Semaphores
|
||||
|
||||
public:
|
||||
virtual SemaphoreID semaphore_create() override final;
|
||||
virtual void semaphore_free(SemaphoreID p_semaphore) override final;
|
||||
|
||||
#pragma mark - Commands
|
||||
// ----- QUEUE FAMILY -----
|
||||
|
||||
virtual CommandQueueFamilyID command_queue_family_get(BitField<CommandQueueFamilyBits> p_cmd_queue_family_bits, RenderingContextDriver::SurfaceID p_surface = 0) override final;
|
||||
|
||||
// ----- QUEUE -----
|
||||
public:
|
||||
virtual CommandQueueID command_queue_create(CommandQueueFamilyID p_cmd_queue_family, bool p_identify_as_main_queue = false) override final;
|
||||
virtual Error command_queue_execute_and_present(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_semaphores, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_semaphores, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains) override final;
|
||||
virtual void command_queue_free(CommandQueueID p_cmd_queue) override final;
|
||||
|
||||
// ----- POOL -----
|
||||
|
||||
virtual CommandPoolID command_pool_create(CommandQueueFamilyID p_cmd_queue_family, CommandBufferType p_cmd_buffer_type) override final;
|
||||
virtual void command_pool_free(CommandPoolID p_cmd_pool) override final;
|
||||
|
||||
// ----- BUFFER -----
|
||||
|
||||
private:
|
||||
// Used to maintain references.
|
||||
Vector<MDCommandBuffer *> command_buffers;
|
||||
|
||||
public:
|
||||
virtual CommandBufferID command_buffer_create(CommandPoolID p_cmd_pool) override final;
|
||||
virtual bool command_buffer_begin(CommandBufferID p_cmd_buffer) override final;
|
||||
virtual bool command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) override final;
|
||||
virtual void command_buffer_end(CommandBufferID p_cmd_buffer) override final;
|
||||
virtual void command_buffer_execute_secondary(CommandBufferID p_cmd_buffer, VectorView<CommandBufferID> p_secondary_cmd_buffers) override final;
|
||||
|
||||
#pragma mark - Swapchain
|
||||
|
||||
private:
|
||||
struct SwapChain {
|
||||
RenderingContextDriver::SurfaceID surface = RenderingContextDriver::SurfaceID();
|
||||
RenderPassID render_pass;
|
||||
RDD::DataFormat data_format = DATA_FORMAT_MAX;
|
||||
SwapChain() :
|
||||
render_pass(nullptr) {}
|
||||
};
|
||||
|
||||
void _swap_chain_release(SwapChain *p_swap_chain);
|
||||
void _swap_chain_release_buffers(SwapChain *p_swap_chain);
|
||||
|
||||
public:
|
||||
virtual SwapChainID swap_chain_create(RenderingContextDriver::SurfaceID p_surface) override final;
|
||||
virtual Error swap_chain_resize(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, uint32_t p_desired_framebuffer_count) override final;
|
||||
virtual FramebufferID swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) override final;
|
||||
virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) override final;
|
||||
virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override final;
|
||||
virtual void swap_chain_free(SwapChainID p_swap_chain) override final;
|
||||
|
||||
#pragma mark - Frame Buffer
|
||||
|
||||
virtual FramebufferID framebuffer_create(RenderPassID p_render_pass, VectorView<TextureID> p_attachments, uint32_t p_width, uint32_t p_height) override final;
|
||||
virtual void framebuffer_free(FramebufferID p_framebuffer) override final;
|
||||
|
||||
#pragma mark - Shader
|
||||
|
||||
private:
|
||||
// Serialization types need access to private state.
|
||||
|
||||
friend struct ShaderStageData;
|
||||
friend struct SpecializationConstantData;
|
||||
friend struct UniformData;
|
||||
friend struct ShaderBinaryData;
|
||||
friend struct PushConstantData;
|
||||
|
||||
private:
|
||||
Error _reflect_spirv16(VectorView<ShaderStageSPIRVData> p_spirv, ShaderReflection &r_reflection);
|
||||
|
||||
public:
|
||||
virtual String shader_get_binary_cache_key() override final;
|
||||
virtual Vector<uint8_t> shader_compile_binary_from_spirv(VectorView<ShaderStageSPIRVData> p_spirv, const String &p_shader_name) override final;
|
||||
virtual ShaderID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, ShaderDescription &r_shader_desc, String &r_name) override final;
|
||||
virtual void shader_free(ShaderID p_shader) override final;
|
||||
|
||||
#pragma mark - Uniform Set
|
||||
|
||||
public:
|
||||
virtual UniformSetID uniform_set_create(VectorView<BoundUniform> p_uniforms, ShaderID p_shader, uint32_t p_set_index) override final;
|
||||
virtual void uniform_set_free(UniformSetID p_uniform_set) override final;
|
||||
|
||||
#pragma mark - Commands
|
||||
|
||||
virtual void command_uniform_set_prepare_for_use(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final;
|
||||
|
||||
#pragma mark Transfer
|
||||
|
||||
private:
|
||||
enum class CopySource {
|
||||
Buffer,
|
||||
Texture,
|
||||
};
|
||||
void _copy_texture_buffer(CommandBufferID p_cmd_buffer,
|
||||
CopySource p_source,
|
||||
TextureID p_texture,
|
||||
BufferID p_buffer,
|
||||
VectorView<BufferTextureCopyRegion> p_regions);
|
||||
|
||||
public:
|
||||
virtual void command_clear_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, uint64_t p_offset, uint64_t p_size) override final;
|
||||
virtual void command_copy_buffer(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, BufferID p_dst_buffer, VectorView<BufferCopyRegion> p_regions) override final;
|
||||
|
||||
virtual void command_copy_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<TextureCopyRegion> p_regions) override final;
|
||||
virtual void command_resolve_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, uint32_t p_src_layer, uint32_t p_src_mipmap, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, uint32_t p_dst_layer, uint32_t p_dst_mipmap) override final;
|
||||
virtual void command_clear_color_texture(CommandBufferID p_cmd_buffer, TextureID p_texture, TextureLayout p_texture_layout, const Color &p_color, const TextureSubresourceRange &p_subresources) override final;
|
||||
|
||||
virtual void command_copy_buffer_to_texture(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<BufferTextureCopyRegion> p_regions) override final;
|
||||
virtual void command_copy_texture_to_buffer(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, BufferID p_dst_buffer, VectorView<BufferTextureCopyRegion> p_regions) override final;
|
||||
|
||||
#pragma mark Pipeline
|
||||
|
||||
private:
|
||||
Result<id<MTLFunction>> _create_function(id<MTLLibrary> p_library, NSString *p_name, VectorView<PipelineSpecializationConstant> &p_specialization_constants);
|
||||
|
||||
public:
|
||||
virtual void pipeline_free(PipelineID p_pipeline_id) override final;
|
||||
|
||||
// ----- BINDING -----
|
||||
|
||||
virtual void command_bind_push_constants(CommandBufferID p_cmd_buffer, ShaderID p_shader, uint32_t p_first_index, VectorView<uint32_t> p_data) override final;
|
||||
|
||||
// ----- CACHE -----
|
||||
private:
|
||||
String _pipeline_get_cache_path() const;
|
||||
|
||||
public:
|
||||
virtual bool pipeline_cache_create(const Vector<uint8_t> &p_data) override final;
|
||||
virtual void pipeline_cache_free() override final;
|
||||
virtual size_t pipeline_cache_query_size() override final;
|
||||
virtual Vector<uint8_t> pipeline_cache_serialize() override final;
|
||||
|
||||
#pragma mark Rendering
|
||||
|
||||
// ----- SUBPASS -----
|
||||
|
||||
virtual RenderPassID render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count) override final;
|
||||
virtual void render_pass_free(RenderPassID p_render_pass) override final;
|
||||
|
||||
// ----- COMMANDS -----
|
||||
|
||||
public:
|
||||
virtual void command_begin_render_pass(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, FramebufferID p_framebuffer, CommandBufferType p_cmd_buffer_type, const Rect2i &p_rect, VectorView<RenderPassClearValue> p_clear_values) override final;
|
||||
virtual void command_end_render_pass(CommandBufferID p_cmd_buffer) override final;
|
||||
virtual void command_next_render_subpass(CommandBufferID p_cmd_buffer, CommandBufferType p_cmd_buffer_type) override final;
|
||||
virtual void command_render_set_viewport(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_viewports) override final;
|
||||
virtual void command_render_set_scissor(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_scissors) override final;
|
||||
virtual void command_render_clear_attachments(CommandBufferID p_cmd_buffer, VectorView<AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects) override final;
|
||||
|
||||
// Binding.
|
||||
virtual void command_bind_render_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) override final;
|
||||
virtual void command_bind_render_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final;
|
||||
|
||||
// Drawing.
|
||||
virtual void command_render_draw(CommandBufferID p_cmd_buffer, uint32_t p_vertex_count, uint32_t p_instance_count, uint32_t p_base_vertex, uint32_t p_first_instance) override final;
|
||||
virtual void command_render_draw_indexed(CommandBufferID p_cmd_buffer, uint32_t p_index_count, uint32_t p_instance_count, uint32_t p_first_index, int32_t p_vertex_offset, uint32_t p_first_instance) override final;
|
||||
virtual void command_render_draw_indexed_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final;
|
||||
virtual void command_render_draw_indexed_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final;
|
||||
virtual void command_render_draw_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final;
|
||||
virtual void command_render_draw_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final;
|
||||
|
||||
// Buffer binding.
|
||||
virtual void command_render_bind_vertex_buffers(CommandBufferID p_cmd_buffer, uint32_t p_binding_count, const BufferID *p_buffers, const uint64_t *p_offsets) override final;
|
||||
virtual void command_render_bind_index_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, IndexBufferFormat p_format, uint64_t p_offset) override final;
|
||||
|
||||
// Dynamic state.
|
||||
virtual void command_render_set_blend_constants(CommandBufferID p_cmd_buffer, const Color &p_constants) override final;
|
||||
virtual void command_render_set_line_width(CommandBufferID p_cmd_buffer, float p_width) override final;
|
||||
|
||||
// ----- PIPELINE -----
|
||||
|
||||
virtual PipelineID render_pipeline_create(
|
||||
ShaderID p_shader,
|
||||
VertexFormatID p_vertex_format,
|
||||
RenderPrimitive p_render_primitive,
|
||||
PipelineRasterizationState p_rasterization_state,
|
||||
PipelineMultisampleState p_multisample_state,
|
||||
PipelineDepthStencilState p_depth_stencil_state,
|
||||
PipelineColorBlendState p_blend_state,
|
||||
VectorView<int32_t> p_color_attachments,
|
||||
BitField<PipelineDynamicStateFlags> p_dynamic_state,
|
||||
RenderPassID p_render_pass,
|
||||
uint32_t p_render_subpass,
|
||||
VectorView<PipelineSpecializationConstant> p_specialization_constants) override final;
|
||||
|
||||
#pragma mark - Compute
|
||||
|
||||
// ----- COMMANDS -----
|
||||
|
||||
// Binding.
|
||||
virtual void command_bind_compute_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) override final;
|
||||
virtual void command_bind_compute_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final;
|
||||
|
||||
// Dispatching.
|
||||
virtual void command_compute_dispatch(CommandBufferID p_cmd_buffer, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) override final;
|
||||
virtual void command_compute_dispatch_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset) override final;
|
||||
|
||||
// ----- PIPELINE -----
|
||||
|
||||
virtual PipelineID compute_pipeline_create(ShaderID p_shader, VectorView<PipelineSpecializationConstant> p_specialization_constants) override final;
|
||||
|
||||
#pragma mark - Queries
|
||||
|
||||
// ----- TIMESTAMP -----
|
||||
|
||||
// Basic.
|
||||
virtual QueryPoolID timestamp_query_pool_create(uint32_t p_query_count) override final;
|
||||
virtual void timestamp_query_pool_free(QueryPoolID p_pool_id) override final;
|
||||
virtual void timestamp_query_pool_get_results(QueryPoolID p_pool_id, uint32_t p_query_count, uint64_t *r_results) override final;
|
||||
virtual uint64_t timestamp_query_result_to_time(uint64_t p_result) override final;
|
||||
|
||||
// Commands.
|
||||
virtual void command_timestamp_query_pool_reset(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_query_count) override final;
|
||||
virtual void command_timestamp_write(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_index) override final;
|
||||
|
||||
#pragma mark - Labels
|
||||
|
||||
virtual void command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) override final;
|
||||
virtual void command_end_label(CommandBufferID p_cmd_buffer) override final;
|
||||
|
||||
#pragma mark - Submission
|
||||
|
||||
virtual void begin_segment(uint32_t p_frame_index, uint32_t p_frames_drawn) override final;
|
||||
virtual void end_segment() override final;
|
||||
|
||||
#pragma mark - Miscellaneous
|
||||
|
||||
virtual void set_object_name(ObjectType p_type, ID p_driver_id, const String &p_name) override final;
|
||||
virtual uint64_t get_resource_native_handle(DriverResource p_type, ID p_driver_id) override final;
|
||||
virtual uint64_t get_total_memory_used() override final;
|
||||
virtual uint64_t limit_get(Limit p_limit) override final;
|
||||
virtual uint64_t api_trait_get(ApiTrait p_trait) override final;
|
||||
virtual bool has_feature(Features p_feature) override final;
|
||||
virtual const MultiviewCapabilities &get_multiview_capabilities() override final;
|
||||
virtual String get_api_name() const override final { return "Metal"; };
|
||||
virtual String get_api_version() const override final;
|
||||
virtual String get_pipeline_cache_uuid() const override final;
|
||||
virtual const Capabilities &get_capabilities() const override final;
|
||||
virtual bool is_composite_alpha_supported(CommandQueueID p_queue) const override final;
|
||||
|
||||
// Metal-specific.
|
||||
id<MTLDevice> get_device() const { return device; }
|
||||
PixelFormats &get_pixel_formats() const { return *pixel_formats; }
|
||||
MDResourceCache &get_resource_cache() const { return *resource_cache; }
|
||||
MetalDeviceProperties const &get_device_properties() const { return *metal_device_properties; }
|
||||
|
||||
_FORCE_INLINE_ uint32_t get_metal_buffer_index_for_vertex_attribute_binding(uint32_t p_binding) {
|
||||
return (metal_device_properties->limits.maxPerStageBufferCount - 1) - p_binding;
|
||||
}
|
||||
|
||||
size_t get_texel_buffer_alignment_for_format(RDD::DataFormat p_format) const;
|
||||
size_t get_texel_buffer_alignment_for_format(MTLPixelFormat p_format) const;
|
||||
|
||||
/******************/
|
||||
RenderingDeviceDriverMetal(RenderingContextDriverMetal *p_context_driver);
|
||||
~RenderingDeviceDriverMetal();
|
||||
};
|
||||
|
||||
#endif // RENDERING_DEVICE_DRIVER_METAL_H
|
3883
drivers/metal/rendering_device_driver_metal.mm
Normal file
3883
drivers/metal/rendering_device_driver_metal.mm
Normal file
File diff suppressed because it is too large
Load Diff
@ -5032,6 +5032,8 @@ String EditorNode::_get_system_info() const {
|
||||
driver_name = "Vulkan";
|
||||
} else if (driver_name.begins_with("opengl3")) {
|
||||
driver_name = "GLES3";
|
||||
} else if (driver_name == "metal") {
|
||||
driver_name = "Metal";
|
||||
}
|
||||
|
||||
// Join info.
|
||||
|
@ -1935,6 +1935,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
||||
{
|
||||
String driver_hints = "";
|
||||
String driver_hints_with_d3d12 = "";
|
||||
String driver_hints_with_metal = "";
|
||||
|
||||
{
|
||||
Vector<String> driver_hints_arr;
|
||||
@ -1947,18 +1948,25 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
||||
driver_hints_arr.push_back("d3d12");
|
||||
#endif
|
||||
driver_hints_with_d3d12 = String(",").join(driver_hints_arr);
|
||||
|
||||
#ifdef METAL_ENABLED
|
||||
// Make metal the preferred and default driver.
|
||||
driver_hints_arr.insert(0, "metal");
|
||||
#endif
|
||||
driver_hints_with_metal = String(",").join(driver_hints_arr);
|
||||
}
|
||||
|
||||
String default_driver = driver_hints.get_slice(",", 0);
|
||||
String default_driver_with_d3d12 = driver_hints_with_d3d12.get_slice(",", 0);
|
||||
String default_driver_with_metal = driver_hints_with_metal.get_slice(",", 0);
|
||||
|
||||
// For now everything defaults to vulkan when available. This can change in future updates.
|
||||
GLOBAL_DEF_RST_NOVAL("rendering/rendering_device/driver", default_driver);
|
||||
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.windows", PROPERTY_HINT_ENUM, driver_hints_with_d3d12), default_driver_with_d3d12);
|
||||
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.linuxbsd", PROPERTY_HINT_ENUM, driver_hints), default_driver);
|
||||
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.android", PROPERTY_HINT_ENUM, driver_hints), default_driver);
|
||||
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.ios", PROPERTY_HINT_ENUM, driver_hints), default_driver);
|
||||
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.macos", PROPERTY_HINT_ENUM, driver_hints), default_driver);
|
||||
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.ios", PROPERTY_HINT_ENUM, driver_hints_with_metal), default_driver_with_metal);
|
||||
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.macos", PROPERTY_HINT_ENUM, driver_hints_with_metal), default_driver_with_metal);
|
||||
|
||||
GLOBAL_DEF_RST("rendering/rendering_device/fallback_to_vulkan", true);
|
||||
GLOBAL_DEF_RST("rendering/rendering_device/fallback_to_d3d12", true);
|
||||
@ -2232,6 +2240,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
||||
#endif
|
||||
#ifdef D3D12_ENABLED
|
||||
available_drivers.push_back("d3d12");
|
||||
#endif
|
||||
#ifdef METAL_ENABLED
|
||||
available_drivers.push_back("metal");
|
||||
#endif
|
||||
}
|
||||
#ifdef GLES3_ENABLED
|
||||
|
@ -1,7 +1,7 @@
|
||||
def can_build(env, platform):
|
||||
# glslang is only needed when Vulkan or Direct3D 12-based renderers are available,
|
||||
# glslang is only needed when Vulkan, Direct3D 12 or Metal-based renderers are available,
|
||||
# as OpenGL doesn't use glslang.
|
||||
return env["vulkan"] or env["d3d12"]
|
||||
return env["vulkan"] or env["d3d12"] or env["metal"]
|
||||
|
||||
|
||||
def configure(env):
|
||||
|
@ -73,6 +73,9 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
|
||||
// - SPIRV-Reflect won't be able to parse the compute workgroup size.
|
||||
// - We want to play it safe with NIR-DXIL.
|
||||
TargetVersion = glslang::EShTargetSpv_1_3;
|
||||
} else if (capabilities.device_family == RDD::DEVICE_METAL) {
|
||||
ClientVersion = glslang::EShTargetVulkan_1_1;
|
||||
TargetVersion = glslang::EShTargetSpv_1_6;
|
||||
} else {
|
||||
// once we support other backends we'll need to do something here
|
||||
if (r_error) {
|
||||
|
@ -51,6 +51,7 @@ def get_flags():
|
||||
"arch": "arm64",
|
||||
"target": "template_debug",
|
||||
"use_volk": False,
|
||||
"metal": True,
|
||||
"supported": ["mono"],
|
||||
"builtin_pcre2_with_jit": False,
|
||||
}
|
||||
@ -154,8 +155,22 @@ def configure(env: "SConsEnvironment"):
|
||||
env.Prepend(CPPPATH=["#platform/ios"])
|
||||
env.Append(CPPDEFINES=["IOS_ENABLED", "UNIX_ENABLED", "COREAUDIO_ENABLED"])
|
||||
|
||||
if env["metal"] and env["arch"] != "arm64":
|
||||
# Only supported on arm64, so skip it for x86_64 builds.
|
||||
env["metal"] = False
|
||||
|
||||
if env["metal"]:
|
||||
env.AppendUnique(CPPDEFINES=["METAL_ENABLED", "RD_ENABLED"])
|
||||
env.Prepend(
|
||||
CPPPATH=[
|
||||
"$IOS_SDK_PATH/System/Library/Frameworks/Metal.framework/Headers",
|
||||
"$IOS_SDK_PATH/System/Library/Frameworks/QuartzCore.framework/Headers",
|
||||
]
|
||||
)
|
||||
env.Prepend(CPPPATH=["#thirdparty/spirv-cross"])
|
||||
|
||||
if env["vulkan"]:
|
||||
env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
|
||||
env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
|
||||
|
||||
if env["opengl3"]:
|
||||
env.Append(CPPDEFINES=["GLES3_ENABLED", "GLES_SILENCE_DEPRECATION"])
|
||||
|
@ -47,6 +47,10 @@
|
||||
#include <vulkan/vulkan.h>
|
||||
#endif
|
||||
#endif // VULKAN_ENABLED
|
||||
|
||||
#if defined(METAL_ENABLED)
|
||||
#include "drivers/metal/rendering_context_driver_metal.h"
|
||||
#endif // METAL_ENABLED
|
||||
#endif // RD_ENABLED
|
||||
|
||||
#if defined(GLES3_ENABLED)
|
||||
|
@ -72,6 +72,13 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode
|
||||
union {
|
||||
#ifdef VULKAN_ENABLED
|
||||
RenderingContextDriverVulkanIOS::WindowPlatformData vulkan;
|
||||
#endif
|
||||
#ifdef METAL_ENABLED
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
||||
// Eliminate "RenderingContextDriverMetal is only available on iOS 14.0 or newer".
|
||||
RenderingContextDriverMetal::WindowPlatformData metal;
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
} wpd;
|
||||
|
||||
@ -85,7 +92,19 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode
|
||||
rendering_context = memnew(RenderingContextDriverVulkanIOS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef METAL_ENABLED
|
||||
if (rendering_driver == "metal") {
|
||||
if (@available(iOS 14.0, *)) {
|
||||
layer = [AppDelegate.viewController.godotView initializeRenderingForDriver:@"metal"];
|
||||
wpd.metal.layer = (CAMetalLayer *)layer;
|
||||
rendering_context = memnew(RenderingContextDriverMetal);
|
||||
} else {
|
||||
OS::get_singleton()->alert("Metal is only supported on iOS 14.0 and later.");
|
||||
r_error = ERR_UNAVAILABLE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (rendering_context) {
|
||||
if (rendering_context->initialize() != OK) {
|
||||
ERR_PRINT(vformat("Failed to initialize %s context", rendering_driver));
|
||||
@ -172,6 +191,11 @@ Vector<String> DisplayServerIOS::get_rendering_drivers_func() {
|
||||
#if defined(VULKAN_ENABLED)
|
||||
drivers.push_back("vulkan");
|
||||
#endif
|
||||
#if defined(METAL_ENABLED)
|
||||
if (@available(ios 14.0, *)) {
|
||||
drivers.push_back("metal");
|
||||
}
|
||||
#endif
|
||||
#if defined(GLES3_ENABLED)
|
||||
drivers.push_back("opengl3");
|
||||
#endif
|
||||
|
@ -282,6 +282,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
|
||||
|
||||
// TODO(sgc): set to iOS 14.0 for Metal
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/min_ios_version"), "12.0"));
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/additional_plist_content", PROPERTY_HINT_MULTILINE_TEXT), ""));
|
||||
@ -2656,6 +2657,13 @@ bool EditorExportPlatformIOS::has_valid_export_configuration(const Ref<EditorExp
|
||||
}
|
||||
}
|
||||
|
||||
if (GLOBAL_GET("rendering/rendering_device/driver.ios") == "metal") {
|
||||
float version = p_preset->get("application/min_ios_version").operator String().to_float();
|
||||
if (version < 14.0) {
|
||||
err += TTR("Metal renderer require iOS 14+.") + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!err.is_empty()) {
|
||||
r_error = err;
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ static const float earth_gravity = 9.80665;
|
||||
|
||||
CALayer<DisplayLayer> *layer;
|
||||
|
||||
if ([driverName isEqualToString:@"vulkan"]) {
|
||||
if ([driverName isEqualToString:@"vulkan"] || [driverName isEqualToString:@"metal"]) {
|
||||
#if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
|
||||
if (@available(iOS 13, *)) {
|
||||
layer = [GodotMetalLayer layer];
|
||||
|
@ -56,6 +56,7 @@ def get_flags():
|
||||
return {
|
||||
"arch": detect_arch(),
|
||||
"use_volk": False,
|
||||
"metal": True,
|
||||
"supported": ["mono"],
|
||||
}
|
||||
|
||||
@ -239,9 +240,22 @@ def configure(env: "SConsEnvironment"):
|
||||
|
||||
env.Append(LINKFLAGS=["-rpath", "@executable_path/../Frameworks", "-rpath", "@executable_path"])
|
||||
|
||||
if env["metal"] and env["arch"] != "arm64":
|
||||
# Only supported on arm64, so skip it for x86_64 builds.
|
||||
env["metal"] = False
|
||||
|
||||
extra_frameworks = set()
|
||||
|
||||
if env["metal"]:
|
||||
env.AppendUnique(CPPDEFINES=["METAL_ENABLED", "RD_ENABLED"])
|
||||
extra_frameworks.add("Metal")
|
||||
extra_frameworks.add("MetalKit")
|
||||
env.Prepend(CPPPATH=["#thirdparty/spirv-cross"])
|
||||
|
||||
if env["vulkan"]:
|
||||
env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
|
||||
env.Append(LINKFLAGS=["-framework", "Metal", "-framework", "IOSurface"])
|
||||
env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
|
||||
extra_frameworks.add("Metal")
|
||||
extra_frameworks.add("IOSurface")
|
||||
if not env["use_volk"]:
|
||||
env.Append(LINKFLAGS=["-lMoltenVK"])
|
||||
|
||||
@ -260,3 +274,7 @@ def configure(env: "SConsEnvironment"):
|
||||
"MoltenVK SDK installation directory not found, use 'vulkan_sdk_path' SCons parameter to specify SDK path."
|
||||
)
|
||||
sys.exit(255)
|
||||
|
||||
if len(extra_frameworks) > 0:
|
||||
frameworks = [item for key in extra_frameworks for item in ["-framework", key]]
|
||||
env.Append(LINKFLAGS=frameworks)
|
||||
|
@ -47,6 +47,9 @@
|
||||
#if defined(VULKAN_ENABLED)
|
||||
#include "rendering_context_driver_vulkan_macos.h"
|
||||
#endif // VULKAN_ENABLED
|
||||
#if defined(METAL_ENABLED)
|
||||
#include "drivers/metal/rendering_context_driver_metal.h"
|
||||
#endif
|
||||
#endif // RD_ENABLED
|
||||
|
||||
#define BitMap _QDBitMap // Suppress deprecated QuickDraw definition.
|
||||
|
@ -138,12 +138,20 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod
|
||||
union {
|
||||
#ifdef VULKAN_ENABLED
|
||||
RenderingContextDriverVulkanMacOS::WindowPlatformData vulkan;
|
||||
#endif
|
||||
#ifdef METAL_ENABLED
|
||||
RenderingContextDriverMetal::WindowPlatformData metal;
|
||||
#endif
|
||||
} wpd;
|
||||
#ifdef VULKAN_ENABLED
|
||||
if (rendering_driver == "vulkan") {
|
||||
wpd.vulkan.layer_ptr = (CAMetalLayer *const *)&layer;
|
||||
}
|
||||
#endif
|
||||
#ifdef METAL_ENABLED
|
||||
if (rendering_driver == "metal") {
|
||||
wpd.metal.layer = (CAMetalLayer *)layer;
|
||||
}
|
||||
#endif
|
||||
Error err = rendering_context->window_create(window_id_counter, &wpd);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, vformat("Can't create a %s context", rendering_driver));
|
||||
@ -2700,7 +2708,7 @@ void DisplayServerMacOS::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_
|
||||
gl_manager_legacy->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED);
|
||||
}
|
||||
#endif
|
||||
#if defined(VULKAN_ENABLED)
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
rendering_context->window_set_vsync_mode(p_window, p_vsync_mode);
|
||||
}
|
||||
@ -2717,7 +2725,7 @@ DisplayServer::VSyncMode DisplayServerMacOS::window_get_vsync_mode(WindowID p_wi
|
||||
return (gl_manager_legacy->is_using_vsync() ? DisplayServer::VSyncMode::VSYNC_ENABLED : DisplayServer::VSyncMode::VSYNC_DISABLED);
|
||||
}
|
||||
#endif
|
||||
#if defined(VULKAN_ENABLED)
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
return rendering_context->window_get_vsync_mode(p_window);
|
||||
}
|
||||
@ -3301,6 +3309,9 @@ Vector<String> DisplayServerMacOS::get_rendering_drivers_func() {
|
||||
#if defined(VULKAN_ENABLED)
|
||||
drivers.push_back("vulkan");
|
||||
#endif
|
||||
#if defined(METAL_ENABLED)
|
||||
drivers.push_back("metal");
|
||||
#endif
|
||||
#if defined(GLES3_ENABLED)
|
||||
drivers.push_back("opengl3");
|
||||
drivers.push_back("opengl3_angle");
|
||||
@ -3623,6 +3634,11 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM
|
||||
rendering_context = memnew(RenderingContextDriverVulkanMacOS);
|
||||
}
|
||||
#endif
|
||||
#if defined(METAL_ENABLED)
|
||||
if (rendering_driver == "metal") {
|
||||
rendering_context = memnew(RenderingContextDriverMetal);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (rendering_context) {
|
||||
if (rendering_context->initialize() != OK) {
|
||||
|
@ -458,6 +458,7 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/additional_plist_content", PROPERTY_HINT_MULTILINE_TEXT), ""));
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/platform_build"), "14C18"));
|
||||
// TODO(sgc): Need to set appropriate version when using Metal
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_version"), "13.1"));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_build"), "22C55"));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_name"), "macosx13.1"));
|
||||
|
@ -185,7 +185,14 @@ private:
|
||||
};
|
||||
|
||||
uint32_t cluster_size = 32;
|
||||
#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)
|
||||
// Results in visual artifacts on macOS and iOS when using MSAA and subgroups.
|
||||
// Using subgroups and disabling MSAA is the optimal solution for now and also works
|
||||
// with MoltenVK.
|
||||
bool use_msaa = false;
|
||||
#else
|
||||
bool use_msaa = true;
|
||||
#endif
|
||||
Divisor divisor = DIVISOR_4;
|
||||
|
||||
Size2i screen_size;
|
||||
|
@ -191,9 +191,14 @@ void ShaderRD::_build_variant_code(StringBuilder &builder, uint32_t p_variant, c
|
||||
for (const KeyValue<StringName, CharString> &E : p_version->code_sections) {
|
||||
builder.append(String("#define ") + String(E.key) + "_CODE_USED\n");
|
||||
}
|
||||
#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)
|
||||
builder.append("#define MOLTENVK_USED\n");
|
||||
#if (defined(MACOS_ENABLED) || defined(IOS_ENABLED))
|
||||
if (RD::get_singleton()->get_device_capabilities().device_family == RDD::DEVICE_VULKAN) {
|
||||
builder.append("#define MOLTENVK_USED\n");
|
||||
}
|
||||
// Image atomics are supported on Metal 3.1 but no support in MoltenVK or SPIRV-Cross yet.
|
||||
builder.append("#define NO_IMAGE_ATOMICS\n");
|
||||
#endif
|
||||
|
||||
builder.append(String("#define RENDER_DRIVER_") + OS::get_singleton()->get_current_rendering_driver_name().to_upper() + "\n");
|
||||
} break;
|
||||
case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: {
|
||||
|
@ -34,7 +34,7 @@ layout(push_constant, std430) uniform Params {
|
||||
}
|
||||
params;
|
||||
|
||||
#ifdef MOLTENVK_USED
|
||||
#ifdef NO_IMAGE_ATOMICS
|
||||
layout(set = 1, binding = 1) volatile buffer emissive_only_map_buffer {
|
||||
uint emissive_only_map[];
|
||||
};
|
||||
@ -64,7 +64,7 @@ layout(set = 1, binding = 2, std140) uniform SceneParams {
|
||||
}
|
||||
scene_params;
|
||||
|
||||
#ifdef MOLTENVK_USED
|
||||
#ifdef NO_IMAGE_ATOMICS
|
||||
layout(set = 1, binding = 3) volatile buffer density_only_map_buffer {
|
||||
uint density_only_map[];
|
||||
};
|
||||
@ -117,7 +117,7 @@ void main() {
|
||||
if (any(greaterThanEqual(pos, scene_params.fog_volume_size))) {
|
||||
return; //do not compute
|
||||
}
|
||||
#ifdef MOLTENVK_USED
|
||||
#ifdef NO_IMAGE_ATOMICS
|
||||
uint lpos = pos.z * scene_params.fog_volume_size.x * scene_params.fog_volume_size.y + pos.y * scene_params.fog_volume_size.x + pos.x;
|
||||
#endif
|
||||
|
||||
@ -222,7 +222,7 @@ void main() {
|
||||
density *= cull_mask;
|
||||
if (abs(density) > 0.001) {
|
||||
int final_density = int(density * DENSITY_SCALE);
|
||||
#ifdef MOLTENVK_USED
|
||||
#ifdef NO_IMAGE_ATOMICS
|
||||
atomicAdd(density_only_map[lpos], uint(final_density));
|
||||
#else
|
||||
imageAtomicAdd(density_only_map, pos, uint(final_density));
|
||||
@ -236,7 +236,7 @@ void main() {
|
||||
uvec3 emission_u = uvec3(emission.r * 511.0, emission.g * 511.0, emission.b * 255.0);
|
||||
// R and G have 11 bits each and B has 10. Then pack them into a 32 bit uint
|
||||
uint final_emission = emission_u.r << 21 | emission_u.g << 10 | emission_u.b;
|
||||
#ifdef MOLTENVK_USED
|
||||
#ifdef NO_IMAGE_ATOMICS
|
||||
uint prev_emission = atomicAdd(emissive_only_map[lpos], final_emission);
|
||||
#else
|
||||
uint prev_emission = imageAtomicAdd(emissive_only_map, pos, final_emission);
|
||||
@ -252,7 +252,7 @@ void main() {
|
||||
if (any(overflowing)) {
|
||||
uvec3 overflow_factor = mix(uvec3(0), uvec3(2047 << 21, 2047 << 10, 1023), overflowing);
|
||||
uint force_max = overflow_factor.r | overflow_factor.g | overflow_factor.b;
|
||||
#ifdef MOLTENVK_USED
|
||||
#ifdef NO_IMAGE_ATOMICS
|
||||
atomicOr(emissive_only_map[lpos], force_max);
|
||||
#else
|
||||
imageAtomicOr(emissive_only_map, pos, force_max);
|
||||
@ -267,7 +267,7 @@ void main() {
|
||||
uvec3 scattering_u = uvec3(scattering.r * 2047.0, scattering.g * 2047.0, scattering.b * 1023.0);
|
||||
// R and G have 11 bits each and B has 10. Then pack them into a 32 bit uint
|
||||
uint final_scattering = scattering_u.r << 21 | scattering_u.g << 10 | scattering_u.b;
|
||||
#ifdef MOLTENVK_USED
|
||||
#ifdef NO_IMAGE_ATOMICS
|
||||
uint prev_scattering = atomicAdd(light_only_map[lpos], final_scattering);
|
||||
#else
|
||||
uint prev_scattering = imageAtomicAdd(light_only_map, pos, final_scattering);
|
||||
@ -283,7 +283,7 @@ void main() {
|
||||
if (any(overflowing)) {
|
||||
uvec3 overflow_factor = mix(uvec3(0), uvec3(2047 << 21, 2047 << 10, 1023), overflowing);
|
||||
uint force_max = overflow_factor.r | overflow_factor.g | overflow_factor.b;
|
||||
#ifdef MOLTENVK_USED
|
||||
#ifdef NO_IMAGE_ATOMICS
|
||||
atomicOr(light_only_map[lpos], force_max);
|
||||
#else
|
||||
imageAtomicOr(light_only_map, pos, force_max);
|
||||
|
@ -190,7 +190,7 @@ params;
|
||||
#ifndef MODE_COPY
|
||||
layout(set = 0, binding = 15) uniform texture3D prev_density_texture;
|
||||
|
||||
#ifdef MOLTENVK_USED
|
||||
#ifdef NO_IMAGE_ATOMICS
|
||||
layout(set = 0, binding = 16) buffer density_only_map_buffer {
|
||||
uint density_only_map[];
|
||||
};
|
||||
@ -287,7 +287,7 @@ void main() {
|
||||
if (any(greaterThanEqual(pos, params.fog_volume_size))) {
|
||||
return; //do not compute
|
||||
}
|
||||
#ifdef MOLTENVK_USED
|
||||
#ifdef NO_IMAGE_ATOMICS
|
||||
uint lpos = pos.z * params.fog_volume_size.x * params.fog_volume_size.y + pos.y * params.fog_volume_size.x + pos.x;
|
||||
#endif
|
||||
|
||||
@ -353,7 +353,7 @@ void main() {
|
||||
vec3 total_light = vec3(0.0);
|
||||
|
||||
float total_density = params.base_density;
|
||||
#ifdef MOLTENVK_USED
|
||||
#ifdef NO_IMAGE_ATOMICS
|
||||
uint local_density = density_only_map[lpos];
|
||||
#else
|
||||
uint local_density = imageLoad(density_only_map, pos).x;
|
||||
@ -362,7 +362,7 @@ void main() {
|
||||
total_density += float(int(local_density)) / DENSITY_SCALE;
|
||||
total_density = max(0.0, total_density);
|
||||
|
||||
#ifdef MOLTENVK_USED
|
||||
#ifdef NO_IMAGE_ATOMICS
|
||||
uint scattering_u = light_only_map[lpos];
|
||||
#else
|
||||
uint scattering_u = imageLoad(light_only_map, pos).x;
|
||||
@ -370,7 +370,7 @@ void main() {
|
||||
vec3 scattering = vec3(scattering_u >> 21, (scattering_u << 11) >> 21, scattering_u % 1024) / vec3(2047.0, 2047.0, 1023.0);
|
||||
scattering += params.base_scattering * params.base_density;
|
||||
|
||||
#ifdef MOLTENVK_USED
|
||||
#ifdef NO_IMAGE_ATOMICS
|
||||
uint emission_u = emissive_only_map[lpos];
|
||||
#else
|
||||
uint emission_u = imageLoad(emissive_only_map, pos).x;
|
||||
@ -710,7 +710,7 @@ void main() {
|
||||
final_density = mix(final_density, reprojected_density, reproject_amount);
|
||||
|
||||
imageStore(density_map, pos, final_density);
|
||||
#ifdef MOLTENVK_USED
|
||||
#ifdef NO_IMAGE_ATOMICS
|
||||
density_only_map[lpos] = 0;
|
||||
light_only_map[lpos] = 0;
|
||||
emissive_only_map[lpos] = 0;
|
||||
|
@ -2374,7 +2374,7 @@ void fragment_shader(in SceneData scene_data) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MOLTENVK_USED
|
||||
#ifdef NO_IMAGE_ATOMICS
|
||||
imageStore(geom_facing_grid, grid_pos, uvec4(imageLoad(geom_facing_grid, grid_pos).r | facing_bits)); //store facing bits
|
||||
#else
|
||||
imageAtomicOr(geom_facing_grid, grid_pos, facing_bits); //store facing bits
|
||||
|
@ -2826,6 +2826,7 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p
|
||||
for (int j = 0; j < (int)uniform_count; j++) {
|
||||
if (uniforms[j].binding == set_uniform.binding) {
|
||||
uniform_idx = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(uniform_idx == -1, RID(),
|
||||
@ -3240,6 +3241,7 @@ RID RenderingDevice::render_pipeline_create(RID p_shader, FramebufferFormatID p_
|
||||
for (int j = 0; j < vd.vertex_formats.size(); j++) {
|
||||
if (vd.vertex_formats[j].location == i) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -759,6 +759,7 @@ public:
|
||||
DEVICE_OPENGL,
|
||||
DEVICE_VULKAN,
|
||||
DEVICE_DIRECTX,
|
||||
DEVICE_METAL,
|
||||
};
|
||||
|
||||
struct Capabilities {
|
||||
|
16
thirdparty/README.md
vendored
16
thirdparty/README.md
vendored
@ -827,6 +827,22 @@ and solve conflicts and also enrich the feature set originally
|
||||
proposed by these libraries and better integrate them with Godot.
|
||||
|
||||
|
||||
## spirv-cross
|
||||
|
||||
- Upstream: https://github.com/KhronosGroup/SPIRV-Cross
|
||||
- Version: vulkan-sdk-1.3.290.0 (5d127b917f080c6f052553c47170ec0ba702e54f, 2024)
|
||||
- License: Apache 2.0
|
||||
|
||||
Files extracted from upstream source:
|
||||
|
||||
- All `.cpp`, `.hpp` and `.h` files, minus `main.cpp`, `spirv_cross_c.*`, `spirv_hlsl.*`, `spirv_cpp.*`
|
||||
- `include/` folder
|
||||
- `LICENSE` and `LICENSES/` folder, minus `CC-BY-4.0.txt`
|
||||
|
||||
Versions of this SDK do not have to match the `vulkan` section, as this SDK is required
|
||||
to generate Metal source from Vulkan SPIR-V.
|
||||
|
||||
|
||||
## spirv-reflect
|
||||
|
||||
- Upstream: https://github.com/KhronosGroup/SPIRV-Reflect
|
||||
|
114
thirdparty/spirv-cross/GLSL.std.450.h
vendored
Normal file
114
thirdparty/spirv-cross/GLSL.std.450.h
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2014-2016,2021 The Khronos Group, Inc.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
|
||||
* STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
|
||||
* HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
|
||||
*/
|
||||
|
||||
#ifndef GLSLstd450_H
|
||||
#define GLSLstd450_H
|
||||
|
||||
static const int GLSLstd450Version = 100;
|
||||
static const int GLSLstd450Revision = 3;
|
||||
|
||||
enum GLSLstd450 {
|
||||
GLSLstd450Bad = 0, // Don't use
|
||||
|
||||
GLSLstd450Round = 1,
|
||||
GLSLstd450RoundEven = 2,
|
||||
GLSLstd450Trunc = 3,
|
||||
GLSLstd450FAbs = 4,
|
||||
GLSLstd450SAbs = 5,
|
||||
GLSLstd450FSign = 6,
|
||||
GLSLstd450SSign = 7,
|
||||
GLSLstd450Floor = 8,
|
||||
GLSLstd450Ceil = 9,
|
||||
GLSLstd450Fract = 10,
|
||||
|
||||
GLSLstd450Radians = 11,
|
||||
GLSLstd450Degrees = 12,
|
||||
GLSLstd450Sin = 13,
|
||||
GLSLstd450Cos = 14,
|
||||
GLSLstd450Tan = 15,
|
||||
GLSLstd450Asin = 16,
|
||||
GLSLstd450Acos = 17,
|
||||
GLSLstd450Atan = 18,
|
||||
GLSLstd450Sinh = 19,
|
||||
GLSLstd450Cosh = 20,
|
||||
GLSLstd450Tanh = 21,
|
||||
GLSLstd450Asinh = 22,
|
||||
GLSLstd450Acosh = 23,
|
||||
GLSLstd450Atanh = 24,
|
||||
GLSLstd450Atan2 = 25,
|
||||
|
||||
GLSLstd450Pow = 26,
|
||||
GLSLstd450Exp = 27,
|
||||
GLSLstd450Log = 28,
|
||||
GLSLstd450Exp2 = 29,
|
||||
GLSLstd450Log2 = 30,
|
||||
GLSLstd450Sqrt = 31,
|
||||
GLSLstd450InverseSqrt = 32,
|
||||
|
||||
GLSLstd450Determinant = 33,
|
||||
GLSLstd450MatrixInverse = 34,
|
||||
|
||||
GLSLstd450Modf = 35, // second operand needs an OpVariable to write to
|
||||
GLSLstd450ModfStruct = 36, // no OpVariable operand
|
||||
GLSLstd450FMin = 37,
|
||||
GLSLstd450UMin = 38,
|
||||
GLSLstd450SMin = 39,
|
||||
GLSLstd450FMax = 40,
|
||||
GLSLstd450UMax = 41,
|
||||
GLSLstd450SMax = 42,
|
||||
GLSLstd450FClamp = 43,
|
||||
GLSLstd450UClamp = 44,
|
||||
GLSLstd450SClamp = 45,
|
||||
GLSLstd450FMix = 46,
|
||||
GLSLstd450IMix = 47, // Reserved
|
||||
GLSLstd450Step = 48,
|
||||
GLSLstd450SmoothStep = 49,
|
||||
|
||||
GLSLstd450Fma = 50,
|
||||
GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to
|
||||
GLSLstd450FrexpStruct = 52, // no OpVariable operand
|
||||
GLSLstd450Ldexp = 53,
|
||||
|
||||
GLSLstd450PackSnorm4x8 = 54,
|
||||
GLSLstd450PackUnorm4x8 = 55,
|
||||
GLSLstd450PackSnorm2x16 = 56,
|
||||
GLSLstd450PackUnorm2x16 = 57,
|
||||
GLSLstd450PackHalf2x16 = 58,
|
||||
GLSLstd450PackDouble2x32 = 59,
|
||||
GLSLstd450UnpackSnorm2x16 = 60,
|
||||
GLSLstd450UnpackUnorm2x16 = 61,
|
||||
GLSLstd450UnpackHalf2x16 = 62,
|
||||
GLSLstd450UnpackSnorm4x8 = 63,
|
||||
GLSLstd450UnpackUnorm4x8 = 64,
|
||||
GLSLstd450UnpackDouble2x32 = 65,
|
||||
|
||||
GLSLstd450Length = 66,
|
||||
GLSLstd450Distance = 67,
|
||||
GLSLstd450Cross = 68,
|
||||
GLSLstd450Normalize = 69,
|
||||
GLSLstd450FaceForward = 70,
|
||||
GLSLstd450Reflect = 71,
|
||||
GLSLstd450Refract = 72,
|
||||
|
||||
GLSLstd450FindILsb = 73,
|
||||
GLSLstd450FindSMsb = 74,
|
||||
GLSLstd450FindUMsb = 75,
|
||||
|
||||
GLSLstd450InterpolateAtCentroid = 76,
|
||||
GLSLstd450InterpolateAtSample = 77,
|
||||
GLSLstd450InterpolateAtOffset = 78,
|
||||
|
||||
GLSLstd450NMin = 79,
|
||||
GLSLstd450NMax = 80,
|
||||
GLSLstd450NClamp = 81,
|
||||
|
||||
GLSLstd450Count
|
||||
};
|
||||
|
||||
#endif // #ifndef GLSLstd450_H
|
202
thirdparty/spirv-cross/LICENSE
vendored
Normal file
202
thirdparty/spirv-cross/LICENSE
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
208
thirdparty/spirv-cross/LICENSES/Apache-2.0.txt
vendored
Normal file
208
thirdparty/spirv-cross/LICENSES/Apache-2.0.txt
vendored
Normal 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.
|
23
thirdparty/spirv-cross/LICENSES/LicenseRef-KhronosFreeUse.txt
vendored
Normal file
23
thirdparty/spirv-cross/LICENSES/LicenseRef-KhronosFreeUse.txt
vendored
Normal 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.
|
19
thirdparty/spirv-cross/LICENSES/MIT.txt
vendored
Normal file
19
thirdparty/spirv-cross/LICENSES/MIT.txt
vendored
Normal 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.
|
80
thirdparty/spirv-cross/include/spirv_cross/barrier.hpp
vendored
Normal file
80
thirdparty/spirv-cross/include/spirv_cross/barrier.hpp
vendored
Normal 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
|
127
thirdparty/spirv-cross/include/spirv_cross/external_interface.h
vendored
Normal file
127
thirdparty/spirv-cross/include/spirv_cross/external_interface.h
vendored
Normal 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
|
63
thirdparty/spirv-cross/include/spirv_cross/image.hpp
vendored
Normal file
63
thirdparty/spirv-cross/include/spirv_cross/image.hpp
vendored
Normal 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
|
604
thirdparty/spirv-cross/include/spirv_cross/internal_interface.hpp
vendored
Normal file
604
thirdparty/spirv-cross/include/spirv_cross/internal_interface.hpp
vendored
Normal 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
|
106
thirdparty/spirv-cross/include/spirv_cross/sampler.hpp
vendored
Normal file
106
thirdparty/spirv-cross/include/spirv_cross/sampler.hpp
vendored
Normal 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
|
114
thirdparty/spirv-cross/include/spirv_cross/thread_group.hpp
vendored
Normal file
114
thirdparty/spirv-cross/include/spirv_cross/thread_group.hpp
vendored
Normal 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
|
2592
thirdparty/spirv-cross/spirv.hpp
vendored
Normal file
2592
thirdparty/spirv-cross/spirv.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
430
thirdparty/spirv-cross/spirv_cfg.cpp
vendored
Normal file
430
thirdparty/spirv-cross/spirv_cfg.cpp
vendored
Normal file
@ -0,0 +1,430 @@
|
||||
/*
|
||||
* Copyright 2016-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#include "spirv_cfg.hpp"
|
||||
#include "spirv_cross.hpp"
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace SPIRV_CROSS_NAMESPACE
|
||||
{
|
||||
CFG::CFG(Compiler &compiler_, const SPIRFunction &func_)
|
||||
: compiler(compiler_)
|
||||
, func(func_)
|
||||
{
|
||||
build_post_order_visit_order();
|
||||
build_immediate_dominators();
|
||||
}
|
||||
|
||||
uint32_t CFG::find_common_dominator(uint32_t a, uint32_t b) const
|
||||
{
|
||||
while (a != b)
|
||||
{
|
||||
if (get_visit_order(a) < get_visit_order(b))
|
||||
a = get_immediate_dominator(a);
|
||||
else
|
||||
b = get_immediate_dominator(b);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
void CFG::build_immediate_dominators()
|
||||
{
|
||||
// Traverse the post-order in reverse and build up the immediate dominator tree.
|
||||
immediate_dominators.clear();
|
||||
immediate_dominators[func.entry_block] = func.entry_block;
|
||||
|
||||
for (auto i = post_order.size(); i; i--)
|
||||
{
|
||||
uint32_t block = post_order[i - 1];
|
||||
auto &pred = preceding_edges[block];
|
||||
if (pred.empty()) // This is for the entry block, but we've already set up the dominators.
|
||||
continue;
|
||||
|
||||
for (auto &edge : pred)
|
||||
{
|
||||
if (immediate_dominators[block])
|
||||
{
|
||||
assert(immediate_dominators[edge]);
|
||||
immediate_dominators[block] = find_common_dominator(immediate_dominators[block], edge);
|
||||
}
|
||||
else
|
||||
immediate_dominators[block] = edge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CFG::is_back_edge(uint32_t to) const
|
||||
{
|
||||
// We have a back edge if the visit order is set with the temporary magic value 0.
|
||||
// Crossing edges will have already been recorded with a visit order.
|
||||
auto itr = visit_order.find(to);
|
||||
return itr != end(visit_order) && itr->second.get() == 0;
|
||||
}
|
||||
|
||||
bool CFG::has_visited_forward_edge(uint32_t to) const
|
||||
{
|
||||
// If > 0, we have visited the edge already, and this is not a back edge branch.
|
||||
auto itr = visit_order.find(to);
|
||||
return itr != end(visit_order) && itr->second.get() > 0;
|
||||
}
|
||||
|
||||
bool CFG::post_order_visit(uint32_t block_id)
|
||||
{
|
||||
// If we have already branched to this block (back edge), stop recursion.
|
||||
// If our branches are back-edges, we do not record them.
|
||||
// We have to record crossing edges however.
|
||||
if (has_visited_forward_edge(block_id))
|
||||
return true;
|
||||
else if (is_back_edge(block_id))
|
||||
return false;
|
||||
|
||||
// Block back-edges from recursively revisiting ourselves.
|
||||
visit_order[block_id].get() = 0;
|
||||
|
||||
auto &block = compiler.get<SPIRBlock>(block_id);
|
||||
|
||||
// If this is a loop header, add an implied branch to the merge target.
|
||||
// This is needed to avoid annoying cases with do { ... } while(false) loops often generated by inliners.
|
||||
// To the CFG, this is linear control flow, but we risk picking the do/while scope as our dominating block.
|
||||
// This makes sure that if we are accessing a variable outside the do/while, we choose the loop header as dominator.
|
||||
// We could use has_visited_forward_edge, but this break code-gen where the merge block is unreachable in the CFG.
|
||||
|
||||
// Make a point out of visiting merge target first. This is to make sure that post visit order outside the loop
|
||||
// is lower than inside the loop, which is going to be key for some traversal algorithms like post-dominance analysis.
|
||||
// For selection constructs true/false blocks will end up visiting the merge block directly and it works out fine,
|
||||
// but for loops, only the header might end up actually branching to merge block.
|
||||
if (block.merge == SPIRBlock::MergeLoop && post_order_visit(block.merge_block))
|
||||
add_branch(block_id, block.merge_block);
|
||||
|
||||
// First visit our branch targets.
|
||||
switch (block.terminator)
|
||||
{
|
||||
case SPIRBlock::Direct:
|
||||
if (post_order_visit(block.next_block))
|
||||
add_branch(block_id, block.next_block);
|
||||
break;
|
||||
|
||||
case SPIRBlock::Select:
|
||||
if (post_order_visit(block.true_block))
|
||||
add_branch(block_id, block.true_block);
|
||||
if (post_order_visit(block.false_block))
|
||||
add_branch(block_id, block.false_block);
|
||||
break;
|
||||
|
||||
case SPIRBlock::MultiSelect:
|
||||
{
|
||||
const auto &cases = compiler.get_case_list(block);
|
||||
for (const auto &target : cases)
|
||||
{
|
||||
if (post_order_visit(target.block))
|
||||
add_branch(block_id, target.block);
|
||||
}
|
||||
if (block.default_block && post_order_visit(block.default_block))
|
||||
add_branch(block_id, block.default_block);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// If this is a selection merge, add an implied branch to the merge target.
|
||||
// This is needed to avoid cases where an inner branch dominates the outer branch.
|
||||
// This can happen if one of the branches exit early, e.g.:
|
||||
// if (cond) { ...; break; } else { var = 100 } use_var(var);
|
||||
// We can use the variable without a Phi since there is only one possible parent here.
|
||||
// However, in this case, we need to hoist out the inner variable to outside the branch.
|
||||
// Use same strategy as loops.
|
||||
if (block.merge == SPIRBlock::MergeSelection && post_order_visit(block.next_block))
|
||||
{
|
||||
// If there is only one preceding edge to the merge block and it's not ourselves, we need a fixup.
|
||||
// Add a fake branch so any dominator in either the if (), or else () block, or a lone case statement
|
||||
// will be hoisted out to outside the selection merge.
|
||||
// If size > 1, the variable will be automatically hoisted, so we should not mess with it.
|
||||
// The exception here is switch blocks, where we can have multiple edges to merge block,
|
||||
// all coming from same scope, so be more conservative in this case.
|
||||
// Adding fake branches unconditionally breaks parameter preservation analysis,
|
||||
// which looks at how variables are accessed through the CFG.
|
||||
auto pred_itr = preceding_edges.find(block.next_block);
|
||||
if (pred_itr != end(preceding_edges))
|
||||
{
|
||||
auto &pred = pred_itr->second;
|
||||
auto succ_itr = succeeding_edges.find(block_id);
|
||||
size_t num_succeeding_edges = 0;
|
||||
if (succ_itr != end(succeeding_edges))
|
||||
num_succeeding_edges = succ_itr->second.size();
|
||||
|
||||
if (block.terminator == SPIRBlock::MultiSelect && num_succeeding_edges == 1)
|
||||
{
|
||||
// Multiple branches can come from the same scope due to "break;", so we need to assume that all branches
|
||||
// come from same case scope in worst case, even if there are multiple preceding edges.
|
||||
// If we have more than one succeeding edge from the block header, it should be impossible
|
||||
// to have a dominator be inside the block.
|
||||
// Only case this can go wrong is if we have 2 or more edges from block header and
|
||||
// 2 or more edges to merge block, and still have dominator be inside a case label.
|
||||
if (!pred.empty())
|
||||
add_branch(block_id, block.next_block);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pred.size() == 1 && *pred.begin() != block_id)
|
||||
add_branch(block_id, block.next_block);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the merge block does not have any preceding edges, i.e. unreachable, hallucinate it.
|
||||
// We're going to do code-gen for it, and domination analysis requires that we have at least one preceding edge.
|
||||
add_branch(block_id, block.next_block);
|
||||
}
|
||||
}
|
||||
|
||||
// Then visit ourselves. Start counting at one, to let 0 be a magic value for testing back vs. crossing edges.
|
||||
visit_order[block_id].get() = ++visit_count;
|
||||
post_order.push_back(block_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CFG::build_post_order_visit_order()
|
||||
{
|
||||
uint32_t block = func.entry_block;
|
||||
visit_count = 0;
|
||||
visit_order.clear();
|
||||
post_order.clear();
|
||||
post_order_visit(block);
|
||||
}
|
||||
|
||||
void CFG::add_branch(uint32_t from, uint32_t to)
|
||||
{
|
||||
const auto add_unique = [](SmallVector<uint32_t> &l, uint32_t value) {
|
||||
auto itr = find(begin(l), end(l), value);
|
||||
if (itr == end(l))
|
||||
l.push_back(value);
|
||||
};
|
||||
add_unique(preceding_edges[to], from);
|
||||
add_unique(succeeding_edges[from], to);
|
||||
}
|
||||
|
||||
uint32_t CFG::find_loop_dominator(uint32_t block_id) const
|
||||
{
|
||||
while (block_id != SPIRBlock::NoDominator)
|
||||
{
|
||||
auto itr = preceding_edges.find(block_id);
|
||||
if (itr == end(preceding_edges))
|
||||
return SPIRBlock::NoDominator;
|
||||
if (itr->second.empty())
|
||||
return SPIRBlock::NoDominator;
|
||||
|
||||
uint32_t pred_block_id = SPIRBlock::NoDominator;
|
||||
bool ignore_loop_header = false;
|
||||
|
||||
// If we are a merge block, go directly to the header block.
|
||||
// Only consider a loop dominator if we are branching from inside a block to a loop header.
|
||||
// NOTE: In the CFG we forced an edge from header to merge block always to support variable scopes properly.
|
||||
for (auto &pred : itr->second)
|
||||
{
|
||||
auto &pred_block = compiler.get<SPIRBlock>(pred);
|
||||
if (pred_block.merge == SPIRBlock::MergeLoop && pred_block.merge_block == ID(block_id))
|
||||
{
|
||||
pred_block_id = pred;
|
||||
ignore_loop_header = true;
|
||||
break;
|
||||
}
|
||||
else if (pred_block.merge == SPIRBlock::MergeSelection && pred_block.next_block == ID(block_id))
|
||||
{
|
||||
pred_block_id = pred;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// No merge block means we can just pick any edge. Loop headers dominate the inner loop, so any path we
|
||||
// take will lead there.
|
||||
if (pred_block_id == SPIRBlock::NoDominator)
|
||||
pred_block_id = itr->second.front();
|
||||
|
||||
block_id = pred_block_id;
|
||||
|
||||
if (!ignore_loop_header && block_id)
|
||||
{
|
||||
auto &block = compiler.get<SPIRBlock>(block_id);
|
||||
if (block.merge == SPIRBlock::MergeLoop)
|
||||
return block_id;
|
||||
}
|
||||
}
|
||||
|
||||
return block_id;
|
||||
}
|
||||
|
||||
bool CFG::node_terminates_control_flow_in_sub_graph(BlockID from, BlockID to) const
|
||||
{
|
||||
// Walk backwards, starting from "to" block.
|
||||
// Only follow pred edges if they have a 1:1 relationship, or a merge relationship.
|
||||
// If we cannot find a path to "from", we must assume that to is inside control flow in some way.
|
||||
|
||||
auto &from_block = compiler.get<SPIRBlock>(from);
|
||||
BlockID ignore_block_id = 0;
|
||||
if (from_block.merge == SPIRBlock::MergeLoop)
|
||||
ignore_block_id = from_block.merge_block;
|
||||
|
||||
while (to != from)
|
||||
{
|
||||
auto pred_itr = preceding_edges.find(to);
|
||||
if (pred_itr == end(preceding_edges))
|
||||
return false;
|
||||
|
||||
DominatorBuilder builder(*this);
|
||||
for (auto &edge : pred_itr->second)
|
||||
builder.add_block(edge);
|
||||
|
||||
uint32_t dominator = builder.get_dominator();
|
||||
if (dominator == 0)
|
||||
return false;
|
||||
|
||||
auto &dom = compiler.get<SPIRBlock>(dominator);
|
||||
|
||||
bool true_path_ignore = false;
|
||||
bool false_path_ignore = false;
|
||||
|
||||
bool merges_to_nothing = dom.merge == SPIRBlock::MergeNone ||
|
||||
(dom.merge == SPIRBlock::MergeSelection && dom.next_block &&
|
||||
compiler.get<SPIRBlock>(dom.next_block).terminator == SPIRBlock::Unreachable) ||
|
||||
(dom.merge == SPIRBlock::MergeLoop && dom.merge_block &&
|
||||
compiler.get<SPIRBlock>(dom.merge_block).terminator == SPIRBlock::Unreachable);
|
||||
|
||||
if (dom.self == from || merges_to_nothing)
|
||||
{
|
||||
// We can only ignore inner branchy paths if there is no merge,
|
||||
// i.e. no code is generated afterwards. E.g. this allows us to elide continue:
|
||||
// for (;;) { if (cond) { continue; } else { break; } }.
|
||||
// Codegen here in SPIR-V will be something like either no merge if one path directly breaks, or
|
||||
// we merge to Unreachable.
|
||||
if (ignore_block_id && dom.terminator == SPIRBlock::Select)
|
||||
{
|
||||
auto &true_block = compiler.get<SPIRBlock>(dom.true_block);
|
||||
auto &false_block = compiler.get<SPIRBlock>(dom.false_block);
|
||||
auto &ignore_block = compiler.get<SPIRBlock>(ignore_block_id);
|
||||
true_path_ignore = compiler.execution_is_branchless(true_block, ignore_block);
|
||||
false_path_ignore = compiler.execution_is_branchless(false_block, ignore_block);
|
||||
}
|
||||
}
|
||||
|
||||
// Cases where we allow traversal. This serves as a proxy for post-dominance in a loop body.
|
||||
// TODO: Might want to do full post-dominance analysis, but it's a lot of churn for something like this ...
|
||||
// - We're the merge block of a selection construct. Jump to header.
|
||||
// - We're the merge block of a loop. Jump to header.
|
||||
// - Direct branch. Trivial.
|
||||
// - Allow cases inside a branch if the header cannot merge execution before loop exit.
|
||||
if ((dom.merge == SPIRBlock::MergeSelection && dom.next_block == to) ||
|
||||
(dom.merge == SPIRBlock::MergeLoop && dom.merge_block == to) ||
|
||||
(dom.terminator == SPIRBlock::Direct && dom.next_block == to) ||
|
||||
(dom.terminator == SPIRBlock::Select && dom.true_block == to && false_path_ignore) ||
|
||||
(dom.terminator == SPIRBlock::Select && dom.false_block == to && true_path_ignore))
|
||||
{
|
||||
// Allow walking selection constructs if the other branch reaches out of a loop construct.
|
||||
// It cannot be in-scope anymore.
|
||||
to = dominator;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DominatorBuilder::DominatorBuilder(const CFG &cfg_)
|
||||
: cfg(cfg_)
|
||||
{
|
||||
}
|
||||
|
||||
void DominatorBuilder::add_block(uint32_t block)
|
||||
{
|
||||
if (!cfg.get_immediate_dominator(block))
|
||||
{
|
||||
// Unreachable block via the CFG, we will never emit this code anyways.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dominator)
|
||||
{
|
||||
dominator = block;
|
||||
return;
|
||||
}
|
||||
|
||||
if (block != dominator)
|
||||
dominator = cfg.find_common_dominator(block, dominator);
|
||||
}
|
||||
|
||||
void DominatorBuilder::lift_continue_block_dominator()
|
||||
{
|
||||
// It is possible for a continue block to be the dominator of a variable is only accessed inside the while block of a do-while loop.
|
||||
// We cannot safely declare variables inside a continue block, so move any variable declared
|
||||
// in a continue block to the entry block to simplify.
|
||||
// It makes very little sense for a continue block to ever be a dominator, so fall back to the simplest
|
||||
// solution.
|
||||
|
||||
if (!dominator)
|
||||
return;
|
||||
|
||||
auto &block = cfg.get_compiler().get<SPIRBlock>(dominator);
|
||||
auto post_order = cfg.get_visit_order(dominator);
|
||||
|
||||
// If we are branching to a block with a higher post-order traversal index (continue blocks), we have a problem
|
||||
// since we cannot create sensible GLSL code for this, fallback to entry block.
|
||||
bool back_edge_dominator = false;
|
||||
switch (block.terminator)
|
||||
{
|
||||
case SPIRBlock::Direct:
|
||||
if (cfg.get_visit_order(block.next_block) > post_order)
|
||||
back_edge_dominator = true;
|
||||
break;
|
||||
|
||||
case SPIRBlock::Select:
|
||||
if (cfg.get_visit_order(block.true_block) > post_order)
|
||||
back_edge_dominator = true;
|
||||
if (cfg.get_visit_order(block.false_block) > post_order)
|
||||
back_edge_dominator = true;
|
||||
break;
|
||||
|
||||
case SPIRBlock::MultiSelect:
|
||||
{
|
||||
auto &cases = cfg.get_compiler().get_case_list(block);
|
||||
for (auto &target : cases)
|
||||
{
|
||||
if (cfg.get_visit_order(target.block) > post_order)
|
||||
back_edge_dominator = true;
|
||||
}
|
||||
if (block.default_block && cfg.get_visit_order(block.default_block) > post_order)
|
||||
back_edge_dominator = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (back_edge_dominator)
|
||||
dominator = cfg.get_function().entry_block;
|
||||
}
|
||||
} // namespace SPIRV_CROSS_NAMESPACE
|
168
thirdparty/spirv-cross/spirv_cfg.hpp
vendored
Normal file
168
thirdparty/spirv-cross/spirv_cfg.hpp
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2016-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_CFG_HPP
|
||||
#define SPIRV_CROSS_CFG_HPP
|
||||
|
||||
#include "spirv_common.hpp"
|
||||
#include <assert.h>
|
||||
|
||||
namespace SPIRV_CROSS_NAMESPACE
|
||||
{
|
||||
class Compiler;
|
||||
class CFG
|
||||
{
|
||||
public:
|
||||
CFG(Compiler &compiler, const SPIRFunction &function);
|
||||
|
||||
Compiler &get_compiler()
|
||||
{
|
||||
return compiler;
|
||||
}
|
||||
|
||||
const Compiler &get_compiler() const
|
||||
{
|
||||
return compiler;
|
||||
}
|
||||
|
||||
const SPIRFunction &get_function() const
|
||||
{
|
||||
return func;
|
||||
}
|
||||
|
||||
uint32_t get_immediate_dominator(uint32_t block) const
|
||||
{
|
||||
auto itr = immediate_dominators.find(block);
|
||||
if (itr != std::end(immediate_dominators))
|
||||
return itr->second;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_reachable(uint32_t block) const
|
||||
{
|
||||
return visit_order.count(block) != 0;
|
||||
}
|
||||
|
||||
uint32_t get_visit_order(uint32_t block) const
|
||||
{
|
||||
auto itr = visit_order.find(block);
|
||||
assert(itr != std::end(visit_order));
|
||||
int v = itr->second.get();
|
||||
assert(v > 0);
|
||||
return uint32_t(v);
|
||||
}
|
||||
|
||||
uint32_t find_common_dominator(uint32_t a, uint32_t b) const;
|
||||
|
||||
const SmallVector<uint32_t> &get_preceding_edges(uint32_t block) const
|
||||
{
|
||||
auto itr = preceding_edges.find(block);
|
||||
if (itr != std::end(preceding_edges))
|
||||
return itr->second;
|
||||
else
|
||||
return empty_vector;
|
||||
}
|
||||
|
||||
const SmallVector<uint32_t> &get_succeeding_edges(uint32_t block) const
|
||||
{
|
||||
auto itr = succeeding_edges.find(block);
|
||||
if (itr != std::end(succeeding_edges))
|
||||
return itr->second;
|
||||
else
|
||||
return empty_vector;
|
||||
}
|
||||
|
||||
template <typename Op>
|
||||
void walk_from(std::unordered_set<uint32_t> &seen_blocks, uint32_t block, const Op &op) const
|
||||
{
|
||||
if (seen_blocks.count(block))
|
||||
return;
|
||||
seen_blocks.insert(block);
|
||||
|
||||
if (op(block))
|
||||
{
|
||||
for (auto b : get_succeeding_edges(block))
|
||||
walk_from(seen_blocks, b, op);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t find_loop_dominator(uint32_t block) const;
|
||||
|
||||
bool node_terminates_control_flow_in_sub_graph(BlockID from, BlockID to) const;
|
||||
|
||||
private:
|
||||
struct VisitOrder
|
||||
{
|
||||
int &get()
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
const int &get() const
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
int v = -1;
|
||||
};
|
||||
|
||||
Compiler &compiler;
|
||||
const SPIRFunction &func;
|
||||
std::unordered_map<uint32_t, SmallVector<uint32_t>> preceding_edges;
|
||||
std::unordered_map<uint32_t, SmallVector<uint32_t>> succeeding_edges;
|
||||
std::unordered_map<uint32_t, uint32_t> immediate_dominators;
|
||||
std::unordered_map<uint32_t, VisitOrder> visit_order;
|
||||
SmallVector<uint32_t> post_order;
|
||||
SmallVector<uint32_t> empty_vector;
|
||||
|
||||
void add_branch(uint32_t from, uint32_t to);
|
||||
void build_post_order_visit_order();
|
||||
void build_immediate_dominators();
|
||||
bool post_order_visit(uint32_t block);
|
||||
uint32_t visit_count = 0;
|
||||
|
||||
bool is_back_edge(uint32_t to) const;
|
||||
bool has_visited_forward_edge(uint32_t to) const;
|
||||
};
|
||||
|
||||
class DominatorBuilder
|
||||
{
|
||||
public:
|
||||
DominatorBuilder(const CFG &cfg);
|
||||
|
||||
void add_block(uint32_t block);
|
||||
uint32_t get_dominator() const
|
||||
{
|
||||
return dominator;
|
||||
}
|
||||
|
||||
void lift_continue_block_dominator();
|
||||
|
||||
private:
|
||||
const CFG &cfg;
|
||||
uint32_t dominator = 0;
|
||||
};
|
||||
} // namespace SPIRV_CROSS_NAMESPACE
|
||||
|
||||
#endif
|
1943
thirdparty/spirv-cross/spirv_common.hpp
vendored
Normal file
1943
thirdparty/spirv-cross/spirv_common.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5668
thirdparty/spirv-cross/spirv_cross.cpp
vendored
Normal file
5668
thirdparty/spirv-cross/spirv_cross.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1182
thirdparty/spirv-cross/spirv_cross.hpp
vendored
Normal file
1182
thirdparty/spirv-cross/spirv_cross.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
756
thirdparty/spirv-cross/spirv_cross_containers.hpp
vendored
Normal file
756
thirdparty/spirv-cross/spirv_cross_containers.hpp
vendored
Normal 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
|
99
thirdparty/spirv-cross/spirv_cross_error_handling.hpp
vendored
Normal file
99
thirdparty/spirv-cross/spirv_cross_error_handling.hpp
vendored
Normal 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
|
1083
thirdparty/spirv-cross/spirv_cross_parsed_ir.cpp
vendored
Normal file
1083
thirdparty/spirv-cross/spirv_cross_parsed_ir.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
256
thirdparty/spirv-cross/spirv_cross_parsed_ir.hpp
vendored
Normal file
256
thirdparty/spirv-cross/spirv_cross_parsed_ir.hpp
vendored
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright 2018-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_PARSED_IR_HPP
|
||||
#define SPIRV_CROSS_PARSED_IR_HPP
|
||||
|
||||
#include "spirv_common.hpp"
|
||||
#include <stdint.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace SPIRV_CROSS_NAMESPACE
|
||||
{
|
||||
|
||||
// This data structure holds all information needed to perform cross-compilation and reflection.
|
||||
// It is the output of the Parser, but any implementation could create this structure.
|
||||
// It is intentionally very "open" and struct-like with some helper functions to deal with decorations.
|
||||
// Parser is the reference implementation of how this data structure should be filled in.
|
||||
|
||||
class ParsedIR
|
||||
{
|
||||
private:
|
||||
// This must be destroyed after the "ids" vector.
|
||||
std::unique_ptr<ObjectPoolGroup> pool_group;
|
||||
|
||||
public:
|
||||
ParsedIR();
|
||||
|
||||
// Due to custom allocations from object pools, we cannot use a default copy constructor.
|
||||
ParsedIR(const ParsedIR &other);
|
||||
ParsedIR &operator=(const ParsedIR &other);
|
||||
|
||||
// Moves are unproblematic, but we need to implement it anyways, since MSVC 2013 does not understand
|
||||
// how to default-implement these.
|
||||
ParsedIR(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT;
|
||||
ParsedIR &operator=(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT;
|
||||
|
||||
// Resizes ids, meta and block_meta.
|
||||
void set_id_bounds(uint32_t bounds);
|
||||
|
||||
// The raw SPIR-V, instructions and opcodes refer to this by offset + count.
|
||||
std::vector<uint32_t> spirv;
|
||||
|
||||
// Holds various data structures which inherit from IVariant.
|
||||
SmallVector<Variant> ids;
|
||||
|
||||
// Various meta data for IDs, decorations, names, etc.
|
||||
std::unordered_map<ID, Meta> meta;
|
||||
|
||||
// Holds all IDs which have a certain type.
|
||||
// This is needed so we can iterate through a specific kind of resource quickly,
|
||||
// and in-order of module declaration.
|
||||
SmallVector<ID> ids_for_type[TypeCount];
|
||||
|
||||
// Special purpose lists which contain a union of types.
|
||||
// This is needed so we can declare specialization constants and structs in an interleaved fashion,
|
||||
// among other things.
|
||||
// Constants can be undef or of struct type, and struct array sizes can use specialization constants.
|
||||
SmallVector<ID> ids_for_constant_undef_or_type;
|
||||
SmallVector<ID> ids_for_constant_or_variable;
|
||||
|
||||
// We need to keep track of the width the Ops that contains a type for the
|
||||
// OpSwitch instruction, since this one doesn't contains the type in the
|
||||
// instruction itself. And in some case we need to cast the condition to
|
||||
// wider types. We only need the width to do the branch fixup since the
|
||||
// type check itself can be done at runtime
|
||||
std::unordered_map<ID, uint32_t> load_type_width;
|
||||
|
||||
// Declared capabilities and extensions in the SPIR-V module.
|
||||
// Not really used except for reflection at the moment.
|
||||
SmallVector<spv::Capability> declared_capabilities;
|
||||
SmallVector<std::string> declared_extensions;
|
||||
|
||||
// Meta data about blocks. The cross-compiler needs to query if a block is either of these types.
|
||||
// It is a bitset as there can be more than one tag per block.
|
||||
enum BlockMetaFlagBits
|
||||
{
|
||||
BLOCK_META_LOOP_HEADER_BIT = 1 << 0,
|
||||
BLOCK_META_CONTINUE_BIT = 1 << 1,
|
||||
BLOCK_META_LOOP_MERGE_BIT = 1 << 2,
|
||||
BLOCK_META_SELECTION_MERGE_BIT = 1 << 3,
|
||||
BLOCK_META_MULTISELECT_MERGE_BIT = 1 << 4
|
||||
};
|
||||
using BlockMetaFlags = uint8_t;
|
||||
SmallVector<BlockMetaFlags> block_meta;
|
||||
std::unordered_map<BlockID, BlockID> continue_block_to_loop_header;
|
||||
|
||||
// Normally, we'd stick SPIREntryPoint in ids array, but it conflicts with SPIRFunction.
|
||||
// Entry points can therefore be seen as some sort of meta structure.
|
||||
std::unordered_map<FunctionID, SPIREntryPoint> entry_points;
|
||||
FunctionID default_entry_point = 0;
|
||||
|
||||
struct Source
|
||||
{
|
||||
uint32_t version = 0;
|
||||
bool es = false;
|
||||
bool known = false;
|
||||
bool hlsl = false;
|
||||
|
||||
Source() = default;
|
||||
};
|
||||
|
||||
Source source;
|
||||
|
||||
spv::AddressingModel addressing_model = spv::AddressingModelMax;
|
||||
spv::MemoryModel memory_model = spv::MemoryModelMax;
|
||||
|
||||
// Decoration handling methods.
|
||||
// Can be useful for simple "raw" reflection.
|
||||
// However, most members are here because the Parser needs most of these,
|
||||
// and might as well just have the whole suite of decoration/name handling in one place.
|
||||
void set_name(ID id, const std::string &name);
|
||||
const std::string &get_name(ID id) const;
|
||||
void set_decoration(ID id, spv::Decoration decoration, uint32_t argument = 0);
|
||||
void set_decoration_string(ID id, spv::Decoration decoration, const std::string &argument);
|
||||
bool has_decoration(ID id, spv::Decoration decoration) const;
|
||||
uint32_t get_decoration(ID id, spv::Decoration decoration) const;
|
||||
const std::string &get_decoration_string(ID id, spv::Decoration decoration) const;
|
||||
const Bitset &get_decoration_bitset(ID id) const;
|
||||
void unset_decoration(ID id, spv::Decoration decoration);
|
||||
|
||||
// Decoration handling methods (for members of a struct).
|
||||
void set_member_name(TypeID id, uint32_t index, const std::string &name);
|
||||
const std::string &get_member_name(TypeID id, uint32_t index) const;
|
||||
void set_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration, uint32_t argument = 0);
|
||||
void set_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration,
|
||||
const std::string &argument);
|
||||
uint32_t get_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const;
|
||||
const std::string &get_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration) const;
|
||||
bool has_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const;
|
||||
const Bitset &get_member_decoration_bitset(TypeID id, uint32_t index) const;
|
||||
void unset_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration);
|
||||
|
||||
void mark_used_as_array_length(ID id);
|
||||
uint32_t increase_bound_by(uint32_t count);
|
||||
Bitset get_buffer_block_flags(const SPIRVariable &var) const;
|
||||
Bitset get_buffer_block_type_flags(const SPIRType &type) const;
|
||||
|
||||
void add_typed_id(Types type, ID id);
|
||||
void remove_typed_id(Types type, ID id);
|
||||
|
||||
class LoopLock
|
||||
{
|
||||
public:
|
||||
explicit LoopLock(uint32_t *counter);
|
||||
LoopLock(const LoopLock &) = delete;
|
||||
void operator=(const LoopLock &) = delete;
|
||||
LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT;
|
||||
LoopLock &operator=(LoopLock &&other) SPIRV_CROSS_NOEXCEPT;
|
||||
~LoopLock();
|
||||
|
||||
private:
|
||||
uint32_t *lock = 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
|
77
thirdparty/spirv-cross/spirv_cross_util.cpp
vendored
Normal file
77
thirdparty/spirv-cross/spirv_cross_util.cpp
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2015-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#include "spirv_cross_util.hpp"
|
||||
#include "spirv_common.hpp"
|
||||
|
||||
using namespace spv;
|
||||
using namespace SPIRV_CROSS_NAMESPACE;
|
||||
|
||||
namespace spirv_cross_util
|
||||
{
|
||||
void rename_interface_variable(Compiler &compiler, const SmallVector<Resource> &resources, uint32_t location,
|
||||
const std::string &name)
|
||||
{
|
||||
for (auto &v : resources)
|
||||
{
|
||||
if (!compiler.has_decoration(v.id, spv::DecorationLocation))
|
||||
continue;
|
||||
|
||||
auto loc = compiler.get_decoration(v.id, spv::DecorationLocation);
|
||||
if (loc != location)
|
||||
continue;
|
||||
|
||||
auto &type = compiler.get_type(v.base_type_id);
|
||||
|
||||
// This is more of a friendly variant. If we need to rename interface variables, we might have to rename
|
||||
// structs as well and make sure all the names match up.
|
||||
if (type.basetype == SPIRType::Struct)
|
||||
{
|
||||
compiler.set_name(v.base_type_id, join("SPIRV_Cross_Interface_Location", location));
|
||||
for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
|
||||
compiler.set_member_name(v.base_type_id, i, join("InterfaceMember", i));
|
||||
}
|
||||
|
||||
compiler.set_name(v.id, name);
|
||||
}
|
||||
}
|
||||
|
||||
void inherit_combined_sampler_bindings(Compiler &compiler)
|
||||
{
|
||||
auto &samplers = compiler.get_combined_image_samplers();
|
||||
for (auto &s : samplers)
|
||||
{
|
||||
if (compiler.has_decoration(s.image_id, spv::DecorationDescriptorSet))
|
||||
{
|
||||
uint32_t set = compiler.get_decoration(s.image_id, spv::DecorationDescriptorSet);
|
||||
compiler.set_decoration(s.combined_id, spv::DecorationDescriptorSet, set);
|
||||
}
|
||||
|
||||
if (compiler.has_decoration(s.image_id, spv::DecorationBinding))
|
||||
{
|
||||
uint32_t binding = compiler.get_decoration(s.image_id, spv::DecorationBinding);
|
||||
compiler.set_decoration(s.combined_id, spv::DecorationBinding, binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace spirv_cross_util
|
37
thirdparty/spirv-cross/spirv_cross_util.hpp
vendored
Normal file
37
thirdparty/spirv-cross/spirv_cross_util.hpp
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2015-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_UTIL_HPP
|
||||
#define SPIRV_CROSS_UTIL_HPP
|
||||
|
||||
#include "spirv_cross.hpp"
|
||||
|
||||
namespace spirv_cross_util
|
||||
{
|
||||
void rename_interface_variable(SPIRV_CROSS_NAMESPACE::Compiler &compiler,
|
||||
const SPIRV_CROSS_NAMESPACE::SmallVector<SPIRV_CROSS_NAMESPACE::Resource> &resources,
|
||||
uint32_t location, const std::string &name);
|
||||
void inherit_combined_sampler_bindings(SPIRV_CROSS_NAMESPACE::Compiler &compiler);
|
||||
} // namespace spirv_cross_util
|
||||
|
||||
#endif
|
19109
thirdparty/spirv-cross/spirv_glsl.cpp
vendored
Normal file
19109
thirdparty/spirv-cross/spirv_glsl.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1074
thirdparty/spirv-cross/spirv_glsl.hpp
vendored
Normal file
1074
thirdparty/spirv-cross/spirv_glsl.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
18810
thirdparty/spirv-cross/spirv_msl.cpp
vendored
Normal file
18810
thirdparty/spirv-cross/spirv_msl.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1349
thirdparty/spirv-cross/spirv_msl.hpp
vendored
Normal file
1349
thirdparty/spirv-cross/spirv_msl.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1337
thirdparty/spirv-cross/spirv_parser.cpp
vendored
Normal file
1337
thirdparty/spirv-cross/spirv_parser.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
103
thirdparty/spirv-cross/spirv_parser.hpp
vendored
Normal file
103
thirdparty/spirv-cross/spirv_parser.hpp
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2018-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_PARSER_HPP
|
||||
#define SPIRV_CROSS_PARSER_HPP
|
||||
|
||||
#include "spirv_cross_parsed_ir.hpp"
|
||||
#include <stdint.h>
|
||||
|
||||
namespace SPIRV_CROSS_NAMESPACE
|
||||
{
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
Parser(const uint32_t *spirv_data, size_t word_count);
|
||||
Parser(std::vector<uint32_t> spirv);
|
||||
|
||||
void parse();
|
||||
|
||||
ParsedIR &get_parsed_ir()
|
||||
{
|
||||
return ir;
|
||||
}
|
||||
|
||||
private:
|
||||
ParsedIR ir;
|
||||
SPIRFunction *current_function = nullptr;
|
||||
SPIRBlock *current_block = nullptr;
|
||||
// For workarounds.
|
||||
bool ignore_trailing_block_opcodes = false;
|
||||
|
||||
void parse(const Instruction &instr);
|
||||
const uint32_t *stream(const Instruction &instr) const;
|
||||
|
||||
template <typename T, typename... P>
|
||||
T &set(uint32_t id, P &&... args)
|
||||
{
|
||||
ir.add_typed_id(static_cast<Types>(T::type), id);
|
||||
auto &var = variant_set<T>(ir.ids[id], std::forward<P>(args)...);
|
||||
var.self = id;
|
||||
return var;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T &get(uint32_t id)
|
||||
{
|
||||
return variant_get<T>(ir.ids[id]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *maybe_get(uint32_t id)
|
||||
{
|
||||
if (ir.ids[id].get_type() == static_cast<Types>(T::type))
|
||||
return &get<T>(id);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T &get(uint32_t id) const
|
||||
{
|
||||
return variant_get<T>(ir.ids[id]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T *maybe_get(uint32_t id) const
|
||||
{
|
||||
if (ir.ids[id].get_type() == T::type)
|
||||
return &get<T>(id);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// This must be an ordered data structure so we always pick the same type aliases.
|
||||
SmallVector<uint32_t> global_struct_cache;
|
||||
SmallVector<std::pair<uint32_t, uint32_t>> forward_pointer_fixups;
|
||||
|
||||
bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const;
|
||||
bool variable_storage_is_aliased(const SPIRVariable &v) const;
|
||||
};
|
||||
} // namespace SPIRV_CROSS_NAMESPACE
|
||||
|
||||
#endif
|
710
thirdparty/spirv-cross/spirv_reflect.cpp
vendored
Normal file
710
thirdparty/spirv-cross/spirv_reflect.cpp
vendored
Normal file
@ -0,0 +1,710 @@
|
||||
/*
|
||||
* 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));
|
||||
|
||||
// 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);
|
||||
}
|
91
thirdparty/spirv-cross/spirv_reflect.hpp
vendored
Normal file
91
thirdparty/spirv-cross/spirv_reflect.hpp
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2018-2021 Bradley Austin Davis
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_REFLECT_HPP
|
||||
#define SPIRV_CROSS_REFLECT_HPP
|
||||
|
||||
#include "spirv_glsl.hpp"
|
||||
#include <utility>
|
||||
|
||||
namespace simple_json
|
||||
{
|
||||
class Stream;
|
||||
}
|
||||
|
||||
namespace SPIRV_CROSS_NAMESPACE
|
||||
{
|
||||
class CompilerReflection : public CompilerGLSL
|
||||
{
|
||||
using Parent = CompilerGLSL;
|
||||
|
||||
public:
|
||||
explicit CompilerReflection(std::vector<uint32_t> spirv_)
|
||||
: Parent(std::move(spirv_))
|
||||
{
|
||||
options.vulkan_semantics = true;
|
||||
}
|
||||
|
||||
CompilerReflection(const uint32_t *ir_, size_t word_count)
|
||||
: Parent(ir_, word_count)
|
||||
{
|
||||
options.vulkan_semantics = true;
|
||||
}
|
||||
|
||||
explicit CompilerReflection(const ParsedIR &ir_)
|
||||
: CompilerGLSL(ir_)
|
||||
{
|
||||
options.vulkan_semantics = true;
|
||||
}
|
||||
|
||||
explicit CompilerReflection(ParsedIR &&ir_)
|
||||
: CompilerGLSL(std::move(ir_))
|
||||
{
|
||||
options.vulkan_semantics = true;
|
||||
}
|
||||
|
||||
void set_format(const std::string &format);
|
||||
std::string compile() override;
|
||||
|
||||
private:
|
||||
static std::string execution_model_to_str(spv::ExecutionModel model);
|
||||
|
||||
void emit_entry_points();
|
||||
void emit_types();
|
||||
void emit_resources();
|
||||
void emit_specialization_constants();
|
||||
|
||||
void emit_type(uint32_t type_id, bool &emitted_open_tag);
|
||||
void emit_type_member(const SPIRType &type, uint32_t index);
|
||||
void emit_type_member_qualifiers(const SPIRType &type, uint32_t index);
|
||||
void emit_type_array(const SPIRType &type);
|
||||
void emit_resources(const char *tag, const SmallVector<Resource> &resources);
|
||||
bool type_is_reference(const SPIRType &type) const;
|
||||
|
||||
std::string to_member_name(const SPIRType &type, uint32_t index) const;
|
||||
|
||||
std::shared_ptr<simple_json::Stream> json_stream;
|
||||
};
|
||||
|
||||
} // namespace SPIRV_CROSS_NAMESPACE
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user