From d436e7ab50f27c08a0c8887bc77480d325bb1a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 7 Feb 2021 13:33:07 +0100 Subject: [PATCH 01/44] Vk: expose EXT_robustness2 and EXT_image_robustness2 extensions. --- doc/vulkan-mapping.dox | 3 + doc/vulkan-support.dox | 2 + src/Magnum/Vk/Device.cpp | 6 ++ src/Magnum/Vk/DeviceCreateInfo.h | 4 ++ src/Magnum/Vk/DeviceFeatures.h | 57 ++++++++++++++++++- src/Magnum/Vk/DeviceProperties.cpp | 4 ++ src/Magnum/Vk/DeviceProperties.h | 6 ++ src/Magnum/Vk/Extensions.cpp | 2 + src/Magnum/Vk/Extensions.h | 2 + src/Magnum/Vk/Implementation/DeviceFeatures.h | 2 + .../Implementation/deviceFeatureMapping.hpp | 8 +++ src/MagnumExternal/Vulkan/extensions.txt | 2 + src/MagnumExternal/Vulkan/flextVk.h | 36 +++++++++++- 13 files changed, 131 insertions(+), 3 deletions(-) diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 2483db162..d200e5177 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -614,6 +614,7 @@ Vulkan structure | Matching API @type_vk{PhysicalDeviceIDProperties} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @type_vk{PhysicalDeviceImageFormatInfo2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @type_vk{PhysicalDeviceImagelessFramebufferFeatures} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref DeviceFeatures +@type_vk{PhysicalDeviceImageRobustnessFeaturesEXT} @m_class{m-label m-flat m-warning} **EXT** | @ref DeviceFeatures @type_vk{PhysicalDeviceIndexTypeUint8FeaturesEXT} @m_class{m-label m-flat m-warning} **EXT** | @ref DeviceFeatures @type_vk{PhysicalDeviceTextureCompressionASTCHDRFeaturesEXT} @m_class{m-label m-flat m-warning} **EXT** | @ref DeviceFeatures @type_vk{PhysicalDeviceLimits} | | @@ -630,6 +631,8 @@ Vulkan structure | Matching API @type_vk{PhysicalDeviceRayQueryFeaturesKHR} @m_class{m-label m-flat m-warning} **KHR** | @ref DeviceFeatures @type_vk{PhysicalDeviceRayTracingPipelineFeaturesKHR} @m_class{m-label m-flat m-warning} **KHR** | @ref DeviceFeatures @type_vk{PhysicalDeviceRayTracingPipelinePropertiesKHR} @m_class{m-label m-flat m-warning} **KHR** | | +@type_vk{PhysicalDeviceRobustness2FeaturesEXT} @m_class{m-label m-flat m-warning} **EXT** | @ref DeviceFeatures +@type_vk{PhysicalDeviceRobustness2PropertiesEXT} @m_class{m-label m-flat m-warning} **EXT** | | @type_vk{PhysicalDeviceSamplerFilterMinmaxProperties} @m_class{m-label m-flat m-success} **EXT, 1.2** | | @type_vk{PhysicalDeviceSamplerYcbcrConversionFeatures} @m_class{m-label m-flat m-success} **KHR, 1.1** | @ref DeviceFeatures @type_vk{PhysicalDeviceSeparateDepthStencilLayoutsFeatures} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref DeviceFeatures diff --git a/doc/vulkan-support.dox b/doc/vulkan-support.dox index 7215f8a66..0f2defdf7 100644 --- a/doc/vulkan-support.dox +++ b/doc/vulkan-support.dox @@ -123,6 +123,8 @@ Extension | Status @vk_extension{EXT,vertex_attribute_divisor} | done @vk_extension{EXT,index_type_uint8} | @ref Vk::vkIndexType() only @vk_extension{EXT,extended_dynamic_state} | | +@vk_extension{EXT,robustness2} | done except properties +@vk_extension{EXT,image_robustness} | done @vk_extension{KHR,acceleration_structure} | | @vk_extension{KHR,portability_subset} | done except properties @vk_extension{KHR,deferred_host_operations} | | diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index 80c4072ba..ec74756e1 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -412,6 +412,8 @@ DeviceCreateInfo& DeviceCreateInfo::setEnabledFeatures(const DeviceFeatures& fea _state->features.hostQueryReset, _state->features.indexTypeUint8, _state->features.extendedDynamicState, + _state->features.robustness2, + _state->features.imageRobustness, _state->features.rayTracingPipeline, _state->features.rayQuery }); @@ -533,6 +535,10 @@ DeviceCreateInfo& DeviceCreateInfo::setEnabledFeatures(const DeviceFeatures& fea _state->features.indexTypeUint8, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT); structureConnectIfUsed(next, _state->firstEnabledFeature, _state->features.extendedDynamicState, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT); + structureConnectIfUsed(next, _state->firstEnabledFeature, + _state->features.robustness2, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT); + structureConnectIfUsed(next, _state->firstEnabledFeature, + _state->features.imageRobustness, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES_EXT); structureConnectIfUsed(next, _state->firstEnabledFeature, _state->features.rayTracingPipeline, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR); structureConnectIfUsed(next, _state->firstEnabledFeature, diff --git a/src/Magnum/Vk/DeviceCreateInfo.h b/src/Magnum/Vk/DeviceCreateInfo.h index 44fc0c45c..088a5ad14 100644 --- a/src/Magnum/Vk/DeviceCreateInfo.h +++ b/src/Magnum/Vk/DeviceCreateInfo.h @@ -268,6 +268,10 @@ class MAGNUM_VK_EXPORT DeviceCreateInfo { * (@vk_extension{EXT,index_type_uint8}) * - @type_vk_keyword{PhysicalDeviceExtendedDynamicStateFeaturesEXT} * (@vk_extension{EXT,extended_dynamic_state}) + * - @type_vk_keyword{PhysicalDeviceRobustness2FeaturesEXT} + * (@vk_extension{EXT,robustness2}) + * - @type_vk_keyword{PhysicalDeviceImageRobustnessFeaturesEXT} + * (@vk_extension{EXT,image_robustness}) * - @type_vk_keyword{PhysicalDeviceRayTracingPipelineFeaturesKHR} * (@vk_extension{KHR,ray_tracing_pipeline}) * - @type_vk_keyword{PhysicalDeviceRayQueryFeaturesKHR} diff --git a/src/Magnum/Vk/DeviceFeatures.h b/src/Magnum/Vk/DeviceFeatures.h index dcf958b10..bac1f076f 100644 --- a/src/Magnum/Vk/DeviceFeatures.h +++ b/src/Magnum/Vk/DeviceFeatures.h @@ -59,8 +59,12 @@ usually just with the first letter uppercase instead of lowercase. enum class DeviceFeature: UnsignedShort { /** * Whether accesses to buffers are bounds-checked against the range of the - * buffer descriptor. - * @todo expose @vk_extension{EXT,robustness2}, reference from here + * buffer descriptor. Out of bounds accesses are guaranteed to not cause + * application termination, but have implementation-dependent behavior. A + * subset of the guarantees provided by + * @ref DeviceFeature::RobustBufferAccess2. + * @see @ref DeviceFeature::RobustImageAccess, + * @relativeref{DeviceFeature,RobustImageAccess2} */ RobustBufferAccess, @@ -1292,6 +1296,55 @@ enum class DeviceFeature: UnsignedShort { */ ExtendedDynamicState, + /* VkPhysicalDeviceRobustness2FeaturesEXT, #287 */ + + /** + * Whether accesses to buffers are bounds-checked against the range of the + * buffer descriptor, rounded up to a multiple of buffer access size + * alignment. Out of bounds writes are guaranteed to be discarded, out of + * bounds reads are guaranteed to return zero values. A stricter but more + * expensive variant of @ref DeviceFeature::RobustBufferAccess. + * @requires_vk_extension Extension @vk_extension{EXT,robustness2} + * @todo expose the alignment limit + */ + RobustBufferAccess2, + + /** + * Whether accesses to images are bounds-checked against the dimensions of + * the image view. Out of bounds loads are guaranteed to return zero + * values, with @f$ (0, 0, 0, 1) @f$ inserted for missing components based + * on the image format. A stricted but more expensive variant of + * @ref DeviceFeature::RobustImageAccess. + * @see @ref DeviceFeature::RobustBufferAccess, + * @relativeref{DeviceFeature,RobustBufferAccess2} + * @requires_vk_extension Extension @vk_extension{EXT,robustness2} + */ + RobustImageAccess2, + + /** + * Whether descriptors can be written with a null resource or view handle, + * at which point they're considered valid and act as if the descriptor was + * bound to nothing. + * @requires_vk_extension Extension @vk_extension{EXT,robustness2} + */ + NullDescriptor, + + /* VkPhysicalDeviceImageRobustnessFeaturesEXT, #336 */ + + /** + * Whether accesses to images are bounds-checked against the dimensions of + * the image view. Out of bounds loads are guaranteed to return zero + * values, with either @f$ (0, 0, 0, 0) @f$ or @f$ (0, 0, 0, 1) @f$ + * inserted for missing components based on the image format, with no + * guarantees provided for values returned for an invalid mip level. A + * subset of the guarantees provided by + * @ref DeviceFeature::RobustImageAccess2. + * @see @ref DeviceFeature::RobustBufferAccess, + * @relativeref{DeviceFeature,RobustBufferAccess2} + * @requires_vk_extension Extension @vk_extension{EXT,image_robustness} + */ + RobustImageAccess, + /* VkPhysicalDeviceRayTracingPipelineFeaturesKHR, #348 */ /** diff --git a/src/Magnum/Vk/DeviceProperties.cpp b/src/Magnum/Vk/DeviceProperties.cpp index b9263f136..739c5a8f7 100644 --- a/src/Magnum/Vk/DeviceProperties.cpp +++ b/src/Magnum/Vk/DeviceProperties.cpp @@ -339,6 +339,10 @@ const DeviceFeatures& DeviceProperties::features() { Implementation::structureConnect(next, features.indexTypeUint8, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT); if(isOrVersionSupportedInternal()) Implementation::structureConnect(next, features.extendedDynamicState, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT); + if(isOrVersionSupportedInternal()) + Implementation::structureConnect(next, features.robustness2, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT); + if(isOrVersionSupportedInternal()) + Implementation::structureConnect(next, features.imageRobustness, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES_EXT); if(isOrVersionSupportedInternal()) Implementation::structureConnect(next, features.rayTracingPipeline, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR); if(isOrVersionSupportedInternal()) diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index 3c9f4e1dd..6fe6a3584 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -492,6 +492,12 @@ class MAGNUM_VK_EXPORT DeviceProperties { * - If the @vk_extension{EXT,extended_dynamic_state} extension is * supported by the device, the `pNext` chain contains * @type_vk_keyword{PhysicalDeviceExtendedDynamicStateFeaturesEXT} + * - If the @vk_extension{EXT,robustness2} extension is supported by + * the device, the `pNext` chain contains + * @type_vk_keyword{PhysicalDeviceRobustness2FeaturesEXT} + * - If the @vk_extension{EXT,image_robustness} extension is + * supported by the device, the `pNext` chain contains + * @type_vk_keyword{PhysicalDeviceImageRobustnessFeaturesEXT} * - If the @vk_extension{KHR,ray_tracing_pipeline} extension is * supported by the device, the `pNext` chain contains * @type_vk_keyword{PhysicalDeviceRayTracingPipelineFeaturesKHR} diff --git a/src/Magnum/Vk/Extensions.cpp b/src/Magnum/Vk/Extensions.cpp index bc8e10523..2cb312300 100644 --- a/src/Magnum/Vk/Extensions.cpp +++ b/src/Magnum/Vk/Extensions.cpp @@ -69,7 +69,9 @@ namespace { constexpr Extension DeviceExtensions[] { Extensions::EXT::debug_marker{}, Extensions::EXT::extended_dynamic_state{}, + Extensions::EXT::image_robustness{}, Extensions::EXT::index_type_uint8{}, + Extensions::EXT::robustness2{}, Extensions::EXT::texture_compression_astc_hdr{}, Extensions::EXT::vertex_attribute_divisor{}, Extensions::IMG::format_pvrtc{}, diff --git a/src/Magnum/Vk/Extensions.h b/src/Magnum/Vk/Extensions.h index 6de3f552a..41c7b6cef 100644 --- a/src/Magnum/Vk/Extensions.h +++ b/src/Magnum/Vk/Extensions.h @@ -124,6 +124,8 @@ namespace EXT { _extension(8, EXT,host_query_reset, Vk10, Vk12) // #262 _extension(9, EXT,index_type_uint8, Vk10, None) // #266 _extension(10, EXT,extended_dynamic_state, Vk10, None) // #268 + _extension(11, EXT,robustness2, Vk10, None) // #287 + _extension(12, EXT,image_robustness, Vk10, None) // #336 } namespace IMG { _extension(20, IMG,format_pvrtc, Vk10, None) // #55 } namespace KHR { diff --git a/src/Magnum/Vk/Implementation/DeviceFeatures.h b/src/Magnum/Vk/Implementation/DeviceFeatures.h index 4d567c67c..15ad590bd 100644 --- a/src/Magnum/Vk/Implementation/DeviceFeatures.h +++ b/src/Magnum/Vk/Implementation/DeviceFeatures.h @@ -61,6 +61,8 @@ struct DeviceFeatures { VkPhysicalDeviceHostQueryResetFeatures hostQueryReset; VkPhysicalDeviceIndexTypeUint8FeaturesEXT indexTypeUint8; VkPhysicalDeviceExtendedDynamicStateFeaturesEXT extendedDynamicState; + VkPhysicalDeviceRobustness2FeaturesEXT robustness2; + VkPhysicalDeviceImageRobustnessFeaturesEXT imageRobustness; VkPhysicalDeviceRayTracingPipelineFeaturesKHR rayTracingPipeline; VkPhysicalDeviceRayQueryFeaturesKHR rayQuery; }; diff --git a/src/Magnum/Vk/Implementation/deviceFeatureMapping.hpp b/src/Magnum/Vk/Implementation/deviceFeatureMapping.hpp index 8c218c4f8..82ecc2ed7 100644 --- a/src/Magnum/Vk/Implementation/deviceFeatureMapping.hpp +++ b/src/Magnum/Vk/Implementation/deviceFeatureMapping.hpp @@ -217,6 +217,14 @@ _cext(IndexTypeUint8, indexTypeUint8, indexTypeUint8, EXT::index_type_uint8) _cext(ExtendedDynamicState, extendedDynamicState, extendedDynamicState, EXT::extended_dynamic_state) +#define _ce(value, field) _cext(value, field, robustness2, EXT::robustness2) +_ce(RobustBufferAccess2, robustBufferAccess2) +_ce(RobustImageAccess2, robustImageAccess2) +_ce(NullDescriptor, nullDescriptor) +#undef _ce + +_cext(RobustImageAccess, robustImageAccess, imageRobustness, EXT::image_robustness) + #define _ce(value, field) _cext(value, field, rayTracingPipeline, KHR::ray_tracing_pipeline) _ce(RayTracingPipeline, rayTracingPipeline) _ce(RayTracingPipelineShaderGroupHandleCaptureReplay, rayTracingPipelineShaderGroupHandleCaptureReplay) diff --git a/src/MagnumExternal/Vulkan/extensions.txt b/src/MagnumExternal/Vulkan/extensions.txt index f70bd97c2..aa9e6b9e8 100644 --- a/src/MagnumExternal/Vulkan/extensions.txt +++ b/src/MagnumExternal/Vulkan/extensions.txt @@ -64,6 +64,8 @@ extension EXT_texture_compression_astc_hdr optional extension EXT_vertex_attribute_divisor optional extension EXT_index_type_uint8 optional extension EXT_extended_dynamic_state optional +extension EXT_robustness2 optional +extension EXT_image_robustness optional extension IMG_format_pvrtc optional extension KHR_acceleration_structure optional extension KHR_portability_subset optional diff --git a/src/MagnumExternal/Vulkan/flextVk.h b/src/MagnumExternal/Vulkan/flextVk.h index d20946024..d0cc4c335 100644 --- a/src/MagnumExternal/Vulkan/flextVk.h +++ b/src/MagnumExternal/Vulkan/flextVk.h @@ -259,6 +259,16 @@ extern "C" { #define VK_EXT_EXTENDED_DYNAMIC_STATE_SPEC_VERSION 1 #define VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME "VK_EXT_extended_dynamic_state" +/* VK_EXT_robustness2 */ + +#define VK_EXT_ROBUSTNESS_2_SPEC_VERSION 1 +#define VK_EXT_ROBUSTNESS_2_EXTENSION_NAME "VK_EXT_robustness2" + +/* VK_EXT_image_robustness */ + +#define VK_EXT_IMAGE_ROBUSTNESS_SPEC_VERSION 1 +#define VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME "VK_EXT_image_robustness" + /* VK_IMG_format_pvrtc */ #define VK_IMG_FORMAT_PVRTC_SPEC_VERSION 1 @@ -329,7 +339,7 @@ extern "C" { // Vulkan 1.2 version number #define VK_API_VERSION_1_2 VK_MAKE_VERSION(1, 2, 0)// Patch version should always be set to 0 // Version of this file -#define VK_HEADER_VERSION 168 +#define VK_HEADER_VERSION 169 // Complete version of this file #define VK_HEADER_VERSION_COMPLETE VK_MAKE_VERSION(1, 2, VK_HEADER_VERSION) #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; @@ -1563,6 +1573,9 @@ typedef enum { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT = 1000190002, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT = 1000265000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT = 1000267000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT = 1000286000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT = 1000286001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES_EXT = 1000335000, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES, @@ -4768,6 +4781,27 @@ typedef struct VkPhysicalDeviceExtendedDynamicStateFeaturesEXT { VkBool32 extendedDynamicState; } VkPhysicalDeviceExtendedDynamicStateFeaturesEXT; +typedef struct VkPhysicalDeviceRobustness2FeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 robustBufferAccess2; + VkBool32 robustImageAccess2; + VkBool32 nullDescriptor; +} VkPhysicalDeviceRobustness2FeaturesEXT; + +typedef struct VkPhysicalDeviceRobustness2PropertiesEXT { + VkStructureType sType; + void* pNext; + VkDeviceSize robustStorageBufferAccessSizeAlignment; + VkDeviceSize robustUniformBufferAccessSizeAlignment; +} VkPhysicalDeviceRobustness2PropertiesEXT; + +typedef struct VkPhysicalDeviceImageRobustnessFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 robustImageAccess; +} VkPhysicalDeviceImageRobustnessFeaturesEXT; + typedef struct VkPhysicalDevicePortabilitySubsetFeaturesKHR { VkStructureType sType; void* pNext; From f9d8032fc9aaf82cad56958d5f5a27e8abd054c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 7 Feb 2021 21:11:42 +0100 Subject: [PATCH 02/44] Revert "Vk: use a compute pipeline for Pipeline move/wrap tests." Err, I'll actually need those to test the dynamic state. Again pushed too soon. This reverts commit 321bb0d9c12408101a4ead69075fbab05f104768. --- src/Magnum/Vk/Test/PipelineVkTest.cpp | 74 ++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/src/Magnum/Vk/Test/PipelineVkTest.cpp b/src/Magnum/Vk/Test/PipelineVkTest.cpp index d365cf630..98c3cf403 100644 --- a/src/Magnum/Vk/Test/PipelineVkTest.cpp +++ b/src/Magnum/Vk/Test/PipelineVkTest.cpp @@ -276,25 +276,49 @@ void PipelineVkTest::constructCompute() { } void PipelineVkTest::constructMove() { + RenderPass renderPass{device(), RenderPassCreateInfo{} + .setAttachments({ + AttachmentDescription{PixelFormat::RGBA8Unorm, + AttachmentLoadOperation::Clear, + AttachmentStoreOperation::Store, + ImageLayout::Undefined, + ImageLayout::ColorAttachment} + }) + .addSubpass(SubpassDescription{}.setColorAttachments({ + AttachmentReference{0, ImageLayout::ColorAttachment} + })) + }; + + /* Not sure if this is really needed, but the shader needs those inputs so + playing it safe */ + MeshLayout meshLayout{MeshPrimitive::Triangles}; + meshLayout + .addBinding(0, 2*4*4) + .addAttribute(0, 0, Vk::VertexFormat::Vector4, 0) + .addAttribute(1, 0, Vk::VertexFormat::Vector4, 4*4); + PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; Shader shader{device(), ShaderCreateInfo{ - Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "compute-noop.spv")) + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "triangle-shaders.spv")) }}; ShaderSet shaderSet; - shaderSet.addShader(ShaderStage::Compute, shader, "main"_s); + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); - Pipeline a{device(), ComputePipelineCreateInfo{ - shaderSet, pipelineLayout - }}; + Pipeline a{device(), RasterizationPipelineCreateInfo{ + shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1} + .setViewport({{}, {200, 200}}) + }; VkPipeline handle = a.handle(); Pipeline b = std::move(a); CORRADE_VERIFY(!a.handle()); CORRADE_COMPARE(b.handle(), handle); CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); - CORRADE_COMPARE(b.bindPoint(), PipelineBindPoint::Compute); + CORRADE_COMPARE(b.bindPoint(), PipelineBindPoint::Rasterization); Pipeline c{NoCreate}; c = std::move(b); @@ -302,30 +326,54 @@ void PipelineVkTest::constructMove() { CORRADE_COMPARE(b.handleFlags(), HandleFlags{}); CORRADE_COMPARE(c.handle(), handle); CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); - CORRADE_COMPARE(c.bindPoint(), PipelineBindPoint::Compute); + CORRADE_COMPARE(c.bindPoint(), PipelineBindPoint::Rasterization); CORRADE_VERIFY(std::is_nothrow_move_constructible::value); CORRADE_VERIFY(std::is_nothrow_move_assignable::value); } void PipelineVkTest::wrap() { + RenderPass renderPass{device(), RenderPassCreateInfo{} + .setAttachments({ + AttachmentDescription{PixelFormat::RGBA8Unorm, + AttachmentLoadOperation::Clear, + AttachmentStoreOperation::Store, + ImageLayout::Undefined, + ImageLayout::ColorAttachment} + }) + .addSubpass(SubpassDescription{}.setColorAttachments({ + AttachmentReference{0, ImageLayout::ColorAttachment} + })) + }; + + /* Not sure if this is really needed, but the shader needs those inputs so + playing it safe */ + MeshLayout meshLayout{MeshPrimitive::Triangles}; + meshLayout + .addBinding(0, 2*4*4) + .addAttribute(0, 0, Vk::VertexFormat::Vector4, 0) + .addAttribute(1, 0, Vk::VertexFormat::Vector4, 4*4); + PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; Shader shader{device(), ShaderCreateInfo{ - Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "compute-noop.spv")) + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "triangle-shaders.spv")) }}; ShaderSet shaderSet; - shaderSet.addShader(ShaderStage::Compute, shader, "main"_s); + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); VkPipeline pipeline{}; - CORRADE_COMPARE(Result(device()->CreateComputePipelines(device(), {}, 1, - ComputePipelineCreateInfo{shaderSet, pipelineLayout}, + CORRADE_COMPARE(Result(device()->CreateGraphicsPipelines(device(), {}, 1, + RasterizationPipelineCreateInfo{shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1} + .setViewport({{}, {200, 200}}), nullptr, &pipeline)), Result::Success); - auto wrapped = Pipeline::wrap(device(), PipelineBindPoint::Compute, pipeline, HandleFlag::DestroyOnDestruction); + auto wrapped = Pipeline::wrap(device(), PipelineBindPoint::Rasterization, pipeline, HandleFlag::DestroyOnDestruction); CORRADE_COMPARE(wrapped.handle(), pipeline); - CORRADE_COMPARE(wrapped.bindPoint(), PipelineBindPoint::Compute); + CORRADE_COMPARE(wrapped.bindPoint(), PipelineBindPoint::Rasterization); /* Release the handle again, destroy by hand */ CORRADE_COMPARE(wrapped.release(), pipeline); From ac62a6c7085070774a430742373b562af231522e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 7 Feb 2021 21:44:22 +0100 Subject: [PATCH 03/44] Vk: debug output for DynamicRasterizationState[s]. Will save them to a Pipeline and when there's a getter for something, it's good to have a printer for it as well. Or at least for the tests. --- src/Magnum/Vk/Pipeline.cpp | 25 +++++++++++++++++++ .../Vk/RasterizationPipelineCreateInfo.h | 12 +++++++++ src/Magnum/Vk/Test/PipelineTest.cpp | 18 ++++++++++++- 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Vk/Pipeline.cpp b/src/Magnum/Vk/Pipeline.cpp index 05f2d80ba..ceef6f531 100644 --- a/src/Magnum/Vk/Pipeline.cpp +++ b/src/Magnum/Vk/Pipeline.cpp @@ -29,6 +29,7 @@ #include "CommandBuffer.h" #include +#include #include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Device.h" @@ -451,4 +452,28 @@ Debug& operator<<(Debug& debug, const PipelineBindPoint value) { return debug << "(" << Debug::nospace << Int(value) << Debug::nospace << ")"; } +namespace { + +constexpr const char* DynamicRasterizationStateNames[]{ + #define _c(value, vkValue) #value, + #include "Magnum/Vk/Implementation/dynamicRasterizationStateMapping.hpp" + #undef _c +}; + +} + +Debug& operator<<(Debug& debug, const DynamicRasterizationState value) { + debug << "Vk::DynamicRasterizationState" << Debug::nospace; + + if(UnsignedInt(value) < Containers::arraySize(DynamicRasterizationStateNames)) { + return debug << "::" << Debug::nospace << DynamicRasterizationStateNames[UnsignedInt(value)]; + } + + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; +} + +Debug& operator<<(Debug& debug, const DynamicRasterizationStates& value) { + return Containers::bigEnumSetDebugOutput(debug, value, "Vk::DynamicRasterizationStates{}"); +} + }} diff --git a/src/Magnum/Vk/RasterizationPipelineCreateInfo.h b/src/Magnum/Vk/RasterizationPipelineCreateInfo.h index b647f5255..e579212bc 100644 --- a/src/Magnum/Vk/RasterizationPipelineCreateInfo.h +++ b/src/Magnum/Vk/RasterizationPipelineCreateInfo.h @@ -273,6 +273,18 @@ typedef Containers::BigEnumSet DynamicRasterizatio CORRADE_ENUMSET_OPERATORS(DynamicRasterizationStates) +/** +@debugoperatorenum{DynamicRasterizationState} +@m_since_latest +*/ +MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, DynamicRasterizationState value); + +/** +@debugoperatorenum{DynamicRasterizationState} +@m_since_latest +*/ +MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, const DynamicRasterizationStates& value); + /** @brief Rasterization pipeline creation info @m_since_latest diff --git a/src/Magnum/Vk/Test/PipelineTest.cpp b/src/Magnum/Vk/Test/PipelineTest.cpp index a5df9d156..23785bcac 100644 --- a/src/Magnum/Vk/Test/PipelineTest.cpp +++ b/src/Magnum/Vk/Test/PipelineTest.cpp @@ -84,6 +84,8 @@ struct PipelineTest: TestSuite::Tester { void imageMemoryBarrierConstructFromVk(); void debugBindPoint(); + void debugDynamicRasterizationState(); + void debugDynamicRasterizationStates(); }; PipelineTest::PipelineTest() { @@ -124,7 +126,9 @@ PipelineTest::PipelineTest() { &PipelineTest::imageMemoryBarrierConstructNoInit, &PipelineTest::imageMemoryBarrierConstructFromVk, - &PipelineTest::debugBindPoint}); + &PipelineTest::debugBindPoint, + &PipelineTest::debugDynamicRasterizationState, + &PipelineTest::debugDynamicRasterizationStates}); } using namespace Containers::Literals; @@ -721,6 +725,18 @@ void PipelineTest::debugBindPoint() { CORRADE_COMPARE(out.str(), "Vk::PipelineBindPoint::Compute Vk::PipelineBindPoint(-10007655)\n"); } +void PipelineTest::debugDynamicRasterizationState() { + std::ostringstream out; + Debug{&out} << DynamicRasterizationState::VertexInputBindingStride << DynamicRasterizationState(0xab); + CORRADE_COMPARE(out.str(), "Vk::DynamicRasterizationState::VertexInputBindingStride Vk::DynamicRasterizationState(0xab)\n"); +} + +void PipelineTest::debugDynamicRasterizationStates() { + std::ostringstream out; + Debug{&out} << (DynamicRasterizationState::Viewport|DynamicRasterizationState::Scissor|DynamicRasterizationState(0x2a)|DynamicRasterizationState(0x3f)) << DynamicRasterizationStates{}; + CORRADE_COMPARE(out.str(), "Vk::DynamicRasterizationState::Viewport|Vk::DynamicRasterizationState::Scissor|Vk::DynamicRasterizationState(0x2a)|Vk::DynamicRasterizationState(0x3f) Vk::DynamicRasterizationStates{}\n"); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::PipelineTest) From 9e71e5ab0e751b4d82bf0ff78d8bbb835d0b91ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 7 Feb 2021 21:47:19 +0100 Subject: [PATCH 04/44] Vk: store the set of dynamic states in a Pipeline instance. Will be subsequently useful for command buffers. --- src/Magnum/Vk/Pipeline.cpp | 43 +++++++--- src/Magnum/Vk/Pipeline.h | 18 +++++ .../Vk/RasterizationPipelineCreateInfo.h | 2 + src/Magnum/Vk/Test/PipelineVkTest.cpp | 78 +++++++++++++++++-- 4 files changed, 124 insertions(+), 17 deletions(-) diff --git a/src/Magnum/Vk/Pipeline.cpp b/src/Magnum/Vk/Pipeline.cpp index ceef6f531..e4e926ee7 100644 --- a/src/Magnum/Vk/Pipeline.cpp +++ b/src/Magnum/Vk/Pipeline.cpp @@ -43,7 +43,12 @@ namespace Magnum { namespace Vk { struct RasterizationPipelineCreateInfo::State { Containers::Array colorBlendAttachments; - Containers::Array dynamicStates; + + /* The enum is saved as well to be subsequently available through + Pipeline::dynamicRasterizationStates() */ + DynamicRasterizationStates dynamicStates; + Containers::Array dynamicStateList; + /** @todo make an array once we support multiview */ VkViewport viewport; VkRect2D scissor; @@ -242,21 +247,24 @@ constexpr VkDynamicState DynamicRasterizationStateMapping[]{ } RasterizationPipelineCreateInfo& RasterizationPipelineCreateInfo::setDynamicStates(const DynamicRasterizationStates& states) { + /* Save the enum so we can store it in the created Pipeline object later */ + _state->dynamicStates = states; + /* Count the number of states set, allocate for that */ std::size_t count = 0; for(std::size_t i = 0; i != DynamicRasterizationStates::Size; ++i) count += Math::popcount(states.data()[i]); - _state->dynamicStates = Containers::Array{Containers::NoInit, count}; + _state->dynamicStateList = Containers::Array{Containers::NoInit, count}; std::size_t offset = 0; for(std::uint64_t i = 0; i != Containers::arraySize(DynamicRasterizationStateMapping); ++i) if(states & DynamicRasterizationState(i)) - _state->dynamicStates[offset++] = DynamicRasterizationStateMapping[i]; + _state->dynamicStateList[offset++] = DynamicRasterizationStateMapping[i]; CORRADE_INTERNAL_ASSERT(offset == count); _dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; _dynamicInfo.dynamicStateCount = count; - _dynamicInfo.pDynamicStates = _state->dynamicStates; + _dynamicInfo.pDynamicStates = _state->dynamicStateList; _info.pDynamicState = &_dynamicInfo; return *this; } @@ -278,15 +286,20 @@ ComputePipelineCreateInfo::ComputePipelineCreateInfo(const VkComputePipelineCrea member instead of doing a copy */ _info(info) {} -Pipeline Pipeline::wrap(Device& device, const PipelineBindPoint bindPoint, const VkPipeline handle, const HandleFlags flags) { +Pipeline Pipeline::wrap(Device& device, const PipelineBindPoint bindPoint, const VkPipeline handle, const DynamicRasterizationStates& dynamicStates, const HandleFlags flags) { Pipeline out{NoCreate}; out._device = &device; out._handle = handle; out._bindPoint = bindPoint; out._flags = flags; + out._dynamicStates.rasterization = dynamicStates; return out; } +Pipeline Pipeline::wrap(Device& device, const PipelineBindPoint bindPoint, const VkPipeline handle, const HandleFlags flags) { + return wrap(device, bindPoint, handle, DynamicRasterizationStates{}, flags); +} + Pipeline::Pipeline(Device& device, const RasterizationPipelineCreateInfo& info): _device{&device}, #ifdef CORRADE_GRACEFUL_ASSERT @@ -294,7 +307,8 @@ Pipeline::Pipeline(Device& device, const RasterizationPipelineCreateInfo& info): _handle{}, #endif _bindPoint{PipelineBindPoint::Rasterization}, - _flags{HandleFlag::DestroyOnDestruction} + _flags{HandleFlag::DestroyOnDestruction}, + _dynamicStates{info._state->dynamicStates} { /* Doesn't check that the viewport is really a dynamic state, but should catch most cases without false positives */ @@ -304,13 +318,17 @@ Pipeline::Pipeline(Device& device, const RasterizationPipelineCreateInfo& info): MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateGraphicsPipelines(device, {}, 1, info, nullptr, &_handle)); } -Pipeline::Pipeline(Device& device, const ComputePipelineCreateInfo& info): _device{&device}, _bindPoint{PipelineBindPoint::Compute}, _flags{HandleFlag::DestroyOnDestruction} { +Pipeline::Pipeline(Device& device, const ComputePipelineCreateInfo& info): _device{&device}, _bindPoint{PipelineBindPoint::Compute}, _flags{HandleFlag::DestroyOnDestruction}, _dynamicStates{} { MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateComputePipelines(device, {}, 1, info, nullptr, &_handle)); } -Pipeline::Pipeline(NoCreateT): _device{}, _handle{}, _bindPoint{} {} +Pipeline::Pipeline(NoCreateT): _device{}, _handle{}, _bindPoint{}, _dynamicStates{} {} -Pipeline::Pipeline(Pipeline&& other) noexcept: _device{other._device}, _handle{other._handle}, _bindPoint{other._bindPoint}, _flags{other._flags} { +Pipeline::Pipeline(Pipeline&& other) noexcept: _device{other._device}, _handle{other._handle}, _bindPoint{other._bindPoint}, _flags{other._flags}, + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _dynamicStates(other._dynamicStates) +{ other._handle = {}; } @@ -325,9 +343,16 @@ Pipeline& Pipeline::operator=(Pipeline&& other) noexcept { swap(other._handle, _handle); swap(other._bindPoint, _bindPoint); swap(other._flags, _flags); + swap(other._dynamicStates, _dynamicStates); return *this; } +DynamicRasterizationStates Pipeline::dynamicRasterizationStates() const { + CORRADE_ASSERT(_bindPoint == PipelineBindPoint::Rasterization, + "Vk::Pipeline::dynamicRasterizationStates(): not a rasterization pipeline", {}); + return _dynamicStates.rasterization; +} + VkPipeline Pipeline::release() { const VkPipeline handle = _handle; _handle = {}; diff --git a/src/Magnum/Vk/Pipeline.h b/src/Magnum/Vk/Pipeline.h index 554968675..1026adb2f 100644 --- a/src/Magnum/Vk/Pipeline.h +++ b/src/Magnum/Vk/Pipeline.h @@ -30,6 +30,7 @@ * @m_since_latest */ +#include #include #include "Magnum/Magnum.h" @@ -116,6 +117,9 @@ class MAGNUM_VK_EXPORT Pipeline { * @param bindPoint Pipeline bind point. Available through * @ref bindPoint() afterwards. * @param handle The @type_vk{Pipeline} handle + * @param dynamicStates Dynamic states enabled on the rasterization + * pipeline. Available through @ref dynamicRasterizationStates() + * afterwards. * @param flags Handle flags * * The @p handle is expected to be originating from @p device. Unlike @@ -124,6 +128,8 @@ class MAGNUM_VK_EXPORT Pipeline { * different behavior. * @see @ref release() */ + static Pipeline wrap(Device& device, PipelineBindPoint bindPoint, VkPipeline handle, const DynamicRasterizationStates& dynamicStates, HandleFlags flags = {}); + /** @overload */ static Pipeline wrap(Device& device, PipelineBindPoint bindPoint, VkPipeline handle, HandleFlags flags = {}); /** @@ -193,6 +199,15 @@ class MAGNUM_VK_EXPORT Pipeline { */ PipelineBindPoint bindPoint() const { return _bindPoint; } + /** + * @brief Dynamic rasterization states enabled in this pipeline + * + * Contains the states passed to @ref RasterizationPipelineCreateInfo::setDynamicStates() + * or to the @ref wrap() call. Expects that @ref bindPoint() is + * @ref PipelineBindPoint::Rasterization. + */ + DynamicRasterizationStates dynamicRasterizationStates() const; + /** * @brief Release the underlying Vulkan pipeline * @@ -210,6 +225,9 @@ class MAGNUM_VK_EXPORT Pipeline { VkPipeline _handle; PipelineBindPoint _bindPoint; HandleFlags _flags; + union { + DynamicRasterizationStates rasterization; + } _dynamicStates; }; /** diff --git a/src/Magnum/Vk/RasterizationPipelineCreateInfo.h b/src/Magnum/Vk/RasterizationPipelineCreateInfo.h index e579212bc..a5ce38bdf 100644 --- a/src/Magnum/Vk/RasterizationPipelineCreateInfo.h +++ b/src/Magnum/Vk/RasterizationPipelineCreateInfo.h @@ -524,6 +524,8 @@ class MAGNUM_VK_EXPORT RasterizationPipelineCreateInfo { operator const VkGraphicsPipelineCreateInfo*() const { return &_info; } private: + friend Pipeline; + VkGraphicsPipelineCreateInfo _info; VkPipelineViewportStateCreateInfo _viewportInfo; VkPipelineRasterizationStateCreateInfo _rasterizationInfo; diff --git a/src/Magnum/Vk/Test/PipelineVkTest.cpp b/src/Magnum/Vk/Test/PipelineVkTest.cpp index 98c3cf403..6fd80df60 100644 --- a/src/Magnum/Vk/Test/PipelineVkTest.cpp +++ b/src/Magnum/Vk/Test/PipelineVkTest.cpp @@ -62,7 +62,10 @@ struct PipelineVkTest: VulkanTester { void constructCompute(); void constructMove(); - void wrap(); + void wrapRasterization(); + void wrapCompute(); + + void dynamicRasterizationStatesNotRasterization(); void cmdBind(); @@ -81,7 +84,10 @@ PipelineVkTest::PipelineVkTest() { &PipelineVkTest::constructCompute, &PipelineVkTest::constructMove, - &PipelineVkTest::wrap, + &PipelineVkTest::wrapRasterization, + &PipelineVkTest::wrapCompute, + + &PipelineVkTest::dynamicRasterizationStatesNotRasterization, &PipelineVkTest::cmdBind, @@ -134,10 +140,12 @@ void PipelineVkTest::constructRasterization() { Pipeline pipeline{device(), RasterizationPipelineCreateInfo{ shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1} .setViewport({{}, {200, 200}}) + .setDynamicStates(DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias) }; CORRADE_VERIFY(pipeline.handle()); CORRADE_COMPARE(pipeline.handleFlags(), HandleFlag::DestroyOnDestruction); CORRADE_COMPARE(pipeline.bindPoint(), PipelineBindPoint::Rasterization); + CORRADE_COMPARE(pipeline.dynamicRasterizationStates(), DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias); } /* Shouldn't crash or anything */ @@ -241,16 +249,16 @@ void PipelineVkTest::constructRasterizationViewportNotSetDynamic() { RasterizationPipelineCreateInfo info{ shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1}; info.setViewport(Range3D{}) /* Has to be set because the count is used */ - .setDynamicStates(DynamicRasterizationState::Viewport| - DynamicRasterizationState::Scissor); + .setDynamicStates(DynamicRasterizationState::Viewport|DynamicRasterizationState::Scissor); /* But the data don't have to be there */ const_cast(info->pViewportState)->pViewports = nullptr; const_cast(info->pViewportState)->pScissors = nullptr; Pipeline pipeline{device(), info}; - /* The only thing I want to verify is that this doesn't crash or assert */ + /* The main thing I want to verify is that this doesn't crash or assert */ CORRADE_VERIFY(pipeline.handle()); + CORRADE_COMPARE(pipeline.dynamicRasterizationStates(), DynamicRasterizationState::Viewport|DynamicRasterizationState::Scissor); } void PipelineVkTest::constructCompute() { @@ -311,6 +319,7 @@ void PipelineVkTest::constructMove() { Pipeline a{device(), RasterizationPipelineCreateInfo{ shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1} .setViewport({{}, {200, 200}}) + .setDynamicStates(DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias) }; VkPipeline handle = a.handle(); @@ -319,6 +328,7 @@ void PipelineVkTest::constructMove() { CORRADE_COMPARE(b.handle(), handle); CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); CORRADE_COMPARE(b.bindPoint(), PipelineBindPoint::Rasterization); + CORRADE_COMPARE(b.dynamicRasterizationStates(), DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias); Pipeline c{NoCreate}; c = std::move(b); @@ -327,12 +337,13 @@ void PipelineVkTest::constructMove() { CORRADE_COMPARE(c.handle(), handle); CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); CORRADE_COMPARE(c.bindPoint(), PipelineBindPoint::Rasterization); + CORRADE_COMPARE(c.dynamicRasterizationStates(), DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias); CORRADE_VERIFY(std::is_nothrow_move_constructible::value); CORRADE_VERIFY(std::is_nothrow_move_assignable::value); } -void PipelineVkTest::wrap() { +void PipelineVkTest::wrapRasterization() { RenderPass renderPass{device(), RenderPassCreateInfo{} .setAttachments({ AttachmentDescription{PixelFormat::RGBA8Unorm, @@ -368,12 +379,39 @@ void PipelineVkTest::wrap() { VkPipeline pipeline{}; CORRADE_COMPARE(Result(device()->CreateGraphicsPipelines(device(), {}, 1, RasterizationPipelineCreateInfo{shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1} - .setViewport({{}, {200, 200}}), + .setViewport({{}, {200, 200}}) + .setDynamicStates(DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias), nullptr, &pipeline)), Result::Success); - auto wrapped = Pipeline::wrap(device(), PipelineBindPoint::Rasterization, pipeline, HandleFlag::DestroyOnDestruction); + auto wrapped = Pipeline::wrap(device(), PipelineBindPoint::Rasterization, pipeline, DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias, HandleFlag::DestroyOnDestruction); CORRADE_COMPARE(wrapped.handle(), pipeline); CORRADE_COMPARE(wrapped.bindPoint(), PipelineBindPoint::Rasterization); + CORRADE_COMPARE(wrapped.dynamicRasterizationStates(), DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias); + + /* Release the handle again, destroy by hand */ + CORRADE_COMPARE(wrapped.release(), pipeline); + CORRADE_VERIFY(!wrapped.handle()); + device()->DestroyPipeline(device(), pipeline, nullptr); +} + +void PipelineVkTest::wrapCompute() { + PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; + + Shader shader{device(), ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "compute-noop.spv")) + }}; + + ShaderSet shaderSet; + shaderSet.addShader(ShaderStage::Compute, shader, "main"_s); + + VkPipeline pipeline{}; + CORRADE_COMPARE(Result(device()->CreateComputePipelines(device(), {}, 1, + ComputePipelineCreateInfo{shaderSet, pipelineLayout}, + nullptr, &pipeline)), Result::Success); + + auto wrapped = Pipeline::wrap(device(), PipelineBindPoint::Compute, pipeline, HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(wrapped.handle(), pipeline); + CORRADE_COMPARE(wrapped.bindPoint(), PipelineBindPoint::Compute); /* Release the handle again, destroy by hand */ CORRADE_COMPARE(wrapped.release(), pipeline); @@ -381,6 +419,30 @@ void PipelineVkTest::wrap() { device()->DestroyPipeline(device(), pipeline, nullptr); } +void PipelineVkTest::dynamicRasterizationStatesNotRasterization() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; + + Shader shader{device(), ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "compute-noop.spv")) + }}; + + ShaderSet shaderSet; + shaderSet.addShader(ShaderStage::Compute, shader, "main"_s); + + Pipeline pipeline{device(), ComputePipelineCreateInfo{ + shaderSet, pipelineLayout + }}; + + std::ostringstream out; + Error redirectError{&out}; + pipeline.dynamicRasterizationStates(); + CORRADE_COMPARE(out.str(), "Vk::Pipeline::dynamicRasterizationStates(): not a rasterization pipeline\n"); +} + void PipelineVkTest::cmdBind() { CommandPool pool{device(), CommandPoolCreateInfo{ /* This might blow up if queue() isn't the one matching this family */ From b3394bfc48c6d04eed4fdc19f87ac028c5b11767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 7 Feb 2021 21:49:00 +0100 Subject: [PATCH 05/44] Vk: properly skip an assertion test case on a build w/o asserts. The "no assert" build on the CIs doesn't test Vulkan, so this didn't get caught. --- src/Magnum/Vk/Test/PipelineVkTest.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Magnum/Vk/Test/PipelineVkTest.cpp b/src/Magnum/Vk/Test/PipelineVkTest.cpp index 6fd80df60..c2bccb7b3 100644 --- a/src/Magnum/Vk/Test/PipelineVkTest.cpp +++ b/src/Magnum/Vk/Test/PipelineVkTest.cpp @@ -153,6 +153,10 @@ void PipelineVkTest::constructRasterization() { } void PipelineVkTest::constructRasterizationViewportNotSet() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + MeshLayout meshLayout{MeshPrimitive::Triangles}; PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; From d9d8c419809a5b5575c0d5b4a55fb1829d96d332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 7 Feb 2021 21:56:03 +0100 Subject: [PATCH 06/44] Vk: rename *IndexTypeUintXY to IndexTypeUnsignedFoo for consistency. It would be weird if we had MeshIndexType::UnsignedByte but DeviceFeature::IndexTypeUint8. --- doc/snippets/MagnumVk.cpp | 4 ++-- src/Magnum/Vk/DeviceFeatures.h | 8 ++++---- src/Magnum/Vk/Implementation/deviceFeatureMapping.hpp | 4 ++-- src/Magnum/Vk/Test/DeviceFeaturesTest.cpp | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index 6cf1ecffc..c6a7e811b 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -348,7 +348,7 @@ using namespace Containers::Literals; Vk::Device device{instance, Vk::DeviceCreateInfo{DOXYGEN_IGNORE(properties)} DOXYGEN_IGNORE() .setEnabledFeatures( - Vk::DeviceFeature::IndexTypeUint8| + Vk::DeviceFeature::IndexTypeUnsignedByte| Vk::DeviceFeature::SamplerAnisotropy| Vk::DeviceFeature::GeometryShader| DOXYGEN_IGNORE(Vk::DeviceFeature{})) @@ -370,7 +370,7 @@ if(extensions.isSupported("VK_NV_mesh_shader"_s)) info.addEnabledExtensions({"VK_NV_mesh_shader"_s}); DOXYGEN_IGNORE() info.setEnabledFeatures(properties.features() & // mask away unsupported ones - (Vk::DeviceFeature::IndexTypeUint8| + (Vk::DeviceFeature::IndexTypeUnsignedByte| Vk::DeviceFeature::SamplerAnisotropy| Vk::DeviceFeature::GeometryShader| DOXYGEN_IGNORE(Vk::DeviceFeature{}))); diff --git a/src/Magnum/Vk/DeviceFeatures.h b/src/Magnum/Vk/DeviceFeatures.h index bac1f076f..5facabe34 100644 --- a/src/Magnum/Vk/DeviceFeatures.h +++ b/src/Magnum/Vk/DeviceFeatures.h @@ -71,10 +71,10 @@ enum class DeviceFeature: UnsignedShort { /** * Whether the full 32-bit range is supported for indexed draw calls when * using @val_vk{INDEX_TYPE_UINT32,IndexType}. - * @see @ref DeviceFeature::IndexTypeUint8 + * @see @ref DeviceFeature::IndexTypeUnsignedByte * @todo expose the `maxDrawIndexedIndexValue` limit */ - FullDrawIndexUint32, + FullDrawIndexUnsignedInt, /** * Whether image views with @val_vk{IMAGE_VIEW_TYPE_CUBE_ARRAY,ImageViewType} @@ -1271,10 +1271,10 @@ enum class DeviceFeature: UnsignedShort { /** * Whether an 8-bit type can be used in index buffers. - * @see @ref DeviceFeature::FullDrawIndexUint32 + * @see @ref DeviceFeature::FullDrawIndexUnsignedInt * @requires_vk_extension Extension @vk_extension{EXT,index_type_uint8} */ - IndexTypeUint8, + IndexTypeUnsignedByte, /* VkPhysicalDeviceExtendedDynamicStateFeaturesEXT, #268 */ diff --git a/src/Magnum/Vk/Implementation/deviceFeatureMapping.hpp b/src/Magnum/Vk/Implementation/deviceFeatureMapping.hpp index 82ecc2ed7..d392e6411 100644 --- a/src/Magnum/Vk/Implementation/deviceFeatureMapping.hpp +++ b/src/Magnum/Vk/Implementation/deviceFeatureMapping.hpp @@ -36,7 +36,7 @@ */ #ifdef _c _c(RobustBufferAccess, robustBufferAccess) -_c(FullDrawIndexUint32, fullDrawIndexUint32) +_c(FullDrawIndexUnsignedInt, fullDrawIndexUint32) _c(ImageCubeArray, imageCubeArray) _c(IndependentBlend, independentBlend) _c(GeometryShader, geometryShader) @@ -213,7 +213,7 @@ _ce(BufferDeviceAddressMultiDevice, bufferDeviceAddressMultiDevice) _cext(HostQueryReset, hostQueryReset, hostQueryReset, EXT::host_query_reset) -_cext(IndexTypeUint8, indexTypeUint8, indexTypeUint8, EXT::index_type_uint8) +_cext(IndexTypeUnsignedByte, indexTypeUint8, indexTypeUint8, EXT::index_type_uint8) _cext(ExtendedDynamicState, extendedDynamicState, extendedDynamicState, EXT::extended_dynamic_state) diff --git a/src/Magnum/Vk/Test/DeviceFeaturesTest.cpp b/src/Magnum/Vk/Test/DeviceFeaturesTest.cpp index 9e0e0ec74..ff6c7af15 100644 --- a/src/Magnum/Vk/Test/DeviceFeaturesTest.cpp +++ b/src/Magnum/Vk/Test/DeviceFeaturesTest.cpp @@ -95,14 +95,14 @@ void DeviceFeaturesTest::mapping() { void DeviceFeaturesTest::debugFeature() { std::ostringstream out; - Debug{&out} << DeviceFeature::FullDrawIndexUint32 << DeviceFeature::VulkanMemoryModel << DeviceFeature(0xab); - CORRADE_COMPARE(out.str(), "Vk::DeviceFeature::FullDrawIndexUint32 Vk::DeviceFeature::VulkanMemoryModel Vk::DeviceFeature(0xab)\n"); + Debug{&out} << DeviceFeature::FullDrawIndexUnsignedInt << DeviceFeature::VulkanMemoryModel << DeviceFeature(0xab); + CORRADE_COMPARE(out.str(), "Vk::DeviceFeature::FullDrawIndexUnsignedInt Vk::DeviceFeature::VulkanMemoryModel Vk::DeviceFeature(0xab)\n"); } void DeviceFeaturesTest::debugFeatures() { std::ostringstream out; - Debug{&out} << (DeviceFeature::FullDrawIndexUint32|DeviceFeature::VulkanMemoryModel|DeviceFeature(0xab)|DeviceFeature(0xcc)) << DeviceFeatures{}; - CORRADE_COMPARE(out.str(), "Vk::DeviceFeature::FullDrawIndexUint32|Vk::DeviceFeature::VulkanMemoryModel|Vk::DeviceFeature(0xab)|Vk::DeviceFeature(0xcc) Vk::DeviceFeatures{}\n"); + Debug{&out} << (DeviceFeature::FullDrawIndexUnsignedInt|DeviceFeature::VulkanMemoryModel|DeviceFeature(0xab)|DeviceFeature(0xcc)) << DeviceFeatures{}; + CORRADE_COMPARE(out.str(), "Vk::DeviceFeature::FullDrawIndexUnsignedInt|Vk::DeviceFeature::VulkanMemoryModel|Vk::DeviceFeature(0xab)|Vk::DeviceFeature(0xcc) Vk::DeviceFeatures{}\n"); } }}}} From 14389c99d503f82578a01ea6e147986c5ed1c73a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 7 Feb 2021 22:32:26 +0100 Subject: [PATCH 07/44] Vk: save current dynamic state on a pipeline bind. Will be used for the draw() command. --- src/Magnum/Vk/CommandBuffer.cpp | 6 +++ src/Magnum/Vk/CommandBuffer.h | 19 +++++++- src/Magnum/Vk/Pipeline.cpp | 4 ++ src/Magnum/Vk/Test/PipelineVkTest.cpp | 65 +++++++++++++++++++++++++-- 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/src/Magnum/Vk/CommandBuffer.cpp b/src/Magnum/Vk/CommandBuffer.cpp index 6e78948c2..65a4529e5 100644 --- a/src/Magnum/Vk/CommandBuffer.cpp +++ b/src/Magnum/Vk/CommandBuffer.cpp @@ -84,6 +84,12 @@ CommandBuffer& CommandBuffer::begin(const CommandBufferBeginInfo& info) { } void CommandBuffer::end() { + /* Clear everything that is valid only for the duration of this command + buffer recording -- so when the user calls reset() and begin() again, + the old values are not preserved */ + /** @todo do this on begin() too? */ + _dynamicRasterizationStates = {}; + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS((**_device).EndCommandBuffer(_handle)); } diff --git a/src/Magnum/Vk/CommandBuffer.h b/src/Magnum/Vk/CommandBuffer.h index cd8440668..fcaa5a4f9 100644 --- a/src/Magnum/Vk/CommandBuffer.h +++ b/src/Magnum/Vk/CommandBuffer.h @@ -31,6 +31,7 @@ */ #include +#include #include #include "Magnum/Tags.h" @@ -259,6 +260,17 @@ class MAGNUM_VK_EXPORT CommandBuffer { /** @brief Handle flags */ HandleFlags handleFlags() const { return _flags; } + /** + * @brief Dynamic states used by currently bound rasterization pipeline + * + * If no rasterization pipeline is bound or there are no dynamic states + * on the currently bound one, returns an empty set. + * @see @ref bindPipeline() + */ + DynamicRasterizationStates dynamicRasterizationStates() const { + return _dynamicRasterizationStates; + } + /** * @brief Reset the command buffer * @@ -361,8 +373,10 @@ class MAGNUM_VK_EXPORT CommandBuffer { * @brief Bind a pipeline * @return Reference to self (for method chaining) * - * Can be called both inside and outside a render pass. See - * @ref Vk-Pipeline-usage for a usage example. + * Can be called both inside and outside a render pass. If the pipeline + * is a rasterization pipeline, the set of its dynamic states is stored + * in @ref dynamicRasterizationStates() for use by drawing and other + * commands. See @ref Vk-Pipeline-usage for a usage example. * @see @fn_vk_keyword{CmdBindPipeline} */ CommandBuffer& bindPipeline(Pipeline& pipeline); @@ -765,6 +779,7 @@ class MAGNUM_VK_EXPORT CommandBuffer { VkCommandPool _pool; /* Used only for vkFreeCommandBuffers() */ VkCommandBuffer _handle; HandleFlags _flags; + DynamicRasterizationStates _dynamicRasterizationStates; }; }} diff --git a/src/Magnum/Vk/Pipeline.cpp b/src/Magnum/Vk/Pipeline.cpp index e4e926ee7..217d9fec0 100644 --- a/src/Magnum/Vk/Pipeline.cpp +++ b/src/Magnum/Vk/Pipeline.cpp @@ -412,6 +412,10 @@ ImageMemoryBarrier::ImageMemoryBarrier(const VkImageMemoryBarrier& barrier): _barrier(barrier) {} CommandBuffer& CommandBuffer::bindPipeline(Pipeline& pipeline) { + /* Save the set of dynamic states for future use */ + if(pipeline.bindPoint() == PipelineBindPoint::Rasterization) + _dynamicRasterizationStates = pipeline.dynamicRasterizationStates(); + (**_device).CmdBindPipeline(_handle, VkPipelineBindPoint(pipeline.bindPoint()), pipeline); return *this; } diff --git a/src/Magnum/Vk/Test/PipelineVkTest.cpp b/src/Magnum/Vk/Test/PipelineVkTest.cpp index c2bccb7b3..789286055 100644 --- a/src/Magnum/Vk/Test/PipelineVkTest.cpp +++ b/src/Magnum/Vk/Test/PipelineVkTest.cpp @@ -67,7 +67,8 @@ struct PipelineVkTest: VulkanTester { void dynamicRasterizationStatesNotRasterization(); - void cmdBind(); + void cmdBindRasterization(); + void cmdBindCompute(); void cmdBarrier(); void cmdBarrierExecutionOnly(); @@ -89,7 +90,8 @@ PipelineVkTest::PipelineVkTest() { &PipelineVkTest::dynamicRasterizationStatesNotRasterization, - &PipelineVkTest::cmdBind, + &PipelineVkTest::cmdBindRasterization, + &PipelineVkTest::cmdBindCompute, &PipelineVkTest::cmdBarrier, &PipelineVkTest::cmdBarrierExecutionOnly, @@ -447,7 +449,64 @@ void PipelineVkTest::dynamicRasterizationStatesNotRasterization() { CORRADE_COMPARE(out.str(), "Vk::Pipeline::dynamicRasterizationStates(): not a rasterization pipeline\n"); } -void PipelineVkTest::cmdBind() { +void PipelineVkTest::cmdBindRasterization() { + CommandPool pool{device(), CommandPoolCreateInfo{ + /* This might blow up if queue() isn't the one matching this family */ + device().properties().pickQueueFamily(QueueFlag::Graphics|QueueFlag::Compute)}}; + + RenderPass renderPass{device(), RenderPassCreateInfo{} + .setAttachments({ + AttachmentDescription{PixelFormat::RGBA8Unorm, + AttachmentLoadOperation::Clear, + AttachmentStoreOperation::Store, + ImageLayout::Undefined, + ImageLayout::ColorAttachment} + }) + .addSubpass(SubpassDescription{}.setColorAttachments({ + AttachmentReference{0, ImageLayout::ColorAttachment} + })) + }; + + /* Not sure if this is really needed, but the shader needs those inputs so + playing it safe */ + MeshLayout meshLayout{MeshPrimitive::Triangles}; + meshLayout + .addBinding(0, 2*4*4) + .addAttribute(0, 0, Vk::VertexFormat::Vector4, 0) + .addAttribute(1, 0, Vk::VertexFormat::Vector4, 4*4); + + PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; + + Shader shader{device(), ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "triangle-shaders.spv")) + }}; + + ShaderSet shaderSet; + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); + + Pipeline pipeline{device(), RasterizationPipelineCreateInfo{ + shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1} + .setViewport({{}, {200, 200}}) + .setDynamicStates(DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias) + }; + + CommandBuffer cmd = pool.allocate(); + + cmd.begin(); + CORRADE_COMPARE(cmd.dynamicRasterizationStates(), DynamicRasterizationStates{}); + + cmd.bindPipeline(pipeline); + CORRADE_COMPARE(cmd.dynamicRasterizationStates(), DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias); + + /* Should be reset again on end() so if it's reset() and begun again it + doesn't show a no-longer-true value */ + cmd.end(); + CORRADE_COMPARE(cmd.dynamicRasterizationStates(), DynamicRasterizationStates{}); +} + +void PipelineVkTest::cmdBindCompute() { CommandPool pool{device(), CommandPoolCreateInfo{ /* This might blow up if queue() isn't the one matching this family */ device().properties().pickQueueFamily(QueueFlag::Graphics|QueueFlag::Compute)}}; From 22f918dffd42d11ecd1cd7cc897c75c30bf69ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 8 Feb 2021 13:31:37 +0100 Subject: [PATCH 08/44] Vk: some more docs / tips for pipeline barriers. --- doc/platforms-vk.dox | 4 ++++ src/Magnum/Vk/CommandBuffer.h | 27 +++++++++++++++++++++++ src/Magnum/Vk/Pipeline.h | 40 +++++++++++++++++++++++++++++++---- 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/doc/platforms-vk.dox b/doc/platforms-vk.dox index ba6d7235b..5a06fa19f 100644 --- a/doc/platforms-vk.dox +++ b/doc/platforms-vk.dox @@ -75,6 +75,10 @@ Build as a static library and supply to CMake via `Vulkan_LIBRARY`: @section platforms-vk-best-practices Vulkan best practices +Khronos wiki: + +- [Synchronization Examples](https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples) + NVidia tutorials and tips: - [Engaging the Voyage to Vulkan](https://developer.nvidia.com/engaging-voyage-vulkan) diff --git a/src/Magnum/Vk/CommandBuffer.h b/src/Magnum/Vk/CommandBuffer.h index fcaa5a4f9..2ebd3345a 100644 --- a/src/Magnum/Vk/CommandBuffer.h +++ b/src/Magnum/Vk/CommandBuffer.h @@ -413,6 +413,33 @@ class MAGNUM_VK_EXPORT CommandBuffer { * * See @ref Vk-Buffer-usage-copy, @ref Vk-Image-usage-clear and * @ref Vk-Image-usage-copy for usage examples. + * + * @m_class{m-note m-success} + * + * @par + * Where possible, expressing the dependencies and image layout + * transitions via @ref RenderPass APIs is considered more + * efficient than an explicit barrier ([source](https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples#graphics-to-graphics-dependencies)). + * @par + * To avoid pipeline stalls and unnecessary synchronization, avoid + * using overly generic stage and access sets. On the other hand, + * using @ref PipelineStage::AllCommands together with + * @ref Access::MemoryRead / @relativeref{Access,MemoryWrite} is + * useful for debugging synchronization issues. + * @par + * According to multiple sources ([1](https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples#three-dispatches-first-dispatch-writes-to-one-storage-buffer-second-dispatch-writes-to-a-different-storage-buffer-third-dispatch-reads-both), + * [2](https://developer.nvidia.com/blog/vulkan-dos-donts/#h.6fr5jvul7u03)), + * it's advised to group multiple barriers together into a single + * call --- that way the worst case can be picked instead of + * sequentially going through all barriers. + * @par + * Even though it may seem counterintuitive, it's recommended + * ([1](https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples#three-dispatches-first-dispatch-writes-to-one-storage-buffer-second-dispatch-writes-to-a-different-storage-buffer-third-dispatch-reads-both), + * [2](http://themaister.net/blog/2019/08/14/yet-another-blog-explaining-vulkan-synchronization/)) + * to do global memory barriers than per-resource barriers, except + * for cases where layout transition or queue ownership transfer + * needs to be done on a particular image or buffer. + * * @see @fn_vk_keyword{CmdPipelineBarrier} */ CommandBuffer& pipelineBarrier(PipelineStages sourceStages, PipelineStages destinationStages, Containers::ArrayView memoryBarriers, Containers::ArrayView bufferMemoryBarriers, Containers::ArrayView imageMemoryBarriers, DependencyFlags dependencyFlags = {}); diff --git a/src/Magnum/Vk/Pipeline.h b/src/Magnum/Vk/Pipeline.h index 1026adb2f..1f0255e1e 100644 --- a/src/Magnum/Vk/Pipeline.h +++ b/src/Magnum/Vk/Pipeline.h @@ -355,7 +355,16 @@ enum class PipelineStage: UnsignedInt { */ AllGraphics = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, - /** All commands */ + /** + * All commands. + * + * @m_class{m-note m-success} + * + * @par + * To avoid pipeline stalls and unnecessary synchronization, it's not + * advised to use this flag except for debugging synchronization + * issues. + */ AllCommands = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT }; @@ -523,6 +532,13 @@ enum class Access: UnsignedInt { /** * All read accesses. Valid for any @ref PipelineStage, treated as * equivalent of a combination of all `*Read` flags valid in given context. + * + * @m_class{m-note m-success} + * + * @par + * To avoid pipeline stalls and unnecessary synchronization, it's not + * advised to use this flag except for debugging synchronization + * issues. */ MemoryRead = VK_ACCESS_MEMORY_READ_BIT, @@ -530,6 +546,13 @@ enum class Access: UnsignedInt { * All write accesses. Valid for any @ref PipelineStage, treated as * equivalent of a combination of all `*Write` flags valid in given * context. + * + * @m_class{m-note m-success} + * + * @par + * To avoid pipeline stalls and unnecessary synchronization, it's not + * advised to use this flag except for debugging synchronization + * issues. */ MemoryWrite = VK_ACCESS_MEMORY_WRITE_BIT, @@ -614,7 +637,10 @@ class MAGNUM_VK_EXPORT MemoryBarrier { /** * @brief Constructor * @param sourceAccesses Source memory access types - * participating in a dependency + * participating in a dependency. While allowed, passing `Read` + * accesses here is redundant --- that's already taken care of by + * the stage execution dependency in + * @ref CommandBuffer::pipelineBarrier(). * @param destinationAccesses Destination memory access types * participating in a dependency * @@ -672,7 +698,10 @@ class MAGNUM_VK_EXPORT BufferMemoryBarrier { /** * @brief Constructor * @param sourceAccesses Source memory access types - * participating in a dependency + * participating in a dependency. While allowed, passing `Read` + * accesses here is redundant --- that's already taken care of by + * the stage execution dependency in + * @ref CommandBuffer::pipelineBarrier(). * @param destinationAccesses Destination memory access types * participating in a dependency * @param buffer A @ref Buffer or a raw Vulkan buffer @@ -740,7 +769,10 @@ class MAGNUM_VK_EXPORT ImageMemoryBarrier { /** * @brief Constructor * @param sourceAccesses Source memory access types - * participating in a dependency + * participating in a dependency. While allowed, passing `Read` + * accesses here is redundant --- that's already taken care of by + * the stage execution dependency in + * @ref CommandBuffer::pipelineBarrier(). * @param oldLayout Old layout in an image layout * transition * @param destinationAccesses Destination memory access types From 571772581cc28be38d8d85f02ed7633eb45b8bbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 8 Feb 2021 14:15:04 +0100 Subject: [PATCH 09/44] Vk: rename PipelineStage::AllGraphics to AllRasterization. Because that's what it is, after all. Also mention this naming difference in the Vulkan wrapping overview. I was wondering if I should rename QueueFlag::Graphics too, but since graphics queues are used for (not really rasterization-specific) image copies as well, and ray tracing is actually done on compute queues, I don't think renaming to Rasterization is a good idea. Clarified the comments at least. --- doc/vulkan-wrapping.dox | 9 +++++++++ src/Magnum/Vk/DeviceProperties.h | 4 ++-- src/Magnum/Vk/Pipeline.h | 14 ++++++++------ src/Magnum/Vk/Test/RenderPassTest.cpp | 4 ++-- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/doc/vulkan-wrapping.dox b/doc/vulkan-wrapping.dox index b64d5873f..b21ddaa1d 100644 --- a/doc/vulkan-wrapping.dox +++ b/doc/vulkan-wrapping.dox @@ -188,5 +188,14 @@ can be also passed to @ref Vk::DeviceCreateInfo to allow reuse: @snippet MagnumVk.cpp wrapping-optimizing-properties-device-move +@section vulkan-wrapping-naming-differences Important differences in naming + +- To emphasise the distinction between rasterization and raytracing + pipelines and prevent confusion, @ref Vk::RasterizationPipelineCreateInfo + is used for @type_vk{GraphicsPipelineCreateInfo}, and similarly for + related APIs, such as @ref Vk::DynamicRasterizationState containing a + rasterization-related subset of @type_vk{DynamicState} or + @ref Vk::PipelineStage::AllRasterization for @val_vk{PIPELINE_STAGE_ALL_GRAPHICS_BIT,PipelineStageFlagBits}. + */ } diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index 6fe6a3584..336a39aaf 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -161,10 +161,10 @@ Wraps a @type_vk_keyword{QueueFlagBits}. @m_enum_values_as_keywords */ enum class QueueFlag: UnsignedInt { - /** Supports graphics operations */ + /** Supports general graphics and rasterization operations. */ Graphics = VK_QUEUE_GRAPHICS_BIT, - /** Supports compute operations */ + /** Supports compute and ray tracing operations. */ Compute = VK_QUEUE_COMPUTE_BIT, /** Supports transfer operations */ diff --git a/src/Magnum/Vk/Pipeline.h b/src/Magnum/Vk/Pipeline.h index 1f0255e1e..694958db8 100644 --- a/src/Magnum/Vk/Pipeline.h +++ b/src/Magnum/Vk/Pipeline.h @@ -335,9 +335,9 @@ enum class PipelineStage: UnsignedInt { RayTracingShader = VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, /** - * Execution of all graphics stages. While numerically a single bit, it's - * equivalent to the logical OR of a supported and enabled subset of the - * following: + * Execution of all rasterization stages. While numerically a single bit, + * it's equivalent to the logical OR of a supported and enabled subset of + * the following: * * - @ref PipelineStage::DrawIndirect * - @ref PipelineStage::VertexInput @@ -350,10 +350,12 @@ enum class PipelineStage: UnsignedInt { * - @ref PipelineStage::LateFragmentTests * - @ref PipelineStage::ColorAttachmentOutput * - * Note that this *does not* include @ref PipelineStage::RayTracingShader - * or @ref PipelineStage::AccelerationStructureBuild. + * As the name suggests, this *does not* include + * @ref PipelineStage::RayTracingShader or + * @ref PipelineStage::AccelerationStructureBuild. + * @todo mention mesh / task shaders once exposed */ - AllGraphics = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + AllRasterization = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, /** * All commands. diff --git a/src/Magnum/Vk/Test/RenderPassTest.cpp b/src/Magnum/Vk/Test/RenderPassTest.cpp index e0f941668..c0cf0d448 100644 --- a/src/Magnum/Vk/Test/RenderPassTest.cpp +++ b/src/Magnum/Vk/Test/RenderPassTest.cpp @@ -776,7 +776,7 @@ void RenderPassTest::subpassDependencyConstruct() { SubpassDependency dependency{ 15, SubpassDependency::External, PipelineStage::ComputeShader|PipelineStage::Transfer, - PipelineStage::AllGraphics, + PipelineStage::AllRasterization, Access::TransferRead|Access::UniformRead, Access::MemoryWrite, DependencyFlag::ByRegion}; @@ -830,7 +830,7 @@ template void RenderPassTest::subpassDependencyConvertToVk() { SubpassDependency dependency{ 15, SubpassDependency::External, PipelineStage::ComputeShader|PipelineStage::Transfer, - PipelineStage::AllGraphics, + PipelineStage::AllRasterization, Access::TransferRead|Access::UniformRead, Access::MemoryWrite, DependencyFlag::ByRegion}; From fb5c1b14cad78498af39a287a67e677dcc36e97f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 8 Feb 2021 14:34:28 +0100 Subject: [PATCH 10/44] Vk: rename DynamicRasterizationState::PrimitiveToplogy to MeshPrimitive. For consistency, again. --- doc/vulkan-wrapping.dox | 12 ++++++++++++ src/Magnum/Trade/MeshData.h | 2 +- src/Magnum/Vk/DeviceFeatures.h | 2 +- .../dynamicRasterizationStateMapping.hpp | 2 +- src/Magnum/Vk/RasterizationPipelineCreateInfo.h | 2 +- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/doc/vulkan-wrapping.dox b/doc/vulkan-wrapping.dox index b21ddaa1d..cd369c011 100644 --- a/doc/vulkan-wrapping.dox +++ b/doc/vulkan-wrapping.dox @@ -196,6 +196,18 @@ can be also passed to @ref Vk::DeviceCreateInfo to allow reuse: related APIs, such as @ref Vk::DynamicRasterizationState containing a rasterization-related subset of @type_vk{DynamicState} or @ref Vk::PipelineStage::AllRasterization for @val_vk{PIPELINE_STAGE_ALL_GRAPHICS_BIT,PipelineStageFlagBits}. +- Because not all @type_vk{Format} values can be used as both pixel and + vertex formats (for example there can be double-precision vertices, but not + pixels, or it makes no sense to use ASTC for vertices), the enum is split + into @ref Vk::PixelFormat and @ref Vk::VertexFormat. For pixel formats the + naming is simplified for easier typing (@ref Vk::PixelFormat::RGBA8Srgb + instead of @val_vk{FORMAT_R8G8B8A8_SRGB,Format}, for example); for vertex + formats the RGBA notion is omitted and replaced with just component count, + in most cases matching Magnum scalar and vector type names + (@ref Vk::VertexFormat::Vector3 instead of @val_vk{FORMAT_R32G32B32_SFLOAT,Format}) +- As Magnum has several mesh-related abstraction (and Vulkan none), + @ref Vk::MeshPrimitive is used instead of @type_vk{PrimitiveTopology} for a + better visual grouping of related concepts */ } diff --git a/src/Magnum/Trade/MeshData.h b/src/Magnum/Trade/MeshData.h index 7a341fe23..613ae00e7 100644 --- a/src/Magnum/Trade/MeshData.h +++ b/src/Magnum/Trade/MeshData.h @@ -690,7 +690,7 @@ to map custom @ref MeshAttribute values to human-readable string names using @ref AbstractImporter::meshAttributeName() and @ref AbstractImporter::meshAttributeForName(). Using @ref meshPrimitiveWrap() you can also supply implementation-specific values that are not available in -the generic @ref MeshPrimitive enum, similarly see also +the generic @relativeref{Magnum,MeshPrimitive} enum, similarly see also @ref Trade-MeshAttributeData-custom-vertex-format for details on implementation-specific @ref VertexFormat values. @see @ref AbstractImporter::mesh() diff --git a/src/Magnum/Vk/DeviceFeatures.h b/src/Magnum/Vk/DeviceFeatures.h index 5facabe34..ca6d4bc00 100644 --- a/src/Magnum/Vk/DeviceFeatures.h +++ b/src/Magnum/Vk/DeviceFeatures.h @@ -1281,7 +1281,7 @@ enum class DeviceFeature: UnsignedShort { /** * The @ref DynamicRasterizationState::CullMode, * @relativeref{DynamicRasterizationState,FrontFace}, - * @relativeref{DynamicRasterizationState,PrimitiveTopology}, + * @relativeref{DynamicRasterizationState,MeshPrimitive}, * @relativeref{DynamicRasterizationState,ViewportWithCount}, * @relativeref{DynamicRasterizationState,ScissorWithCount}, * @relativeref{DynamicRasterizationState,VertexInputBindingStride}, diff --git a/src/Magnum/Vk/Implementation/dynamicRasterizationStateMapping.hpp b/src/Magnum/Vk/Implementation/dynamicRasterizationStateMapping.hpp index afd38311f..63bcb8741 100644 --- a/src/Magnum/Vk/Implementation/dynamicRasterizationStateMapping.hpp +++ b/src/Magnum/Vk/Implementation/dynamicRasterizationStateMapping.hpp @@ -36,7 +36,7 @@ _c(StencilWriteMask, STENCIL_WRITE_MASK) _c(StencilReference, STENCIL_REFERENCE) _c(CullMode, CULL_MODE_EXT) _c(FrontFace, FRONT_FACE_EXT) -_c(PrimitiveTopology, PRIMITIVE_TOPOLOGY_EXT) +_c(MeshPrimitive, PRIMITIVE_TOPOLOGY_EXT) _c(ViewportWithCount, VIEWPORT_WITH_COUNT_EXT) _c(ScissorWithCount, SCISSOR_WITH_COUNT_EXT) _c(VertexInputBindingStride, VERTEX_INPUT_BINDING_STRIDE_EXT) diff --git a/src/Magnum/Vk/RasterizationPipelineCreateInfo.h b/src/Magnum/Vk/RasterizationPipelineCreateInfo.h index a5ce38bdf..aa046cac3 100644 --- a/src/Magnum/Vk/RasterizationPipelineCreateInfo.h +++ b/src/Magnum/Vk/RasterizationPipelineCreateInfo.h @@ -164,7 +164,7 @@ enum class DynamicRasterizationState: UnsignedByte { * @requires_vk_feature @ref DeviceFeature::ExtendedDynamicState * @m_keywords{VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT} */ - PrimitiveTopology, + MeshPrimitive, /** * Both the number of viewports and their ranges set in From 9ce36aa668d8a5c0f5e835984cf1ddf87f8c6cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 8 Feb 2021 17:07:41 +0100 Subject: [PATCH 11/44] Vk: rvalue overloads for MeshLayout setters. So it's possible to do Vk::Mesh mesh{Vk::MeshLayout{} .addBinding(...) .addAttribute(...) }; Without this, the above will result in a dangling layout reference. --- src/Magnum/Vk/MeshLayout.cpp | 24 ++++++++++++++++++++---- src/Magnum/Vk/MeshLayout.h | 16 ++++++++++++---- src/Magnum/Vk/Test/MeshLayoutTest.cpp | 17 +++++++++++++++++ 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/Magnum/Vk/MeshLayout.cpp b/src/Magnum/Vk/MeshLayout.cpp index 5d49267ca..ac485c340 100644 --- a/src/Magnum/Vk/MeshLayout.cpp +++ b/src/Magnum/Vk/MeshLayout.cpp @@ -172,7 +172,7 @@ bool MeshLayout::operator==(const MeshLayout& other) const { #undef _c } -MeshLayout& MeshLayout::addBinding(const UnsignedInt binding, const UnsignedInt stride) { +MeshLayout& MeshLayout::addBinding(const UnsignedInt binding, const UnsignedInt stride) & { if(!_state) _state.emplace(); /* Ensure order for efficient comparisons */ @@ -190,7 +190,11 @@ MeshLayout& MeshLayout::addBinding(const UnsignedInt binding, const UnsignedInt return *this; } -MeshLayout& MeshLayout::addInstancedBinding(const UnsignedInt binding, const UnsignedInt stride, const UnsignedInt divisor) { +MeshLayout&& MeshLayout::addBinding(const UnsignedInt binding, const UnsignedInt stride) && { + return std::move(addBinding(binding, stride)); +} + +MeshLayout& MeshLayout::addInstancedBinding(const UnsignedInt binding, const UnsignedInt stride, const UnsignedInt divisor) & { if(!_state) _state.emplace(); /* Ensure order for efficient comparisons */ @@ -221,7 +225,11 @@ MeshLayout& MeshLayout::addInstancedBinding(const UnsignedInt binding, const Uns return *this; } -MeshLayout& MeshLayout::addAttribute(const UnsignedInt location, const UnsignedInt binding, const VertexFormat format, const UnsignedInt offset) { +MeshLayout&& MeshLayout::addInstancedBinding(const UnsignedInt binding, const UnsignedInt stride, const UnsignedInt divisor) && { + return std::move(addInstancedBinding(binding, stride, divisor)); +} + +MeshLayout& MeshLayout::addAttribute(const UnsignedInt location, const UnsignedInt binding, const VertexFormat format, const UnsignedInt offset) & { if(!_state) _state.emplace(); /* Ensure order for efficient comparisons */ @@ -240,10 +248,18 @@ MeshLayout& MeshLayout::addAttribute(const UnsignedInt location, const UnsignedI return *this; } -MeshLayout& MeshLayout::addAttribute(const UnsignedInt location, const UnsignedInt binding, const Magnum::VertexFormat format, const UnsignedInt offset) { +MeshLayout&& MeshLayout::addAttribute(const UnsignedInt location, const UnsignedInt binding, const VertexFormat format, const UnsignedInt offset) && { + return std::move(addAttribute(location, binding, format, offset)); +} + +MeshLayout& MeshLayout::addAttribute(const UnsignedInt location, const UnsignedInt binding, const Magnum::VertexFormat format, const UnsignedInt offset) & { return addAttribute(location, binding, vertexFormat(format), offset); } +MeshLayout&& MeshLayout::addAttribute(const UnsignedInt location, const UnsignedInt binding, const Magnum::VertexFormat format, const UnsignedInt offset) && { + return std::move(addAttribute(location, binding, format, offset)); +} + Debug& operator<<(Debug& debug, const MeshPrimitive value) { debug << "Vk::MeshPrimitive" << Debug::nospace; diff --git a/src/Magnum/Vk/MeshLayout.h b/src/Magnum/Vk/MeshLayout.h index 86218823e..d27ee00d4 100644 --- a/src/Magnum/Vk/MeshLayout.h +++ b/src/Magnum/Vk/MeshLayout.h @@ -307,7 +307,9 @@ class MAGNUM_VK_EXPORT MeshLayout { * * @see @ref addInstancedBinding() */ - MeshLayout& addBinding(UnsignedInt binding, UnsignedInt stride); + MeshLayout& addBinding(UnsignedInt binding, UnsignedInt stride) &; + /** @overload */ + MeshLayout&& addBinding(UnsignedInt binding, UnsignedInt stride) &&; /** * @brief Add an instanced buffer binding @@ -344,7 +346,9 @@ class MAGNUM_VK_EXPORT MeshLayout { * @requires_vk_feature @ref DeviceFeature::VertexAttributeInstanceRateZeroDivisor * if @p divisor is `0` */ - MeshLayout& addInstancedBinding(UnsignedInt binding, UnsignedInt stride, UnsignedInt divisor = 1); + MeshLayout& addInstancedBinding(UnsignedInt binding, UnsignedInt stride, UnsignedInt divisor = 1) &; + /** @overload */ + MeshLayout&& addInstancedBinding(UnsignedInt binding, UnsignedInt stride, UnsignedInt divisor = 1) &&; /** * @brief Add an attribute @@ -368,9 +372,13 @@ class MAGNUM_VK_EXPORT MeshLayout { * - `format` * - `offset` */ - MeshLayout& addAttribute(UnsignedInt location, UnsignedInt binding, VertexFormat format, UnsignedInt offset); + MeshLayout& addAttribute(UnsignedInt location, UnsignedInt binding, VertexFormat format, UnsignedInt offset) &; + /** @overload */ + MeshLayout&& addAttribute(UnsignedInt location, UnsignedInt binding, VertexFormat format, UnsignedInt offset) &&; + /** @overload */ + MeshLayout& addAttribute(UnsignedInt location, UnsignedInt binding, Magnum::VertexFormat format, UnsignedInt offset) &; /** @overload */ - MeshLayout& addAttribute(UnsignedInt location, UnsignedInt binding, Magnum::VertexFormat format, UnsignedInt offset); + MeshLayout&& addAttribute(UnsignedInt location, UnsignedInt binding, Magnum::VertexFormat format, UnsignedInt offset) &&; /** @brief Underlying @type_vk{PipelineVertexInputStateCreateInfo} structure */ VkPipelineVertexInputStateCreateInfo& vkPipelineVertexInputStateCreateInfo() { diff --git a/src/Magnum/Vk/Test/MeshLayoutTest.cpp b/src/Magnum/Vk/Test/MeshLayoutTest.cpp index d7634e9d1..1520926d7 100644 --- a/src/Magnum/Vk/Test/MeshLayoutTest.cpp +++ b/src/Magnum/Vk/Test/MeshLayoutTest.cpp @@ -57,6 +57,8 @@ struct MeshLayoutTest: TestSuite::Tester { template void addAttribute(); void addAttributeWrongOrder(); + void rvalue(); + void compare(); void compareExternalPointers(); @@ -84,6 +86,8 @@ MeshLayoutTest::MeshLayoutTest() { &MeshLayoutTest::addAttribute, &MeshLayoutTest::addAttributeWrongOrder, + &MeshLayoutTest::rvalue, + &MeshLayoutTest::compare, &MeshLayoutTest::compareExternalPointers, @@ -383,6 +387,19 @@ void MeshLayoutTest::addAttributeWrongOrder() { "Vk::MeshLayout::addAttribute(): location 5 can't be ordered after 5\n"); } +void MeshLayoutTest::rvalue() { + MeshLayout&& layout = MeshLayout{MeshPrimitive::TriangleFan} + .addBinding(0, 37) + .addInstancedBinding(1, 26) + .addAttribute(0, 0, VertexFormat{}, 0) + .addAttribute(1, 1, Magnum::VertexFormat::Vector2, 0); + + /* Just to test something, main point is that the above compiles, links and + returns a &&. Can't test anything related to the contents because the + destructor gets called at the end of the expression. */ + CORRADE_VERIFY(&layout); +} + void MeshLayoutTest::compare() { MeshLayout emptyTriangles1{MeshPrimitive::Triangles}; MeshLayout emptyTriangles2{MeshPrimitive::Triangles}; From d4ab9f4cb09052b7bdc0bedd376666ad522a49a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 9 Feb 2021 00:08:03 +0100 Subject: [PATCH 12/44] Vk: implemented a Mesh wrapper. Together with: * CommandBuffer::draw() * Support for indexed and non-indexed meshes * Support for setting primitive and stride dynamically I took one shortcut and vkCmdBindVertexBuffers() is currently called once for each binding. The interface is ready for this, but I'm not yet 100% sure how to test that it actually does batch the buffers, so it's left at the lazy implementation for now. --- doc/changelog.dox | 5 + doc/snippets/MagnumVk.cpp | 150 ++- doc/vulkan-mapping.dox | 11 +- doc/vulkan-support.dox | 2 +- package/ci/unix-desktop-vulkan.sh | 2 +- src/Magnum/Mesh.h | 13 +- src/Magnum/Vk/CMakeLists.txt | 2 + src/Magnum/Vk/CommandBuffer.h | 13 + src/Magnum/Vk/DeviceFeatures.h | 2 +- src/Magnum/Vk/Enums.cpp | 24 +- src/Magnum/Vk/Enums.h | 32 +- src/Magnum/Vk/Implementation/DeviceState.cpp | 6 + src/Magnum/Vk/Implementation/DeviceState.h | 2 + src/Magnum/Vk/Mesh.cpp | 250 +++++ src/Magnum/Vk/Mesh.h | 406 ++++++++ src/Magnum/Vk/MeshLayout.h | 14 +- .../Vk/RasterizationPipelineCreateInfo.h | 9 +- src/Magnum/Vk/Test/CMakeLists.txt | 49 +- src/Magnum/Vk/Test/EnumsTest.cpp | 78 +- src/Magnum/Vk/Test/MeshTest.cpp | 278 ++++++ src/Magnum/Vk/Test/MeshTestFiles/convert.sh | 5 + src/Magnum/Vk/Test/MeshTestFiles/flat.spv | Bin 0 -> 444 bytes src/Magnum/Vk/Test/MeshTestFiles/flat.spvasm | 35 + src/Magnum/Vk/Test/MeshTestFiles/flat.tga | Bin 0 -> 338 bytes src/Magnum/Vk/Test/MeshTestFiles/noop.spv | Bin 0 -> 360 bytes src/Magnum/Vk/Test/MeshTestFiles/noop.spvasm | 30 + src/Magnum/Vk/Test/MeshTestFiles/noop.tga | Bin 0 -> 178 bytes .../Vk/Test/MeshTestFiles/nullcolor.tga | Bin 0 -> 338 bytes .../Vk/Test/MeshTestFiles/vertexcolor.spv | Bin 0 -> 536 bytes .../Vk/Test/MeshTestFiles/vertexcolor.spvasm | 40 + .../Vk/Test/MeshTestFiles/vertexcolor.tga | Bin 0 -> 1362 bytes src/Magnum/Vk/Test/MeshVkTest.cpp | 878 ++++++++++++++++++ src/Magnum/Vk/Test/configure.h.cmake | 2 + src/Magnum/Vk/VertexFormat.h | 1 + src/Magnum/Vk/Vk.h | 2 + 35 files changed, 2182 insertions(+), 159 deletions(-) create mode 100644 src/Magnum/Vk/Mesh.cpp create mode 100644 src/Magnum/Vk/Mesh.h create mode 100644 src/Magnum/Vk/Test/MeshTest.cpp create mode 100755 src/Magnum/Vk/Test/MeshTestFiles/convert.sh create mode 100644 src/Magnum/Vk/Test/MeshTestFiles/flat.spv create mode 100644 src/Magnum/Vk/Test/MeshTestFiles/flat.spvasm create mode 100644 src/Magnum/Vk/Test/MeshTestFiles/flat.tga create mode 100644 src/Magnum/Vk/Test/MeshTestFiles/noop.spv create mode 100644 src/Magnum/Vk/Test/MeshTestFiles/noop.spvasm create mode 100644 src/Magnum/Vk/Test/MeshTestFiles/noop.tga create mode 100644 src/Magnum/Vk/Test/MeshTestFiles/nullcolor.tga create mode 100644 src/Magnum/Vk/Test/MeshTestFiles/vertexcolor.spv create mode 100644 src/Magnum/Vk/Test/MeshTestFiles/vertexcolor.spvasm create mode 100644 src/Magnum/Vk/Test/MeshTestFiles/vertexcolor.tga create mode 100644 src/Magnum/Vk/Test/MeshVkTest.cpp diff --git a/doc/changelog.dox b/doc/changelog.dox index b6bc3b172..b5249d819 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -378,6 +378,11 @@ See also: @ref Vk::pixelFormat(Magnum::CompressedPixelFormat) that return the new @ref Vk::PixelFormat enum that contains only values suitable for a pixel format +- @cpp Vk::hasVkIndexType() @ce and @cpp Vk::vkIndexType() @ce returning a + raw @type_vk{IndexType} are deprecated in favor of + @ref Vk::meshIndexType() that returns the new @ref Vk::MeshIndexType enum. + Since all generic index types are available in Vulkan now, there's no need + for a @cpp hasMeshIndexType() @ce anymore. - @cpp Vk::hasVkPrimitiveTopology() @ce and @cpp Vk::vkPrimitiveTopology() @ce returning a raw @type_vk{PrimitiveToplogy} are deprecated in favor of diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index c6a7e811b..414ad47f5 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -51,7 +51,7 @@ #include "Magnum/Vk/ImageViewCreateInfo.h" #include "Magnum/Vk/LayerProperties.h" #include "Magnum/Vk/MemoryAllocateInfo.h" -#include "Magnum/Vk/MeshLayout.h" +#include "Magnum/Vk/Mesh.h" #include "Magnum/Vk/Pipeline.h" #include "Magnum/Vk/PipelineLayout.h" #include "Magnum/Vk/PixelFormat.h" @@ -774,25 +774,153 @@ indices.bindMemory(memory, indicesOffset); { /* [MeshLayout-usage] */ -constexpr UnsignedInt BufferBinding = 0; +constexpr UnsignedInt Binding = 0; constexpr UnsignedInt PositionLocation = 0; -constexpr UnsignedInt TextureCoordinateLocation = 1; +constexpr UnsignedInt TextureLocation = 1; constexpr UnsignedInt NormalLocation = 5; Vk::MeshLayout meshLayout{MeshPrimitive::Triangles}; meshLayout - .addBinding(BufferBinding, - sizeof(Vector3) + sizeof(Vector2) + sizeof(Vector3)) - .addAttribute(PositionLocation, BufferBinding, VertexFormat::Vector3, - 0) - .addAttribute(TextureCoordinateLocation, BufferBinding, VertexFormat::Vector2, - sizeof(Vector3)) - .addAttribute(NormalLocation, BufferBinding, VertexFormat::Vector3, - sizeof(Vector3) + sizeof(Vector2)); + .addBinding(Binding, 8*sizeof(Float)) + .addAttribute(PositionLocation, Binding, VertexFormat::Vector3, 0) + .addAttribute(TextureLocation, Binding, VertexFormat::Vector2, 3*sizeof(Float)) + .addAttribute(NormalLocation, Binding, VertexFormat::Vector3, 5*sizeof(Float)); /* [MeshLayout-usage] */ } +{ +constexpr UnsignedInt Binding = 0; +constexpr UnsignedInt PositionLocation = 0; +constexpr UnsignedInt TextureLocation = 1; +constexpr UnsignedInt NormalLocation = 5; +UnsignedInt vertexCount = 35, indexCount = 48; +Vk::Device device{NoCreate}; +/* [Mesh-populating] */ +Vk::MeshLayout meshLayout{MeshPrimitive::Triangles}; +meshLayout + .addBinding(Binding, 8*sizeof(Float)) + .addAttribute(PositionLocation, Binding, VertexFormat::Vector3, 0) + .addAttribute(TextureLocation, Binding, VertexFormat::Vector2, 3*sizeof(Float)) + .addAttribute(NormalLocation, Binding, VertexFormat::Vector3, 5*sizeof(Float)); + +Vk::Buffer vertices{DOXYGEN_IGNORE(device), Vk::BufferCreateInfo{ + Vk::BufferUsage::VertexBuffer, vertexCount*8*sizeof(Float) +}, DOXYGEN_IGNORE(NoAllocate)}; + +DOXYGEN_IGNORE() + +Vk::Mesh mesh{meshLayout}; +mesh.addVertexBuffer(Binding, vertices, 0) + .setCount(vertexCount); +/* [Mesh-populating] */ + +/* [Mesh-populating-indexed] */ +Vk::Buffer indices{DOXYGEN_IGNORE(device), Vk::BufferCreateInfo{ + Vk::BufferUsage::IndexBuffer, indexCount*sizeof(UnsignedShort) +}, DOXYGEN_IGNORE(NoAllocate)}; + +DOXYGEN_IGNORE() + +mesh.setIndexBuffer(indices, 0, MeshIndexType::UnsignedShort) + .setCount(indexCount); +/* [Mesh-populating-indexed] */ +} + +{ +Vk::Device device{NoCreate}; +/* [Mesh-populating-owned] */ +Vk::Buffer buffer{DOXYGEN_IGNORE(device), Vk::BufferCreateInfo{ + Vk::BufferUsage::VertexBuffer|Vk::BufferUsage::IndexBuffer, DOXYGEN_IGNORE(0) +}, DOXYGEN_IGNORE(NoAllocate)}; + +DOXYGEN_IGNORE() + +Vk::Mesh mesh{Vk::MeshLayout{MeshPrimitive::Triangles} + .addBinding(DOXYGEN_IGNORE(0, 0)) + DOXYGEN_IGNORE() +}; +mesh.addVertexBuffer(DOXYGEN_IGNORE(0), buffer, DOXYGEN_IGNORE(0)) + .setIndexBuffer(std::move(buffer), DOXYGEN_IGNORE(0, MeshIndexType{})) + .setCount(DOXYGEN_IGNORE(0)); +/* [Mesh-populating-owned] */ +} + +{ +Vk::Device device{NoCreate}; +Vk::CommandBuffer cmd{NoCreate}; +Vk::ShaderSet shaderSet; +Vk::PipelineLayout pipelineLayout{NoCreate}; +Vk::RenderPass renderPass{NoCreate}; +/* [Mesh-drawing] */ +Vk::Mesh mesh{DOXYGEN_IGNORE(Vk::MeshLayout{MeshPrimitive{}})}; + +Vk::Pipeline pipeline{DOXYGEN_IGNORE(device), Vk::RasterizationPipelineCreateInfo{ + DOXYGEN_IGNORE(shaderSet), mesh.layout(), DOXYGEN_IGNORE(pipelineLayout, renderPass, 0, 1) + }DOXYGEN_IGNORE() +}; + +DOXYGEN_IGNORE() + +cmd.bindPipeline(pipeline) + .draw(mesh); +/* [Mesh-drawing] */ +} + +{ +Vk::Device device{NoCreate}; +Vk::CommandBuffer cmd{NoCreate}; +Vk::ShaderSet shaderSet; +Vk::PipelineLayout pipelineLayout{NoCreate}; +Vk::RenderPass renderPass{NoCreate}; +constexpr UnsignedInt PositionLocation = 0; +constexpr UnsignedInt TextureLocation = 1; +constexpr UnsignedInt NormalLocation = 5; +/* [Mesh-drawing-dynamic] */ +/* Use zero stride and zero offsets, as the stride gets specified dynamically + and offsets specified in concrete buffer bindings instead */ +Vk::MeshLayout dynamicMeshLayout{MeshPrimitive::Triangles}; +dynamicMeshLayout + .addBinding(0, 0) + .addBinding(1, 0) + .addBinding(2, 0) + .addAttribute(PositionLocation, 0, VertexFormat::Vector3, 0) + .addAttribute(TextureLocation, 1, VertexFormat::Vector2, 0) + .addAttribute(NormalLocation, 2, VertexFormat::Vector3, 0); + +Vk::Pipeline pipeline{DOXYGEN_IGNORE(device), Vk::RasterizationPipelineCreateInfo{ + DOXYGEN_IGNORE(shaderSet), dynamicMeshLayout, DOXYGEN_IGNORE(pipelineLayout, renderPass, 0, 1)} + /* Enable dynamic primitive and stride */ + .setDynamicStates(Vk::DynamicRasterizationState::MeshPrimitive| + Vk::DynamicRasterizationState::VertexInputBindingStride) + DOXYGEN_IGNORE() +}; + +Vk::Buffer vertices{DOXYGEN_IGNORE(NoCreate)}; + +Vk::Mesh mesh{Vk::MeshLayout{MeshPrimitive::Triangles} /* Or TriangleStrip etc */ + /* Concrete stride */ + .addBinding(0, 8*sizeof(Float)) + .addBinding(1, 8*sizeof(Float)) + .addBinding(2, 8*sizeof(Float)) + /* Rest the same as in the dynamicMeshLayout */ + .addAttribute(PositionLocation, 0, VertexFormat::Vector3, 0) + .addAttribute(TextureLocation, 1, VertexFormat::Vector2, 0) + .addAttribute(NormalLocation, 2, VertexFormat::Vector3, 0) +}; + +/* Bind the same buffer to three different bindings, with concrete offsets */ +mesh.addVertexBuffer(0, vertices, 0) + .addVertexBuffer(1, vertices, 3*sizeof(Float)) + .addVertexBuffer(2, vertices, 5*sizeof(Float)) + .setCount(DOXYGEN_IGNORE(0)); + +cmd.bindPipeline(pipeline) + /* Updates the dynamic primitive and stride as needed by the mesh */ + .draw(mesh); +/* [Mesh-drawing-dynamic] */ +} + { Vk::Device device{NoCreate}; /* The include should be a no-op here since it was already included above */ diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index d200e5177..43d5db142 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -113,9 +113,9 @@ Vulkan function | Matching API @fn_vk{CmdBeginDebugUtilsLabelEXT} @m_class{m-label m-flat m-warning} **EXT**, \n @fn_vk{CmdEndDebugUtilsLabelEXT} @m_class{m-label m-flat m-warning} **EXT** | | @fn_vk{CmdBeginRenderPass}, \n @fn_vk{CmdBeginRenderPass2} @m_class{m-label m-flat m-success} **KHR, 1.2**, \n @fn_vk{CmdNextSubpass}, \n @fn_vk{CmdNextSubpass2} @m_class{m-label m-flat m-success} **KHR, 1.2**, \n @fn_vk{CmdEndRenderpass}, \n @fn_vk{CmdEndRenderpass2} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref CommandBuffer::beginRenderPass(), \n @ref CommandBuffer::nextSubpass(), \n @ref CommandBuffer::endRenderPass() @fn_vk{CmdBindDescriptorSets} | | -@fn_vk{CmdBindIndexBuffer} | | +@fn_vk{CmdBindIndexBuffer} | internal to @ref CommandBuffer::draw() @fn_vk{CmdBindPipeline} | @ref CommandBuffer::bindPipeline() -@fn_vk{CmdBindVertexBuffers}, \n @fn_vk{CmdBindVertexBuffers2EXT} @m_class{m-label m-flat m-warning} **EXT** | | +@fn_vk{CmdBindVertexBuffers}, \n @fn_vk{CmdBindVertexBuffers2EXT} @m_class{m-label m-flat m-warning} **EXT** | internal to @ref CommandBuffer::draw() @fn_vk{CmdBlitImage}, \n @fn_vk{CmdBlitImage2KHR} @m_class{m-label m-flat m-warning} **KHR** | | @fn_vk{CmdBuildAccelerationStructuresIndirectKHR} @m_class{m-label m-flat m-warning} **KHR** | | @fn_vk{CmdBuildAccelerationStructuresKHR} @m_class{m-label m-flat m-warning} **KHR** | | @@ -135,8 +135,7 @@ Vulkan function | Matching API @fn_vk{CmdDispatch} | | @fn_vk{CmdDispatchBase} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{CmdDispatchIndirect} | | -@fn_vk{CmdDraw} | | -@fn_vk{CmdDrawIndexed} | | +@fn_vk{CmdDraw}, \n @fn_vk{CmdDrawIndexed} | @ref CommandBuffer::draw() @fn_vk{CmdDrawIndexedIndirect} | | @fn_vk{CmdDrawIndexedIndirectCount} @m_class{m-label m-flat m-success} **KHR, 1.2** | | @fn_vk{CmdDrawIndirect} | | @@ -161,7 +160,7 @@ Vulkan function | Matching API @fn_vk{CmdSetEvent} | | @fn_vk{CmdSetFrontFaceEXT} @m_class{m-label m-flat m-warning} **EXT** | | @fn_vk{CmdSetLineWidth} | | -@fn_vk{CmdSetPrimitiveTopologyEXT} @m_class{m-label m-flat m-warning} **EXT** | | +@fn_vk{CmdSetPrimitiveTopologyEXT} @m_class{m-label m-flat m-warning} **EXT** | internal to @ref CommandBuffer::draw() @fn_vk{CmdSetRayTracingPipelineStackSizeKHR} @m_class{m-label m-flat m-warning} **KHR** | | @fn_vk{CmdSetScissor} | | @fn_vk{CmdSetScissorWithCountEXT} @m_class{m-label m-flat m-warning} **EXT** | | @@ -881,7 +880,7 @@ Vulkan enum | Matching API @type_vk{ImageType} | not exposed, internal to @ref ImageCreateInfo subclasses @type_vk{ImageUsageFlagBits}, \n @type_vk{ImageUsageFlags} | @ref ImageUsage, \n @ref ImageUsages @type_vk{ImageViewType} | not exposed, internal to @ref ImageViewCreateInfo subclasses -@type_vk{IndexType} | only @ref vkIndexType() +@type_vk{IndexType} | @ref MeshIndexType @type_vk{InternalAllocationType} | @ref vulkan-wrapping-host-allocation "not exposed" @subsection vulkan-mapping-enums-l L diff --git a/doc/vulkan-support.dox b/doc/vulkan-support.dox index 0f2defdf7..9791802f3 100644 --- a/doc/vulkan-support.dox +++ b/doc/vulkan-support.dox @@ -122,7 +122,7 @@ Extension | Status @vk_extension{EXT,validation_features} @m_class{m-label m-info} **instance** | | @vk_extension{EXT,vertex_attribute_divisor} | done @vk_extension{EXT,index_type_uint8} | @ref Vk::vkIndexType() only -@vk_extension{EXT,extended_dynamic_state} | | +@vk_extension{EXT,extended_dynamic_state} | only dynamic primitive and stride @vk_extension{EXT,robustness2} | done except properties @vk_extension{EXT,image_robustness} | done @vk_extension{KHR,acceleration_structure} | | diff --git a/package/ci/unix-desktop-vulkan.sh b/package/ci/unix-desktop-vulkan.sh index 8747b9dd3..c15d48445 100755 --- a/package/ci/unix-desktop-vulkan.sh +++ b/package/ci/unix-desktop-vulkan.sh @@ -32,7 +32,7 @@ cmake .. \ -DCMAKE_INSTALL_PREFIX=$HOME/deps \ -DCMAKE_BUILD_TYPE=Debug \ -DWITH_AUDIO=OFF \ - -DWITH_DEBUGTOOLS=OFF \ + -DWITH_DEBUGTOOLS=ON \ -DWITH_GL=OFF \ -DWITH_MESHTOOLS=OFF \ -DWITH_PRIMITIVES=OFF \ diff --git a/src/Magnum/Mesh.h b/src/Magnum/Mesh.h index 620f914fb..36b4b9351 100644 --- a/src/Magnum/Mesh.h +++ b/src/Magnum/Mesh.h @@ -240,10 +240,9 @@ In case of OpenGL, corresponds to @ref GL::MeshIndexType and is convertible to it using @ref GL::meshIndexType(). See documentation of each value for more information about the mapping. -In case of Vulkan, corresponds to @type_vk_keyword{IndexType} and is -convertible to it using @ref Vk::vkIndexType(). See documentation of each value -for more information about the mapping. Note that not every type is available -there, use @ref Vk::hasVkIndexType() to check for its presence. +In case of Vulkan, corresponds to @ref Vk::MeshIndexType and is convertible to +it using @ref Vk::meshIndexType(). See documentation of each value for more +information about the mapping. @see @ref meshIndexTypeSize() */ enum class MeshIndexType: UnsignedByte { @@ -253,7 +252,7 @@ enum class MeshIndexType: UnsignedByte { * Unsigned byte * * Corresponds to @ref GL::MeshIndexType::UnsignedByte / - * @val_vk_keyword{INDEX_TYPE_UINT8_EXT,IndexType}. Note that using this + * @ref Vk::MeshIndexTyúe::UnsignedByte. Note that using this * type is discouraged, at least AMD GPUs are known to suggest (via debug * output) using 16-byte types instead for better efficiency. */ @@ -263,7 +262,7 @@ enum class MeshIndexType: UnsignedByte { * Unsigned short * * Corresponds to @ref GL::MeshIndexType::UnsignedShort / - * @val_vk_keyword{INDEX_TYPE_UINT16,IndexType}. + * @ref Vk::MeshIndexType::UnsignedShort. */ UnsignedShort, @@ -271,7 +270,7 @@ enum class MeshIndexType: UnsignedByte { * Unsigned int * * Corresponds to @ref GL::MeshIndexType::UnsignedInt / - * @val_vk_keyword{INDEX_TYPE_UINT32,IndexType}. + * @ref Vk::MeshIndexType::UnsignedInt. */ UnsignedInt }; diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index b5f3c79d3..5b6701875 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -55,6 +55,7 @@ set(MagnumVk_GracefulAssert_SRCS ImageView.cpp Instance.cpp LayerProperties.cpp + Mesh.cpp MeshLayout.cpp Memory.cpp Pipeline.cpp @@ -93,6 +94,7 @@ set(MagnumVk_HEADERS LayerProperties.h Memory.h MemoryAllocateInfo.h + Mesh.h MeshLayout.h Pipeline.h PipelineLayout.h diff --git a/src/Magnum/Vk/CommandBuffer.h b/src/Magnum/Vk/CommandBuffer.h index 2ebd3345a..0fb488259 100644 --- a/src/Magnum/Vk/CommandBuffer.h +++ b/src/Magnum/Vk/CommandBuffer.h @@ -381,6 +381,16 @@ class MAGNUM_VK_EXPORT CommandBuffer { */ CommandBuffer& bindPipeline(Pipeline& pipeline); + /** + * @brief Draw a mesh + * @return Reference to self (for method chaining) + * + * Can be only called inside a render pass with a graphics pipeline + * bound. See @ref Vk-Mesh-drawing for a usage example. + * @see @fn_vk_keyword{CmdDraw}, @fn_vk_keyword{CmdDrawIndexed} + */ + CommandBuffer& draw(Mesh& mesh); + /** * @brief Insert an execution barrier with optional memory dependencies * @param sourceStages Source stages. Has to contain at least @@ -785,6 +795,9 @@ class MAGNUM_VK_EXPORT CommandBuffer { MAGNUM_VK_LOCAL static void endRenderPassImplementationKHR(CommandBuffer& self, const VkSubpassEndInfo& endInfo); MAGNUM_VK_LOCAL static void endRenderPassImplementation12(CommandBuffer& self, const VkSubpassEndInfo& endInfo); + MAGNUM_VK_LOCAL static void bindVertexBuffersImplementationDefault(CommandBuffer& self, UnsignedInt firstBinding, UnsignedInt bindingCount, const VkBuffer* buffers, const UnsignedLong* offsets, const UnsignedLong* strides); + MAGNUM_VK_LOCAL static void bindVertexBuffersImplementationEXT(CommandBuffer& self, UnsignedInt firstBinding, UnsignedInt bindingCount, const VkBuffer* buffers, const UnsignedLong* offsets, const UnsignedLong* strides); + MAGNUM_VK_LOCAL static void copyBufferImplementationDefault(CommandBuffer& self, const CopyBufferInfo& info); MAGNUM_VK_LOCAL static void copyBufferImplementationKHR(CommandBuffer& self, const CopyBufferInfo& info); diff --git a/src/Magnum/Vk/DeviceFeatures.h b/src/Magnum/Vk/DeviceFeatures.h index ca6d4bc00..1d5d4c34c 100644 --- a/src/Magnum/Vk/DeviceFeatures.h +++ b/src/Magnum/Vk/DeviceFeatures.h @@ -70,7 +70,7 @@ enum class DeviceFeature: UnsignedShort { /** * Whether the full 32-bit range is supported for indexed draw calls when - * using @val_vk{INDEX_TYPE_UINT32,IndexType}. + * using @ref MeshIndexType::UnsignedInt. * @see @ref DeviceFeature::IndexTypeUnsignedByte * @todo expose the `maxDrawIndexedIndexValue` limit */ diff --git a/src/Magnum/Vk/Enums.cpp b/src/Magnum/Vk/Enums.cpp index 805c20469..443873d07 100644 --- a/src/Magnum/Vk/Enums.cpp +++ b/src/Magnum/Vk/Enums.cpp @@ -32,6 +32,7 @@ #ifdef MAGNUM_BUILD_DEPRECATED #include "Magnum/Vk/MeshLayout.h" +#include "Magnum/Vk/Mesh.h" #include "Magnum/Vk/PixelFormat.h" #include "Magnum/Vk/VertexFormat.h" #endif @@ -40,12 +41,6 @@ namespace Magnum { namespace Vk { namespace { -constexpr VkIndexType IndexTypeMapping[]{ - VK_INDEX_TYPE_UINT8_EXT, - VK_INDEX_TYPE_UINT16, - VK_INDEX_TYPE_UINT32 -}; - constexpr VkFilter FilterMapping[]{ VK_FILTER_NEAREST, VK_FILTER_LINEAR @@ -75,24 +70,15 @@ bool hasVkPrimitiveTopology(const Magnum::MeshPrimitive primitive) { VkPrimitiveTopology vkPrimitiveTopology(const Magnum::MeshPrimitive primitive) { return VkPrimitiveTopology(meshPrimitive(primitive)); } -#endif -bool hasVkIndexType(const Magnum::MeshIndexType type) { - CORRADE_ASSERT(UnsignedInt(type) - 1 < Containers::arraySize(IndexTypeMapping), - "Vk::hasVkIndexType(): invalid type" << type, {}); - return UnsignedInt(IndexTypeMapping[UnsignedInt(type) - 1]) != ~UnsignedInt{}; +bool hasVkIndexType(const Magnum::MeshIndexType) { + return true; } VkIndexType vkIndexType(const Magnum::MeshIndexType type) { - CORRADE_ASSERT(UnsignedInt(type) - 1 < Containers::arraySize(IndexTypeMapping), - "Vk::vkIndexType(): invalid type" << type, {}); - const VkIndexType out = IndexTypeMapping[UnsignedInt(type) - 1]; - CORRADE_ASSERT(out != VkIndexType(~UnsignedInt{}), - "Vk::vkIndexType(): unsupported type" << type, {}); - return out; + return VkIndexType(meshIndexType(type)); } -#ifdef MAGNUM_BUILD_DEPRECATED bool hasVkFormat(const Magnum::VertexFormat format) { return hasVertexFormat(format); } @@ -104,9 +90,7 @@ bool hasVkFormat(const Magnum::PixelFormat format) { bool hasVkFormat(const Magnum::CompressedPixelFormat format) { return hasPixelFormat(format); } -#endif -#ifdef MAGNUM_BUILD_DEPRECATED VkFormat vkFormat(const Magnum::VertexFormat format) { return VkFormat(vertexFormat(format)); } diff --git a/src/Magnum/Vk/Enums.h b/src/Magnum/Vk/Enums.h index f976dfcca..0d67bc260 100644 --- a/src/Magnum/Vk/Enums.h +++ b/src/Magnum/Vk/Enums.h @@ -51,33 +51,19 @@ CORRADE_DEPRECATED("use hasMeshPrimitive() instead") MAGNUM_VK_EXPORT bool hasVk * @m_deprecated_since_latest Use @ref meshPrimitive() instead. */ CORRADE_DEPRECATED("use meshPrimitive() instead") MAGNUM_VK_EXPORT VkPrimitiveTopology vkPrimitiveTopology(Magnum::MeshPrimitive primitive); -#endif /** -@brief Check availability of a generic index type - -Returns @cpp false @ce if Vulkan doesn't support such type, @cpp true @ce -otherwise. The @p type value is expected to be valid. - -@note Support of some types depends on presence of a particular Vulkan - extension. Such check is outside of the scope of this function and you are - expected to verify extension availability before using such type. - -@see @ref vkIndexType(), @vk_extension{EXT,index_type_uint8} -*/ -MAGNUM_VK_EXPORT bool hasVkIndexType(Magnum::MeshIndexType type); + * @brief Check availability of a generic index type + * @m_deprecated_since_latest All generic index types are available in Vulkan. + */ +CORRADE_DEPRECATED("all generic index types are available in Vulkan") MAGNUM_VK_EXPORT bool hasVkIndexType(Magnum::MeshIndexType type); /** -@brief Convert generic mesh index type to Vulkan mesh index type - -Not all generic index types have a Vulkan equivalent and this function expects -that given type is available. Use @ref hasVkIndexType() to query availability -of given index type. -@see @ref vkPrimitiveTopology() -*/ -MAGNUM_VK_EXPORT VkIndexType vkIndexType(Magnum::MeshIndexType type); + * @brief @copybrief meshIndexType() + * @m_deprecated_since_latest Use @ref meshIndexType() instead. + */ +CORRADE_DEPRECATED("use meshIndexType() instead") MAGNUM_VK_EXPORT VkIndexType vkIndexType(Magnum::MeshIndexType type); -#ifdef MAGNUM_BUILD_DEPRECATED /** * @brief @copybrief hasVertexFormat() * @m_deprecated_since_latest Use @ref hasVertexFormat() instead. @@ -97,9 +83,7 @@ CORRADE_DEPRECATED("use hasPixelFormat() instead") MAGNUM_VK_EXPORT bool hasVkFo * instead. */ CORRADE_DEPRECATED("use hasPixelFormat() instead") MAGNUM_VK_EXPORT bool hasVkFormat(Magnum::CompressedPixelFormat format); -#endif -#ifdef MAGNUM_BUILD_DEPRECATED /** * @brief @copybrief vertexFormat() * @m_deprecated_since_latest Use @ref vertexFormat() instead. diff --git a/src/Magnum/Vk/Implementation/DeviceState.cpp b/src/Magnum/Vk/Implementation/DeviceState.cpp index ffbdd3e4b..93c01d05f 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.cpp +++ b/src/Magnum/Vk/Implementation/DeviceState.cpp @@ -85,6 +85,12 @@ DeviceState::DeviceState(Device& device, Containers::Array()) { + cmdBindVertexBuffersImplementation = &CommandBuffer::bindVertexBuffersImplementationEXT; + } else { + cmdBindVertexBuffersImplementation = &CommandBuffer::bindVertexBuffersImplementationDefault; + } + if(device.isExtensionEnabled()) { cmdCopyBufferImplementation = &CommandBuffer::copyBufferImplementationKHR; cmdCopyImageImplementation = &CommandBuffer::copyImageImplementationKHR; diff --git a/src/Magnum/Vk/Implementation/DeviceState.h b/src/Magnum/Vk/Implementation/DeviceState.h index 7bd797646..583fabaa6 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.h +++ b/src/Magnum/Vk/Implementation/DeviceState.h @@ -56,6 +56,8 @@ struct DeviceState { void(*cmdNextSubpassImplementation)(CommandBuffer&, const VkSubpassEndInfo&, const VkSubpassBeginInfo&); void(*cmdEndRenderPassImplementation)(CommandBuffer&, const VkSubpassEndInfo&); + void(*cmdBindVertexBuffersImplementation)(CommandBuffer&, UnsignedInt, UnsignedInt, const VkBuffer*, const UnsignedLong*, const UnsignedLong*); + void(*cmdCopyBufferImplementation)(CommandBuffer&, const CopyBufferInfo&); void(*cmdCopyImageImplementation)(CommandBuffer&, const CopyImageInfo&); void(*cmdCopyBufferToImageImplementation)(CommandBuffer&, const CopyBufferToImageInfo&); diff --git a/src/Magnum/Vk/Mesh.cpp b/src/Magnum/Vk/Mesh.cpp new file mode 100644 index 000000000..d174419df --- /dev/null +++ b/src/Magnum/Vk/Mesh.cpp @@ -0,0 +1,250 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + 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. +*/ + +#include "Mesh.h" +#include "CommandBuffer.h" + +#include + +#include "Magnum/Mesh.h" +#include "Magnum/Vk/Buffer.h" +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/MeshLayout.h" +#include "Magnum/Vk/RasterizationPipelineCreateInfo.h" +#include "Magnum/Vk/Implementation/DeviceState.h" + +namespace Magnum { namespace Vk { + +namespace { + +constexpr MeshIndexType IndexTypeMapping[]{ + MeshIndexType::UnsignedByte, + MeshIndexType::UnsignedShort, + MeshIndexType::UnsignedInt +}; + +} + +MeshIndexType meshIndexType(const Magnum::MeshIndexType type) { + CORRADE_ASSERT(UnsignedInt(type) - 1 < Containers::arraySize(IndexTypeMapping), + "Vk::meshIndexType(): invalid type" << type, {}); + return IndexTypeMapping[UnsignedInt(type) - 1]; +} + +Debug& operator<<(Debug& debug, const MeshIndexType value) { + debug << "Vk::MeshIndexType" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case Vk::MeshIndexType::value: return debug << "::" << Debug::nospace << #value; + _c(UnsignedByte) + _c(UnsignedShort) + _c(UnsignedInt) + #undef _c + /* LCOV_EXCL_STOP */ + } + + /* Vulkan docs have the values in decimal, so not converting to hex */ + return debug << "(" << Debug::nospace << Int(value) << Debug::nospace << ")"; +} + +struct Mesh::State { + Containers::ArrayTuple vertexBufferData; + Containers::ArrayView vertexBuffers; + Containers::ArrayView vertexBufferOffsets; + Containers::ArrayView vertexBufferStrides; + Containers::ArrayView ownedVertexBuffers; + + VkBuffer indexBuffer{}; + Buffer ownedIndexBuffer{NoCreate}; + UnsignedLong indexBufferOffset{}; + MeshIndexType indexType{}; +}; + +Mesh::Mesh(const MeshLayout& layout): Mesh{MeshLayout{layout.vkPipelineVertexInputStateCreateInfo(), layout.vkPipelineInputAssemblyStateCreateInfo()}} {} + +Mesh::Mesh(MeshLayout&& layout): _layout{std::move(layout)} { + /* Since we know the count of buffer bindings, we can directly allocate + all needed memory upfront */ + if(const UnsignedInt count = _layout.vkPipelineVertexInputStateCreateInfo().vertexBindingDescriptionCount) { + _state.emplace(); + _state->vertexBufferData = Containers::ArrayTuple{ + {Containers::ValueInit, count, _state->vertexBuffers}, + {Containers::ValueInit, count, _state->vertexBufferOffsets}, + {Containers::ValueInit, count, _state->vertexBufferStrides}, + {Containers::NoInit, count, _state->ownedVertexBuffers} + }; + + /** @tod use DirectInit once ArrayTuple can do that */ + for(Buffer& b: _state->ownedVertexBuffers) new(&b) Buffer{NoCreate}; + } +} + +/* We don't have any internal self-pointers so a default is fine, and + MeshLayout has its own constructor to handle those (if any) */ +Mesh::Mesh(Mesh&&) noexcept = default; + +Mesh::~Mesh() = default; + +Mesh& Mesh::operator=(Mesh&&) noexcept = default; + +UnsignedInt Mesh::count() const { + /* The inverse value is used to detect & assert on forgotten setCount() + in CommandBuffer::draw(), return 0 in that case as well */ + return _count == ~UnsignedInt{} ? 0 : _count; +} + +std::size_t Mesh::addVertexBufferInternal(UnsignedInt binding, VkBuffer buffer, UnsignedLong offset) { + /* Find this binding in the layout */ + for(std::size_t i = 0, max = _layout.vkPipelineVertexInputStateCreateInfo().vertexBindingDescriptionCount; i != max; ++i) { + if(_layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[i].binding == binding) { + _state->vertexBuffers[i] = buffer; + _state->vertexBufferOffsets[i] = offset; + /* Save the stride as well in case a dynamic state would need it */ + _state->vertexBufferStrides[i] = _layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[i].stride; + return i; + } + } + + CORRADE_ASSERT_UNREACHABLE("Vk::Mesh::addVertexBuffer(): binding" << binding << "not present among" << _layout.vkPipelineVertexInputStateCreateInfo().vertexBindingDescriptionCount << "bindings in the layout", ~std::size_t{}); +} + +Mesh& Mesh::addVertexBuffer(const UnsignedInt binding, const VkBuffer buffer, const UnsignedLong offset) { + addVertexBufferInternal(binding, buffer, offset); + return *this; +} + +Mesh& Mesh::addVertexBuffer(const UnsignedInt binding, Buffer&& buffer, const UnsignedLong offset) { + const std::size_t index = addVertexBufferInternal(binding, buffer, offset); + #ifdef CORRADE_GRACEFUL_ASSERT + if(index == ~std::size_t{}) return *this; + #endif + _state->ownedVertexBuffers[index] = std::move(buffer); + return *this; +} + +Mesh& Mesh::setIndexBuffer(const VkBuffer buffer, const UnsignedLong offset, const MeshIndexType indexType) { + /* If the mesh has no vertex buffer bindings, the state isn't populated + in the constructor. Do it here. */ + if(!_state) _state.emplace(); + + _state->indexBuffer = buffer; + _state->indexBufferOffset = offset; + _state->indexType = indexType; + return *this; +} + +Mesh& Mesh::setIndexBuffer(const VkBuffer buffer, const UnsignedLong offset, const Magnum::MeshIndexType indexType) { + return setIndexBuffer(buffer, offset, meshIndexType(indexType)); +} + +Mesh& Mesh::setIndexBuffer(Buffer&& buffer, const UnsignedLong offset, const MeshIndexType indexType) { + setIndexBuffer(buffer, offset, indexType); + _state->ownedIndexBuffer = std::move(buffer); + return *this; +} + +Mesh& Mesh::setIndexBuffer(Buffer&& buffer, const UnsignedLong offset, const Magnum::MeshIndexType indexType) { + return setIndexBuffer(std::move(buffer), offset, meshIndexType(indexType)); +} + +Containers::ArrayView Mesh::vertexBuffers() { + return _state ? _state->vertexBuffers : nullptr; +} + +Containers::ArrayView Mesh::vertexBufferOffsets() const { + return _state ? _state->vertexBufferOffsets : nullptr; +} + +Containers::ArrayView Mesh::vertexBufferStrides() const { + return _state ? _state->vertexBufferStrides : nullptr; +} + +bool Mesh::isIndexed() const { return _state && _state->indexBuffer; } + +VkBuffer Mesh::indexBuffer() { + CORRADE_ASSERT(isIndexed(), "Vk::Mesh::indexBuffer(): the mesh is not indexed", {}); + return _state->indexBuffer; +} + +UnsignedLong Mesh::indexBufferOffset() const { + CORRADE_ASSERT(isIndexed(), "Vk::Mesh::indexBufferOffset(): the mesh is not indexed", {}); + return _state->indexBufferOffset; +} + +MeshIndexType Mesh::indexType() const { + CORRADE_ASSERT(isIndexed(), "Vk::Mesh::indexType(): the mesh is not indexed", {}); + return _state->indexType; +} + +CommandBuffer& CommandBuffer::draw(Mesh& mesh) { + CORRADE_ASSERT(mesh.isCountSet(), + "Vk::CommandBuffer::draw(): Mesh::setCount() was never called, probably a mistake?", *this); + + if(!mesh.count() || !mesh.instanceCount()) return *this; + + if(_dynamicRasterizationStates & DynamicRasterizationState::MeshPrimitive) + (**_device).CmdSetPrimitiveTopologyEXT(_handle, mesh.layout().vkPipelineInputAssemblyStateCreateInfo().topology); + + const VkVertexInputBindingDescription* bindings = mesh.layout().vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions; + for(std::size_t i = 0, max = mesh.layout().vkPipelineVertexInputStateCreateInfo().vertexBindingDescriptionCount; i != max; ++i) { + /** @todo don't call this for each binding, detect ranges */ + _device->state().cmdBindVertexBuffersImplementation(*this, + bindings[i].binding, 1, + mesh.vertexBuffers() + i, + mesh.vertexBufferOffsets() + i, + _dynamicRasterizationStates & DynamicRasterizationState::VertexInputBindingStride ? + mesh.vertexBufferStrides() + i : nullptr + ); + } + + if(mesh.isIndexed()) { + (**_device).CmdBindIndexBuffer(_handle, mesh.indexBuffer(), mesh.indexBufferOffset(), VkIndexType(mesh.indexType())); + (**_device).CmdDrawIndexed(_handle, mesh.count(), mesh.instanceCount(), mesh.indexOffset(), mesh.vertexOffset(), mesh.instanceOffset()); + } else { + (**_device).CmdDraw(_handle, mesh.count(), mesh.instanceCount(), mesh.vertexOffset(), mesh.instanceOffset()); + } + + return *this; +} + +void CommandBuffer::bindVertexBuffersImplementationDefault(CommandBuffer& self, const UnsignedInt firstBinding, const UnsignedInt bindingCount, const VkBuffer* const buffers, const UnsignedLong* const offsets, const UnsignedLong* const strides) { + CORRADE_ASSERT(!strides, + "Vk::CommandBuffer::draw(): dynamic strides supplied for an implementation without extended dynamic state", + /* Calling this even in case the assert blows up to avoid validation + layer errors about unbound attributes when CORRADE_GRACEFUL_ASSERT + is enabled */ + (**self._device).CmdBindVertexBuffers(self, firstBinding, bindingCount, buffers, offsets)); + #ifdef CORRADE_NO_ASSERT + static_cast(strides); + #endif + (**self._device).CmdBindVertexBuffers(self, firstBinding, bindingCount, buffers, offsets); +} + +void CommandBuffer::bindVertexBuffersImplementationEXT(CommandBuffer& self, const UnsignedInt firstBinding, const UnsignedInt bindingCount, const VkBuffer* const buffers, const UnsignedLong* const offsets, const UnsignedLong* const strides) { + return (**self._device).CmdBindVertexBuffers2EXT(self, firstBinding, bindingCount, buffers, offsets, nullptr, strides); +} + +}} diff --git a/src/Magnum/Vk/Mesh.h b/src/Magnum/Vk/Mesh.h new file mode 100644 index 000000000..c6a821ce4 --- /dev/null +++ b/src/Magnum/Vk/Mesh.h @@ -0,0 +1,406 @@ +#ifndef Magnum_Vk_Mesh_h +#define Magnum_Vk_Mesh_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Class @ref Magnum::Vk::Mesh, enum @ref Magnum::Vk::MeshIndexType, function @ref Magnum::Vk::meshIndexType() + * @m_since_latest + */ + +#include + +#include "Magnum/Vk/MeshLayout.h" + +namespace Magnum { namespace Vk { + +/** +@brief Mesh index type +@m_since_latest + +Wraps a @type_vk_keyword{IndexType}. +*/ +enum class MeshIndexType: Int { + /** + * @ref Magnum::UnsignedByte "UnsignedByte" + * @requires_vk_feature @ref DeviceFeature::IndexTypeUnsignedByte + */ + UnsignedByte = VK_INDEX_TYPE_UINT8_EXT, + + /** @ref Magnum::UnsignedShort "UnsignedShort" */ + UnsignedShort = VK_INDEX_TYPE_UINT16, + + /** + * @ref Magnum::UnsignedInt "UnsignedInt" + * @see @ref DeviceFeature::FullDrawIndexUnsignedInt + */ + UnsignedInt = VK_INDEX_TYPE_UINT32 +}; + +/** +@debugoperatorenum{MeshIndexType} +@m_since_latest +*/ +MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, MeshIndexType value); + +/** +@brief Convert a generic index type to Vulkan index type +@m_since_latest + +@see @ref meshPrimitive(), @ref vertexFormat() +*/ +MAGNUM_VK_EXPORT MeshIndexType meshIndexType(Magnum::MeshIndexType type); + +/** +@brief Mesh +@m_since_latest + +Connects @ref MeshLayout with concrete vertex/index @ref Buffer instances and +manages related information such as vertex or instance count. + +@section Vk-Mesh-populating Populating a mesh + +Continuing from the @ref Vk-MeshLayout-usage "mesh layout setup", the @ref Mesh +gets concrete buffers bound using @ref addVertexBuffer() and vertex count +specified with @ref setCount(): + +@snippet MagnumVk.cpp Mesh-populating + +For an indexed mesh, the index buffer can be specified via @ref setIndexBuffer(). +With an index buffer present, @ref setCount() then describes count of indices, +instead of vertices: + +@snippet MagnumVk.cpp Mesh-populating-indexed + +@subsection Vk-Mesh-populating-owned Transferring buffer and layout ownership + +To simplify resource management, it's possible to have the @ref Buffer +instances owned by the @ref Mesh, as well as the @ref MeshLayout, either using +@ref std::move() or by directly passing a r-value. If a single buffer is used +for multiple bindings (for example as both a vertex and an index buffer), +perform the move last: + +@snippet MagnumVk.cpp Mesh-populating-owned + +@section Vk-Mesh-drawing Drawing a mesh + +Assuming a rasterization pipeline with the same @ref MeshLayout was bound, a +mesh can be then drawn using @ref CommandBuffer::draw(). The function takes +care of binding all buffers and executing an appropriate draw command: + +@snippet MagnumVk.cpp Mesh-drawing + +@subsection Vk-Mesh-drawing-dynamic Dynamic pipeline state + +Both the @ref MeshPrimitive set in @ref MeshLayout constructor and binding +stride set in @ref MeshLayout::addBinding() can be set as dynamic in the +pipeline using @ref DynamicRasterizationState::MeshPrimitive and +@relativeref{DynamicRasterizationState,VertexInputBindingStride}, assuming +@ref DeviceFeature::ExtendedDynamicState is supported and enabled. The +@ref CommandBuffer::draw() function then checks what dynamic state is enabled +in the currently bound pipeline and implicitly sets all dynamic states. + +Taking this to the extreme, with these two dynamic states and a dedicated +binding for each attribute you can make the pipeline accept basically any mesh +as long as just the attribute locations and types are the same --- offsets and +strided of particular attributes are then fully dynamic. + +@snippet MagnumVk.cpp Mesh-drawing-dynamic + + + +@m_class{m-note m-success} + +@par + The performance aspect of this approach is a whole different topic, of + course. It isn't expected to be as fast as if the vertex and primitive + layout was fixed, but it *may* be faster than having to create and bind a + whole new pipeline several times over when drawing a large set of + layout-incompatible meshes. +*/ +class MAGNUM_VK_EXPORT Mesh { + public: + /** + * @brief Construct with a reference to external @ref MeshLayout + * + * Assumes @p layout stays in scope for the whole lifetime of the + * @ref Mesh instance. + */ + explicit Mesh(const MeshLayout& layout); + + /** @brief Construct with taking over @ref MeshLayout ownership */ + explicit Mesh(MeshLayout&& layout); + + /** @brief Copying is not allowed */ + Mesh(const Mesh&) = delete; + + /** @brief Move constructor */ + Mesh(Mesh&&) noexcept; + + /** + * @brief Destructor + * + * If any buffers were added using @ref addVertexBuffer(UnsignedInt, Buffer&&, UnsignedLong) + * or @ref setIndexBuffer(Buffer&&, UnsignedLong, MeshIndexType), their + * owned instanced are destructed at this point. + */ + ~Mesh(); + + /** @brief Copying is not allowed */ + Mesh& operator=(const Mesh&) = delete; + + /** @brief Move assignment */ + Mesh& operator=(Mesh&&) noexcept; + + /** @brief Vertex/index count */ + UnsignedInt count() const; + + /** + * @brief Set vertex/index count + * @return Reference to self (for method chaining) + * + * If the mesh is indexed, the value is treated as index count, + * otherwise the value is vertex count. If set to @cpp 0 @ce, no draw + * commands are issued when calling @ref CommandBuffer::draw(). + * + * @attention To prevent nothing being rendered by accident, this + * function has to be always called, even to just set the count to + * @cpp 0 @ce. + * + * @see @ref isIndexed(), @ref setInstanceCount(), + * @ref setVertexOffset(), @ref setIndexOffset() + */ + Mesh& setCount(UnsignedInt count) { + _count = count; + return *this; + } + + /** @brief Vertex offset */ + UnsignedInt vertexOffset() const { return _vertexOffset; } + + /** + * @brief Set vertex offset + * @return Reference to self (for method chaining) + * + * For non-indexed meshes specifies the first vertex that will be + * drawn, for indexed meshes specifies the offset added to each index. + * Default is @cpp 0 @ce. + * @see @ref isIndexed(), @ref setIndexOffset(), @ref setCount() + */ + Mesh& setVertexOffset(UnsignedInt offset) { + _vertexOffset = offset; + return *this; + } + + /** @brief Index offset */ + UnsignedInt indexOffset() const { return _indexOffset; } + + /** + * @brief Set index offset + * @return Reference to self (for method chaining) + * + * Expects that the mesh is indexed. Specifies the first index that + * will be drawn. Default is @cpp 0 @ce. + * @see @ref isIndexed(), @ref setVertexOffset(), @ref setCount() + */ + Mesh& setIndexOffset(UnsignedInt offset) { + _indexOffset = offset; + return *this; + } + + /** @brief Instance count */ + UnsignedInt instanceCount() const { return _instanceCount; } + + /** + * @brief Set instance count + * @return Reference to self (for method chaining) + * + * If set to @cpp 0 @ce, no draw commands are issued when calling + * @ref CommandBuffer::draw(). Default is @cpp 1 @ce. + * @see @ref setCount(), @ref setInstanceOffset() + */ + Mesh& setInstanceCount(UnsignedInt count) { + _instanceCount = count; + return *this; + } + + /** @brief Instance offset */ + UnsignedInt instanceOffset() const { return _instanceOffset; } + + /** + * @brief Set instance offset + * @return Reference to self (for method chaining) + * + * Specifies the first instance that will be drawn. Default is + * @cpp 0 @ce. + * @see @ref setInstanceCount() + */ + Mesh& setInstanceOffset(UnsignedInt offset) { + _instanceOffset = offset; + return *this; + } + + /** + * @brief Add a vertex buffer + * @param binding Binding corresponding to a particular + * @ref MeshLayout::addBinding() call + * @param buffer A @ref Buffer instance or a raw Vulkan buffer + * handle + * @param offset Offset into the buffer, in bytes + * @return Reference to self (for method chaining) + * + * @see @ref setCount(), @ref setVertexOffset() + */ + Mesh& addVertexBuffer(UnsignedInt binding, VkBuffer buffer, UnsignedLong offset); + + /** + * @brief Add a vertex buffer and take over its ownership + * @return Reference to self (for method chaining) + * + * Compared to @ref addVertexBuffer(UnsignedInt, VkBuffer, UnsignedLong) + * the @p buffer instance ownership is transferred to the class and + * thus doesn't have to be managed separately. + */ + Mesh& addVertexBuffer(UnsignedInt binding, Buffer&& buffer, UnsignedLong offset); + + /** + * @brief Set an index buffer + * @param buffer A @ref Buffer instance or a raw Vulkan buffer + * handle + * @param offset Offset into the buffer, in bytes + * @param indexType Index type + * @return Reference to self (for method chaining) + * + * @see @ref setCount(), @ref setIndexOffset() + */ + Mesh& setIndexBuffer(VkBuffer buffer, UnsignedLong offset, MeshIndexType indexType); + /** @overload */ + Mesh& setIndexBuffer(VkBuffer buffer, UnsignedLong offset, Magnum::MeshIndexType indexType); + + /** + * @brief Set an index buffer and take over its ownership + * @return Reference to self (for method chaining) + * + * Compared to @ref setIndexBuffer(VkBuffer, UnsignedLong, MeshIndexType) + * the @p buffer instance ownership is transferred to the class and + * thus doesn't have to be managed separately. + */ + Mesh& setIndexBuffer(Buffer&& buffer, UnsignedLong offset, MeshIndexType indexType); + /** @overload */ + Mesh& setIndexBuffer(Buffer&& buffer, UnsignedLong offset, Magnum::MeshIndexType indexType); + + /** @brief Layout of this mesh */ + const MeshLayout& layout() const { return _layout; } + + /** + * @brief Vertex buffers + * + * Has the same length as the vertex buffer binding array in + * @ref layout(), the buffers correspond to binding IDs at the same + * index. + */ + Containers::ArrayView vertexBuffers(); + + /** + * @brief Vertex buffer offsets + * + * Has the same length as the vertex buffer binding array in + * @ref layout(), offsets correspond to @ref vertexBuffers() at the + * same index. + */ + Containers::ArrayView vertexBufferOffsets() const; + + /** + * @brief Vertex buffer strides + * + * Has the same length as the vertex buffer binding array in + * @ref layout(). The strides are the same as strides in the layout + * at the same index, but here in a form that's usable by + * @fn_vk{CmdBindVertexBuffers2} if + * @ref DynamicRasterizationState::VertexInputBindingStride is enabled. + */ + Containers::ArrayView vertexBufferStrides() const; + + /** + * @brief Whether the mesh is indexed + * + * The mesh is considered indexed if @ref setIndexBuffer() was called. + */ + bool isIndexed() const; + + /** + * @brief Index buffer + * + * Expects that the mesh is indexed. + * @see @ref isIndexed() + */ + VkBuffer indexBuffer(); + + /** + * @brief Index buffer offset + * + * Expects that the mesh is indexed. + * @see @ref isIndexed() + */ + UnsignedLong indexBufferOffset() const; + + /** + * @brief Index type + * + * Expects that the mesh is indexed. + * @see @ref isIndexed() + */ + MeshIndexType indexType() const; + + #ifdef DOXYGEN_GENERATING_OUTPUT + private: + #endif + #ifndef CORRADE_NO_ASSERT + /* Used by CommandBuffer::draw() for a sanity assert */ + bool isCountSet() const { return _count != ~UnsignedInt{}; } + #endif + + private: + /* This is all here and not in the State struct in order to avoid + unnecessary allocations for buffer-less meshes -- like with GL, we + want `draw(Mesh{MeshLayout{MeshPrimitive::Triangle}}.setCount(3))` + to be performant enough to not need to invent any alternatives. The + MeshLayout class does a similar thing. */ + UnsignedInt _count = ~UnsignedInt{}, + _vertexOffset = 0, + _indexOffset = 0, + _instanceCount = 1, + _instanceOffset = 0; + MeshLayout _layout; + + MAGNUM_VK_LOCAL std::size_t addVertexBufferInternal(UnsignedInt binding, VkBuffer buffer, UnsignedLong offset); + + struct State; + Containers::Pointer _state; +}; + +}} + +#endif diff --git a/src/Magnum/Vk/MeshLayout.h b/src/Magnum/Vk/MeshLayout.h index d27ee00d4..99a4169bb 100644 --- a/src/Magnum/Vk/MeshLayout.h +++ b/src/Magnum/Vk/MeshLayout.h @@ -159,7 +159,7 @@ In case @ref isMeshPrimitiveImplementationSpecific() returns @cpp false @ce for Not all generic mesh primitives have a Vulkan equivalent and this function expects that given primitive is available. Use @ref hasMeshPrimitive() to query availability of given primitive. -@see @ref vkIndexType() +@see @ref meshIndexType(), @ref vertexFormat() */ MAGNUM_VK_EXPORT MeshPrimitive meshPrimitive(Magnum::MeshPrimitive primitive); @@ -182,7 +182,7 @@ describing how vertex attributes are organized in buffers and what's the layout of each attribute. Used as an input for creating a @ref Vk-Pipeline-creation-rasterization "rasterization pipeline". -@section Vk-MeshLayout-usage Usage +@section Vk-MeshLayout-usage Mesh layout setup As an example let's assume a shader expects positions, texture coordinates and normals in locations @cpp 0 @ce, @cpp 1 @ce and @cpp 5 @ce, respectively. If @@ -191,8 +191,9 @@ could look like this: @snippet MagnumVk.cpp MeshLayout-usage -The `BufferBinding` is then subsequently used as a binding index for a concrete -vertex buffer when drawing. +The `Binding` is then subsequently used as a binding index for a concrete +vertex buffer when drawing, which is described in the @ref Mesh class +documentation. @subsection Vk-MeshLayout-usage-comparison Layout comparison @@ -417,6 +418,11 @@ class MAGNUM_VK_EXPORT MeshLayout { MAGNUM_VK_LOCAL bool hasNoExternalPointers() const; #endif + /* These are here instead of in the State struct in order to avoid + unnecessary allocations for buffer-less layouts -- like with GL, we + want `draw(Mesh{MeshLayout{MeshPrimitive::Triangle}}.setCount(3))` + to be performant enough to not need to invent any alternatives. The + Mesh class does a similar thing. */ VkPipelineVertexInputStateCreateInfo _vertexInfo; VkPipelineInputAssemblyStateCreateInfo _assemblyInfo; diff --git a/src/Magnum/Vk/RasterizationPipelineCreateInfo.h b/src/Magnum/Vk/RasterizationPipelineCreateInfo.h index aa046cac3..34ce4ddbf 100644 --- a/src/Magnum/Vk/RasterizationPipelineCreateInfo.h +++ b/src/Magnum/Vk/RasterizationPipelineCreateInfo.h @@ -159,8 +159,9 @@ enum class DynamicRasterizationState: UnsignedByte { /** * Only the @ref MeshPrimitive topology class set in @ref MeshLayout and * passed to @ref RasterizationPipelineCreateInfo is used and the specific - * topology order and adjacency is expected to be set dynamically using - * @fn_vk{CmdSetPrimitiveTopologyEXT}. + * topology order and adjacency is expected to be set dynamically. + * @ref CommandBuffer::draw() does this automatically if a pipeline with + * this dynamic state is bound. * @requires_vk_feature @ref DeviceFeature::ExtendedDynamicState * @m_keywords{VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT} */ @@ -191,10 +192,10 @@ enum class DynamicRasterizationState: UnsignedByte { /** * Stride set in @ref MeshLayout::addBinding() and passed to * @ref RasterizationPipelineCreateInfo is ignored and expected to be set - * dynamically using @fn_vk{CmdBindVertexBuffers2EXT} + * dynamically. @ref CommandBuffer::draw() does this automatically if a + * pipeline with this dynamic state is bound. * @requires_vk_feature @ref DeviceFeature::ExtendedDynamicState * @m_keywords{VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT} - * @todoc link to the actual API when exposed */ VertexInputBindingStride, diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 6655ef8fa..ab7e1d679 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -42,6 +42,7 @@ corrade_add_test(VkInstanceTest InstanceTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkMemoryTest MemoryTest.cpp LIBRARIES MagnumVkTestLib) +corrade_add_test(VkMeshTest MeshTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkMeshLayoutTest MeshLayoutTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkPipelineTest PipelineTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkPipelineLayoutTest PipelineLayoutTest.cpp LIBRARIES MagnumVk) @@ -140,6 +141,7 @@ set_target_properties( VkIntegrationTest VkLayerPropertiesTest VkMemoryTest + VkMeshTest VkMeshLayoutTest VkPipelineTest VkPipelineLayoutTest @@ -154,14 +156,34 @@ set_target_properties( PROPERTIES FOLDER "Magnum/Vk/Test") if(BUILD_VK_TESTS) + # Otherwise CMake complains that Corrade::PluginManager is not found, wtf + find_package(Corrade REQUIRED PluginManager) + if(CORRADE_TARGET_ANDROID) set(VK_TEST_DIR ".") else() set(VK_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}) endif() + # CMake before 3.8 has broken $ expressions for iOS (see + # https://gitlab.kitware.com/cmake/cmake/merge_requests/404) and since + # Corrade doesn't support dynamic plugins on iOS, this sorta works around + # that. Should be revisited when updating Travis to newer Xcode (xcode7.3 + # has CMake 3.6). + if(NOT BUILD_PLUGINS_STATIC) + if(WITH_ANYIMAGEIMPORTER) + set(ANYIMAGEIMPORTER_PLUGIN_FILENAME $) + endif() + if(WITH_TGAIMPORTER) + set(TGAIMPORTER_PLUGIN_FILENAME $) + endif() + endif() + + # First replace ${} variables, then $<> generator expressions configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake - ${CMAKE_CURRENT_BINARY_DIR}/configure.h) + ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) + file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h + INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) corrade_add_test(VkBufferVkTest BufferVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkCommandBufferVkTest CommandBufferVkTest.cpp LIBRARIES MagnumVulkanTester) @@ -176,17 +198,37 @@ if(BUILD_VK_TESTS) corrade_add_test(VkImageViewVkTest ImageViewVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkInstanceVkTest InstanceVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkMemoryVkTest MemoryVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) + + corrade_add_test(VkMeshVkTest MeshVkTest.cpp + LIBRARIES MagnumVkTestLib MagnumDebugTools MagnumVulkanTester + FILES + MeshTestFiles/flat.spv + MeshTestFiles/flat.tga + MeshTestFiles/noop.spv + MeshTestFiles/noop.tga + MeshTestFiles/vertexcolor.spv + MeshTestFiles/vertexcolor.tga) + target_include_directories(VkMeshVkTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) + if(BUILD_PLUGINS_STATIC) + if(WITH_ANYIMAGEIMPORTER) + target_link_libraries(VkMeshVkTest PRIVATE AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + target_link_libraries(VkMeshVkTest PRIVATE TgaImporter) + endif() + endif() + corrade_add_test(VkPipelineVkTest PipelineVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester FILES triangle-shaders.spv compute-noop.spv) - target_include_directories(VkPipelineVkTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_include_directories(VkPipelineVkTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) corrade_add_test(VkPipelineLayoutVkTest PipelineLayoutVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkQueueVkTest QueueVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkRenderPassVkTest RenderPassVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkShaderVkTest ShaderVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester FILES triangle-shaders.spv) - target_include_directories(VkShaderVkTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_include_directories(VkShaderVkTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) corrade_add_test(VkVersionVkTest VersionVkTest.cpp LIBRARIES MagnumVk) set_target_properties( @@ -203,6 +245,7 @@ if(BUILD_VK_TESTS) VkImageViewVkTest VkInstanceVkTest VkMemoryVkTest + VkMeshVkTest VkPipelineVkTest VkPipelineLayoutVkTest VkQueueVkTest diff --git a/src/Magnum/Vk/Test/EnumsTest.cpp b/src/Magnum/Vk/Test/EnumsTest.cpp index 39406e0bb..a365fbffc 100644 --- a/src/Magnum/Vk/Test/EnumsTest.cpp +++ b/src/Magnum/Vk/Test/EnumsTest.cpp @@ -38,10 +38,6 @@ namespace Magnum { namespace Vk { namespace Test { namespace { struct EnumsTest: TestSuite::Tester { explicit EnumsTest(); - void mapVkIndexType(); - void mapVkIndexTypeUnsupported(); - void mapVkIndexTypeInvalid(); - void mapVkFilter(); void mapVkFilterInvalid(); @@ -55,11 +51,7 @@ struct EnumsTest: TestSuite::Tester { }; EnumsTest::EnumsTest() { - addTests({&EnumsTest::mapVkIndexType, - &EnumsTest::mapVkIndexTypeUnsupported, - &EnumsTest::mapVkIndexTypeInvalid, - - &EnumsTest::mapVkFilter, + addTests({&EnumsTest::mapVkFilter, &EnumsTest::mapVkFilterInvalid, &EnumsTest::mapVkSamplerMipmapMode, @@ -71,74 +63,6 @@ EnumsTest::EnumsTest() { &EnumsTest::mapVkSamplerAddressModeInvalid}); } -void EnumsTest::mapVkIndexType() { - CORRADE_VERIFY(hasVkIndexType(Magnum::MeshIndexType::UnsignedShort)); - CORRADE_COMPARE(vkIndexType(Magnum::MeshIndexType::UnsignedShort), VK_INDEX_TYPE_UINT16); - - CORRADE_VERIFY(hasVkIndexType(Magnum::MeshIndexType::UnsignedInt)); - CORRADE_COMPARE(vkIndexType(Magnum::MeshIndexType::UnsignedInt), VK_INDEX_TYPE_UINT32); - - /* Ensure all generic index types are handled. This goes through the first - 16 bits, which should be enough. Going through 32 bits takes 8 seconds, - too much. */ - for(UnsignedInt i = 1; i <= 0xffff; ++i) { - const auto type = Magnum::MeshIndexType(i); - #ifdef __GNUC__ - #pragma GCC diagnostic push - #pragma GCC diagnostic error "-Wswitch" - #endif - switch(type) { - #define _c(type) \ - case Magnum::MeshIndexType::type: \ - CORRADE_VERIFY(UnsignedInt(vkIndexType(Magnum::MeshIndexType::type)) >= 0); \ - break; - #include "Magnum/Implementation/meshIndexTypeMapping.hpp" - #undef _c - } - #ifdef __GNUC__ - #pragma GCC diagnostic pop - #endif - } -} - -void EnumsTest::mapVkIndexTypeUnsupported() { - #ifdef CORRADE_NO_ASSERT - CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); - #endif - - #if 1 - CORRADE_SKIP("All index formats are supported."); - #else - CORRADE_VERIFY(!hasVkIndexType(Magnum::MeshIndexType::UnsignedByte)); - std::ostringstream out; - { - Error redirectError{&out}; - vkIndexType(Magnum::MeshIndexType::UnsignedByte); - } - CORRADE_COMPARE(out.str(), - "Vk::vkIndexType(): unsupported type MeshIndexType::UnsignedByte\n"); - #endif -} - -void EnumsTest::mapVkIndexTypeInvalid() { - #ifdef CORRADE_NO_ASSERT - CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); - #endif - - std::ostringstream out; - Error redirectError{&out}; - - hasVkIndexType(Magnum::MeshIndexType(0x0)); - hasVkIndexType(Magnum::MeshIndexType(0x12)); - vkIndexType(Magnum::MeshIndexType(0x0)); - vkIndexType(Magnum::MeshIndexType(0x12)); - CORRADE_COMPARE(out.str(), - "Vk::hasVkIndexType(): invalid type MeshIndexType(0x0)\n" - "Vk::hasVkIndexType(): invalid type MeshIndexType(0x12)\n" - "Vk::vkIndexType(): invalid type MeshIndexType(0x0)\n" - "Vk::vkIndexType(): invalid type MeshIndexType(0x12)\n"); -} - void EnumsTest::mapVkFilter() { CORRADE_COMPARE(vkFilter(SamplerFilter::Nearest), VK_FILTER_NEAREST); CORRADE_COMPARE(vkFilter(SamplerFilter::Linear), VK_FILTER_LINEAR); diff --git a/src/Magnum/Vk/Test/MeshTest.cpp b/src/Magnum/Vk/Test/MeshTest.cpp new file mode 100644 index 000000000..f8af4f3d3 --- /dev/null +++ b/src/Magnum/Vk/Test/MeshTest.cpp @@ -0,0 +1,278 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + 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. +*/ + +#include +#include +#include +#include + +#include "Magnum/Mesh.h" +#include "Magnum/Vk/Buffer.h" +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Mesh.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct MeshTest: TestSuite::Tester { + explicit MeshTest(); + + void mapIndexType(); + void mapIndexTypeInvalid(); + + void construct(); + void countsOffsets(); + + void addVertexBuffer(); + void addVertexBufferOwned(); + void addVertexBufferNoSuchBinding(); + + template void setIndexBuffer(); + template void setIndexBufferOwned(); + + void indexPropertiesNotIndexed(); + + void debugIndexType(); +}; + +MeshTest::MeshTest() { + addTests({&MeshTest::mapIndexType, + &MeshTest::mapIndexTypeInvalid, + + &MeshTest::construct, + &MeshTest::countsOffsets, + + &MeshTest::addVertexBuffer, + &MeshTest::addVertexBufferOwned, + &MeshTest::addVertexBufferNoSuchBinding, + + &MeshTest::setIndexBuffer, + &MeshTest::setIndexBuffer, + &MeshTest::setIndexBufferOwned, + &MeshTest::setIndexBufferOwned, + + &MeshTest::indexPropertiesNotIndexed, + + &MeshTest::debugIndexType}); +} + +template struct IndexTypeTraits; +template<> struct IndexTypeTraits { + static const char* name() { return "MeshIndexType"; } +}; +template<> struct IndexTypeTraits { + static const char* name() { return "Magnum::MeshIndexType"; } +}; + +void MeshTest::mapIndexType() { + CORRADE_COMPARE(meshIndexType(Magnum::MeshIndexType::UnsignedByte), MeshIndexType::UnsignedByte); + CORRADE_COMPARE(meshIndexType(Magnum::MeshIndexType::UnsignedShort), MeshIndexType::UnsignedShort); + CORRADE_COMPARE(meshIndexType(Magnum::MeshIndexType::UnsignedInt), MeshIndexType::UnsignedInt); +} + +void MeshTest::mapIndexTypeInvalid() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + meshIndexType(Magnum::MeshIndexType(0x0)); + meshIndexType(Magnum::MeshIndexType(0x12)); + CORRADE_COMPARE(out.str(), + "Vk::meshIndexType(): invalid type MeshIndexType(0x0)\n" + "Vk::meshIndexType(): invalid type MeshIndexType(0x12)\n"); +} + +void MeshTest::construct() { + MeshLayout layout{MeshPrimitive::Triangles}; + layout.vkPipelineVertexInputStateCreateInfo().sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + layout.vkPipelineInputAssemblyStateCreateInfo().sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2; + Mesh mesh{layout}; + /* These should be copies of the original layout */ + CORRADE_COMPARE(mesh.layout().vkPipelineVertexInputStateCreateInfo().sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); + CORRADE_COMPARE(mesh.layout().vkPipelineInputAssemblyStateCreateInfo().sType, VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2); + CORRADE_COMPARE(mesh.count(), 0); + CORRADE_COMPARE(mesh.vertexOffset(), 0); + CORRADE_COMPARE(mesh.indexOffset(), 0); + CORRADE_COMPARE(mesh.instanceCount(), 1); + CORRADE_COMPARE(mesh.instanceOffset(), 0); + CORRADE_VERIFY(mesh.vertexBuffers().empty()); + CORRADE_VERIFY(mesh.vertexBufferOffsets().empty()); + CORRADE_VERIFY(mesh.vertexBufferStrides().empty()); + CORRADE_VERIFY(!mesh.isIndexed()); +} + +void MeshTest::countsOffsets() { + Mesh mesh{MeshLayout{MeshPrimitive::Triangles}}; + mesh.setCount(15) + .setVertexOffset(3) + .setIndexOffset(5) + .setInstanceCount(7) + .setInstanceOffset(9); + CORRADE_COMPARE(mesh.count(), 15); + CORRADE_COMPARE(mesh.vertexOffset(), 3); + CORRADE_COMPARE(mesh.indexOffset(), 5); + CORRADE_COMPARE(mesh.instanceCount(), 7); + CORRADE_COMPARE(mesh.instanceOffset(), 9); +} + +void MeshTest::addVertexBuffer() { + Mesh mesh{MeshLayout{MeshPrimitive::TriangleFan} + .addBinding(1, 2) + .addInstancedBinding(5, 3) + }; + CORRADE_COMPARE_AS(mesh.vertexBuffers(), Containers::arrayView({ + VkBuffer{}, VkBuffer{} + }), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(mesh.vertexBufferOffsets(), Containers::arrayView({ + 0, 0 + }), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(mesh.vertexBufferStrides(), Containers::arrayView({ + 0, 0 + }), TestSuite::Compare::Container); + + mesh.addVertexBuffer(5, reinterpret_cast(0xdead), 15); + CORRADE_COMPARE_AS(mesh.vertexBuffers(), Containers::arrayView({ + VkBuffer{}, reinterpret_cast(0xdead) + }), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(mesh.vertexBufferOffsets(), Containers::arrayView({ + 0, 15 + }), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(mesh.vertexBufferStrides(), Containers::arrayView({ + 0, 3 + }), TestSuite::Compare::Container); + + mesh.addVertexBuffer(1, reinterpret_cast(0xbeef), 37); + CORRADE_COMPARE_AS(mesh.vertexBuffers(), Containers::arrayView({ + reinterpret_cast(0xbeef), reinterpret_cast(0xdead) + }), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(mesh.vertexBufferOffsets(), Containers::arrayView({ + 37, 15 + }), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(mesh.vertexBufferStrides(), Containers::arrayView({ + 2, 3 + }), TestSuite::Compare::Container); +} + +void MeshTest::addVertexBufferOwned() { + Mesh mesh{MeshLayout{MeshPrimitive::TriangleFan} + .addBinding(1, 2) + .addInstancedBinding(5, 3) + }; + + Device device{NoCreate}; + Buffer a = Buffer::wrap(device, reinterpret_cast(0xdead)); + Buffer b = Buffer::wrap(device, reinterpret_cast(0xbeef)); + mesh.addVertexBuffer(5, std::move(a), 15) + .addVertexBuffer(1, std::move(b), 37); + CORRADE_VERIFY(!a.handle()); + CORRADE_VERIFY(!b.handle()); + + CORRADE_COMPARE_AS(mesh.vertexBuffers(), Containers::arrayView({ + reinterpret_cast(0xbeef), reinterpret_cast(0xdead) + }), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(mesh.vertexBufferOffsets(), Containers::arrayView({ + 37, 15 + }), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(mesh.vertexBufferStrides(), Containers::arrayView({ + 2, 3 + }), TestSuite::Compare::Container); +} + +void MeshTest::addVertexBufferNoSuchBinding() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Mesh noBindings{MeshLayout{MeshPrimitive::Triangles}}; + Mesh differentBindings{MeshLayout{MeshPrimitive::Lines} + .addBinding(1, 2) + .addInstancedBinding(5, 3)}; + + std::ostringstream out; + Error redirectError{&out}; + noBindings.addVertexBuffer(2, VkBuffer{}, 0); + differentBindings.addVertexBuffer(3, Buffer{NoCreate}, 5); + CORRADE_COMPARE(out.str(), + "Vk::Mesh::addVertexBuffer(): binding 2 not present among 0 bindings in the layout\n" + "Vk::Mesh::addVertexBuffer(): binding 3 not present among 2 bindings in the layout\n"); +} + +template void MeshTest::setIndexBuffer() { + setTestCaseTemplateName(IndexTypeTraits::name()); + + Mesh mesh{MeshLayout{MeshPrimitive::Triangles}}; + CORRADE_VERIFY(!mesh.isIndexed()); + + mesh.setIndexBuffer(reinterpret_cast(0xdead), 15, T::UnsignedByte); + CORRADE_VERIFY(mesh.isIndexed()); + CORRADE_COMPARE(mesh.indexBuffer(), reinterpret_cast(0xdead)); + CORRADE_COMPARE(mesh.indexBufferOffset(), 15); + CORRADE_COMPARE(mesh.indexType(), MeshIndexType::UnsignedByte); +} + +template void MeshTest::setIndexBufferOwned() { + setTestCaseTemplateName(IndexTypeTraits::name()); + + Device device{NoCreate}; + Buffer a = Buffer::wrap(device, reinterpret_cast(0xdead)); + + Mesh mesh{MeshLayout{MeshPrimitive::Triangles}}; + mesh.setIndexBuffer(std::move(a), 15, T::UnsignedByte); + CORRADE_VERIFY(!a.handle()); + CORRADE_VERIFY(mesh.isIndexed()); + CORRADE_COMPARE(mesh.indexBuffer(), reinterpret_cast(0xdead)); + CORRADE_COMPARE(mesh.indexBufferOffset(), 15); + CORRADE_COMPARE(mesh.indexType(), MeshIndexType::UnsignedByte); +} + +void MeshTest::indexPropertiesNotIndexed() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Mesh mesh{MeshLayout{MeshPrimitive::Triangles}}; + CORRADE_VERIFY(!mesh.isIndexed()); + + std::ostringstream out; + Error redirectError{&out}; + mesh.indexBuffer(); + mesh.indexBufferOffset(); + mesh.indexType(); + CORRADE_COMPARE(out.str(), + "Vk::Mesh::indexBuffer(): the mesh is not indexed\n" + "Vk::Mesh::indexBufferOffset(): the mesh is not indexed\n" + "Vk::Mesh::indexType(): the mesh is not indexed\n"); +} + +void MeshTest::debugIndexType() { + std::ostringstream out; + Debug{&out} << MeshIndexType::UnsignedShort << MeshIndexType(-10007655); + CORRADE_COMPARE(out.str(), "Vk::MeshIndexType::UnsignedShort Vk::MeshIndexType(-10007655)\n"); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::MeshTest) diff --git a/src/Magnum/Vk/Test/MeshTestFiles/convert.sh b/src/Magnum/Vk/Test/MeshTestFiles/convert.sh new file mode 100755 index 000000000..0e65958ac --- /dev/null +++ b/src/Magnum/Vk/Test/MeshTestFiles/convert.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +for i in $(ls *.spvasm); do + magnum-shaderconverter $i ${i%asm} +done diff --git a/src/Magnum/Vk/Test/MeshTestFiles/flat.spv b/src/Magnum/Vk/Test/MeshTestFiles/flat.spv new file mode 100644 index 0000000000000000000000000000000000000000..a833ae87770f8b4bf669877330669b2fe6461a51 GIT binary patch literal 444 zcmX|+%MJli5Jbxi#&aGd>}14}wMb-G`VI>|U>4Rs+eV_!PPI_~A3tubI~1ygdg?j`_x5Tgm9D_OSQkh1t_%#$)^T88&P=S&-Hp=P zUCa|N91rYkW9j*!JsgeN@5r7T$sd0lO>)%HLNnGQCr3Y=8{k`Wc25i}@@Zht^UhbX yCpXB^2Z;CH!oBEX-2d`_3j4-;nJW!$J6R1q_}dc)BhK?LEY4Ss>E}jtWB&)PTMm5y literal 0 HcmV?d00001 diff --git a/src/Magnum/Vk/Test/MeshTestFiles/flat.spvasm b/src/Magnum/Vk/Test/MeshTestFiles/flat.spvasm new file mode 100644 index 000000000..18dcfda06 --- /dev/null +++ b/src/Magnum/Vk/Test/MeshTestFiles/flat.spvasm @@ -0,0 +1,35 @@ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %ver "ver" %position %gl_Position + OpEntryPoint Fragment %fra "fra" %fragmentColor + OpExecutionMode %fra OriginUpperLeft + OpDecorate %position Location 0 + OpDecorate %gl_Position BuiltIn Position + OpDecorate %fragmentColor Location 0 + + %void = OpTypeVoid + %10 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %position = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_Position = OpVariable %_ptr_Output_v4float Output +%fragmentColor = OpVariable %_ptr_Output_v4float Output + + %9 = OpConstant %float 0 + %11 = OpConstant %float 1 + %12 = OpConstantComposite %v4float %11 %9 %9 %11 + + %ver = OpFunction %void None %10 + %ver_ = OpLabel + %16 = OpLoad %v4float %position + OpStore %gl_Position %16 + OpReturn + OpFunctionEnd + + %fra = OpFunction %void None %10 + %fra_ = OpLabel + OpStore %fragmentColor %12 + OpReturn + OpFunctionEnd diff --git a/src/Magnum/Vk/Test/MeshTestFiles/flat.tga b/src/Magnum/Vk/Test/MeshTestFiles/flat.tga new file mode 100644 index 0000000000000000000000000000000000000000..b41ef549b7e65eb6c83eae2f3dd3cfa284cb38e0 GIT binary patch literal 338 pcmZQz;9`IQ1qLAGm@h9c|DOn=9i*Xwc^(R@X2 zZ-uW>4~Sy>D&8T{`SRy4c86TT>83q(dq=o-0NLj~a{qp0yZimC<~>jDn-DKdPycwK TIN?d}UM=aTKL}P@xOlFJ9U_|L*#vAi^0>}IZJ7o}r literal 0 HcmV?d00001 diff --git a/src/Magnum/Vk/Test/MeshTestFiles/vertexcolor.spvasm b/src/Magnum/Vk/Test/MeshTestFiles/vertexcolor.spvasm new file mode 100644 index 000000000..a8154302e --- /dev/null +++ b/src/Magnum/Vk/Test/MeshTestFiles/vertexcolor.spvasm @@ -0,0 +1,40 @@ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %ver "ver" %position %color %gl_Position %interpolatedColorOut + OpEntryPoint Fragment %fra "fra" %interpolatedColorIn %fragmentColor + OpExecutionMode %fra OriginUpperLeft + + OpDecorate %gl_Position BuiltIn Position + OpDecorate %position Location 0 + OpDecorate %color Location 1 + OpDecorate %interpolatedColorIn Location 0 + OpDecorate %interpolatedColorOut Location 0 + OpDecorate %fragmentColor Location 0 + %void = OpTypeVoid + %10 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %position = OpVariable %_ptr_Input_v4float Input + %color = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_Position = OpVariable %_ptr_Output_v4float Output +%interpolatedColorOut = OpVariable %_ptr_Output_v4float Output +%interpolatedColorIn = OpVariable %_ptr_Input_v4float Input +%fragmentColor = OpVariable %_ptr_Output_v4float Output + + %ver = OpFunction %void None %10 + %ver_ = OpLabel + %16 = OpLoad %v4float %position + %17 = OpLoad %v4float %color + OpStore %gl_Position %16 + OpStore %interpolatedColorOut %17 + OpReturn + OpFunctionEnd + + %fra = OpFunction %void None %10 + %fra_ = OpLabel + %19 = OpLoad %v4float %interpolatedColorIn + OpStore %fragmentColor %19 + OpReturn + OpFunctionEnd diff --git a/src/Magnum/Vk/Test/MeshTestFiles/vertexcolor.tga b/src/Magnum/Vk/Test/MeshTestFiles/vertexcolor.tga new file mode 100644 index 0000000000000000000000000000000000000000..228b40b326db6ebfb9ba8b3bf1d02d49e7056de6 GIT binary patch literal 1362 zcma)+A+GH<6h#B8qR!xT3LZsf;0c5qhnlnsGz;=aK}I1VK_LMlAqE$iTww5k!IKQW zWUzXks;W}ZPb5pRPnNd!IonPQ`x6~@^tPgp z1%1uvIHA*s&I7vi=-Q!M|HK*fiP3M2d1360aVN&_n6P5D1#`@pYr@14lLk!gF{Q(Z zI@5+H&Z;+7zp&=U+7s(`tY5KV!EQ75n6TG~jRQ9I*xX@7ovrfS#wX6H7f#ld zb1Tj-xG>|k33rURYrw@GmpYuNbK;$=vfrjB&Z`@*PrTXjcE!5|?`M3N@Y{$#2K?3I zV}}=YUcB=~_N$z?`H2haBE$mW+d8y=z5~>h@vivcTqYUW$vP+BBq6yS7O5*gxlI=;ZBZ;~s-X-a5lDSKgiWC=8T1j~)m6KF&QhPmdS&d{pkWEjv9a+?6 z@h(eev&>zVRAk9)jw?Csa|njNliC3zo_L+t)JBP z^~5#R(L`Mn@0xVB$=o$bMU%`nxl!7&)2@>iZ(4fM@|#vZY4wZNe*Ta9@$qZ-;y+5m BU=9EP literal 0 HcmV?d00001 diff --git a/src/Magnum/Vk/Test/MeshVkTest.cpp b/src/Magnum/Vk/Test/MeshVkTest.cpp new file mode 100644 index 000000000..529cb6abd --- /dev/null +++ b/src/Magnum/Vk/Test/MeshVkTest.cpp @@ -0,0 +1,878 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + 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. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "Magnum/ImageView.h" +#include "Magnum/PixelFormat.h" +#include "Magnum/DebugTools/CompareImage.h" +#include "Magnum/Math/Color.h" +#include "Magnum/Math/Range.h" +#include "Magnum/Trade/AbstractImporter.h" +#include "Magnum/Vk/BufferCreateInfo.h" +#include "Magnum/Vk/CommandBuffer.h" +#include "Magnum/Vk/CommandPoolCreateInfo.h" +#include "Magnum/Vk/DeviceCreateInfo.h" +#include "Magnum/Vk/DeviceFeatures.h" +#include "Magnum/Vk/DeviceProperties.h" +#include "Magnum/Vk/ExtensionProperties.h" +#include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/Fence.h" +#include "Magnum/Vk/FramebufferCreateInfo.h" +#include "Magnum/Vk/ImageCreateInfo.h" +#include "Magnum/Vk/ImageViewCreateInfo.h" +#include "Magnum/Vk/Mesh.h" +#include "Magnum/Vk/PipelineLayoutCreateInfo.h" +#include "Magnum/Vk/PixelFormat.h" +#include "Magnum/Vk/RasterizationPipelineCreateInfo.h" +#include "Magnum/Vk/RenderPassCreateInfo.h" +#include "Magnum/Vk/ShaderCreateInfo.h" +#include "Magnum/Vk/ShaderSet.h" +#include "Magnum/Vk/VertexFormat.h" +#include "Magnum/Vk/VulkanTester.h" + +#include "configure.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct MeshVkTest: VulkanTester { + explicit MeshVkTest(); + + void setup(Device& device); + void setup() { setup(device()); } + void setupRobustness2(); + void setupExtendedDynamicState(); + void teardown(); + + void cmdDraw(); + void cmdDrawIndexed(); + void cmdDrawTwoAttributes(); + void cmdDrawTwoAttributesTwoBindings(); + void cmdDrawNullBindingRobustness2(); + void cmdDrawZeroCount(); + void cmdDrawNoCountSet(); + + void cmdDrawDynamicPrimitive(); + void cmdDrawDynamicStride(); + void cmdDrawDynamicStrideInsufficientImplementation(); + + Queue _queue{NoCreate}; + Device _deviceRobustness2{NoCreate}, _deviceExtendedDynamicState{NoCreate}; + CommandPool _pool{NoCreate}; + Image _color{NoCreate}; + RenderPass _renderPass{NoCreate}; + ImageView _colorView{NoCreate}; + Framebuffer _framebuffer{NoCreate}; + PipelineLayout _pipelineLayout{NoCreate}; + Buffer _pixels{NoCreate}; + + private: + PluginManager::Manager _manager{"nonexistent"}; +}; + +using namespace Containers::Literals; +using namespace Math::Literals; + +const struct { + const char* name; + UnsignedInt count; + UnsignedInt instanceCount; +} CmdDrawZeroCountData[] { + {"zero elements", 0, 1}, + {"zero instances", 4, 0} +}; + +MeshVkTest::MeshVkTest() { + addTests({&MeshVkTest::cmdDraw, + &MeshVkTest::cmdDrawIndexed, + &MeshVkTest::cmdDrawTwoAttributes, + &MeshVkTest::cmdDrawTwoAttributesTwoBindings}, + &MeshVkTest::setup, + &MeshVkTest::teardown); + + addTests({&MeshVkTest::cmdDrawNullBindingRobustness2}, + &MeshVkTest::setupRobustness2, + &MeshVkTest::teardown); + + addInstancedTests({&MeshVkTest::cmdDrawZeroCount}, + Containers::arraySize(CmdDrawZeroCountData), + &MeshVkTest::setup, + &MeshVkTest::teardown); + + addTests({&MeshVkTest::cmdDrawNoCountSet}, + &MeshVkTest::setup, + &MeshVkTest::teardown); + + addTests({&MeshVkTest::cmdDrawDynamicPrimitive, + &MeshVkTest::cmdDrawDynamicStride}, + &MeshVkTest::setupExtendedDynamicState, + &MeshVkTest::teardown); + + addTests({&MeshVkTest::cmdDrawDynamicStrideInsufficientImplementation}, + &MeshVkTest::setup, + &MeshVkTest::teardown); + + /* Load the plugins directly from the build tree. Otherwise they're either + static and already loaded or not present in the build tree */ + #ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT_OUTPUT(_manager.load(ANYIMAGEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + #ifdef TGAIMPORTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT_OUTPUT(_manager.load(TGAIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif +} + +void MeshVkTest::setup(Device& device) { + _pool = CommandPool{device, CommandPoolCreateInfo{ + device.properties().pickQueueFamily(QueueFlag::Graphics)}}; + _color = Image{device, ImageCreateInfo2D{ + ImageUsage::ColorAttachment|ImageUsage::TransferSource, + PixelFormat::RGBA8Srgb, {32, 32}, 1 + }, Vk::MemoryFlag::DeviceLocal}; + _renderPass = RenderPass{device, RenderPassCreateInfo{} + .setAttachments({AttachmentDescription{ + _color.format(), + AttachmentLoadOperation::Clear, + AttachmentStoreOperation::Store, + ImageLayout::Undefined, + ImageLayout::TransferSource + }}) + .addSubpass(SubpassDescription{}.setColorAttachments({ + AttachmentReference{0, ImageLayout::ColorAttachment} + })) + /* So the color data are visible for the transfer */ + .setDependencies({SubpassDependency{ + 0, SubpassDependency::External, + PipelineStage::ColorAttachmentOutput, + PipelineStage::Transfer, + Access::ColorAttachmentWrite, + Access::TransferRead + }}) + }; + _colorView = ImageView{device, ImageViewCreateInfo2D{_color}}; + _framebuffer = Framebuffer{device, FramebufferCreateInfo{_renderPass, { + _colorView + }, {32, 32}}}; + _pipelineLayout = PipelineLayout{device, PipelineLayoutCreateInfo{}}; + _pixels = Buffer{device, BufferCreateInfo{ + BufferUsage::TransferDestination, 32*32*4 + }, Vk::MemoryFlag::HostVisible}; +} + +void MeshVkTest::setupRobustness2() { + DeviceProperties properties = pickDevice(instance()); + /* If the extension / feature isn't supported, do nothing */ + if(!properties.enumerateExtensionProperties().isSupported() || + !(properties.features() & DeviceFeature::NullDescriptor)) + return; + + /* Create the device only if not already, to avoid spamming the output */ + if(!_deviceRobustness2.handle()) _deviceRobustness2.create(instance(), DeviceCreateInfo{std::move(properties)} + .addQueues(QueueFlag::Graphics, {0.0f}, {_queue}) + .addEnabledExtensions() + .setEnabledFeatures(DeviceFeature::NullDescriptor) + ); + + setup(_deviceRobustness2); +} + +void MeshVkTest::setupExtendedDynamicState() { + DeviceProperties properties = pickDevice(instance()); + /* If the extension / feature isn't supported, do nothing */ + if(!properties.enumerateExtensionProperties().isSupported() || + !(properties.features() & DeviceFeature::ExtendedDynamicState)) + return; + + /* Create the device only if not already, to avoid spamming the output */ + if(!_deviceExtendedDynamicState.handle()) _deviceExtendedDynamicState.create(instance(), DeviceCreateInfo{std::move(properties)} + .addQueues(QueueFlag::Graphics, {0.0f}, {_queue}) + .addEnabledExtensions() + .setEnabledFeatures(DeviceFeature::ExtendedDynamicState) + ); + + setup(_deviceExtendedDynamicState); +} + +void MeshVkTest::teardown() { + _pool = CommandPool{NoCreate}; + _renderPass = RenderPass{NoCreate}; + _color = Image{NoCreate}; + _colorView = ImageView{NoCreate}; + _framebuffer = Framebuffer{NoCreate}; + _pipelineLayout = PipelineLayout{NoCreate}; + _pixels = Buffer{NoCreate}; +} + +const struct Quad { + Vector3 position; + Vector3 color; +} QuadData[] { + {{-0.5f, -0.5f, 0.0f}, 0xff0000_srgbf}, + {{ 0.5f, -0.5f, 0.0f}, 0x00ff00_srgbf}, + {{-0.5f, 0.5f, 0.0f}, 0x0000ff_srgbf}, + {{ 0.5f, 0.5f, 0.0f}, 0xffffff_srgbf} +}; + +constexpr UnsignedShort QuadIndexData[] { + 0, 1, 2, 2, 1, 3 +}; + +void MeshVkTest::cmdDraw() { + /* This is the most simple binding (no offsets, single attribute, single + buffer) to test the basic workflow. The cmdDrawIndexed() test and others + pile on the complexity, but when everything goes wrong it's good to have + a simple test case. */ + + Mesh mesh{MeshLayout{MeshPrimitive::TriangleStrip} + .addBinding(0, sizeof(Vector3)) + .addAttribute(0, 0, VertexFormat::Vector3, 0) + }; + { + Buffer buffer{device(), BufferCreateInfo{ + BufferUsage::VertexBuffer, + sizeof(Vector3)*4 + }, MemoryFlag::HostVisible}; + /** @todo ffs fucking casts!!! */ + Utility::copy( + Containers::stridedArrayView(QuadData).slice(&Quad::position), + Containers::arrayCast(Containers::arrayView(buffer.dedicatedMemory().map()))); + mesh.addVertexBuffer(0, std::move(buffer), 0) + .setCount(4); + } + + Shader shader{device(), ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/flat.spv")) + }}; + + ShaderSet shaderSet; + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); + + Pipeline pipeline{device(), RasterizationPipelineCreateInfo{ + shaderSet, mesh.layout(), _pipelineLayout, _renderPass, 0, 1} + .setViewport({{}, Vector2{_framebuffer.size().xy()}}) + }; + + CommandBuffer cmd = _pool.allocate(); + cmd.begin() + .beginRenderPass(Vk::RenderPassBeginInfo{_renderPass, _framebuffer} + .clearColor(0, 0x1f1f1f_srgbf) + ) + .bindPipeline(pipeline) + .draw(mesh) + .endRenderPass() + .copyImageToBuffer({_color, Vk::ImageLayout::TransferSource, _pixels, { + Vk::BufferImageCopy2D{0, Vk::ImageAspect::Color, 0, {{}, _framebuffer.size().xy()}} + }}) + .pipelineBarrier(Vk::PipelineStage::Transfer, Vk::PipelineStage::Host, { + {Vk::Access::TransferWrite, Vk::Access::HostRead, _pixels} + }) + .end(); + + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + CORRADE_COMPARE_WITH((ImageView2D{Magnum::PixelFormat::RGBA8Unorm, + _framebuffer.size().xy(), + _pixels.dedicatedMemory().mapRead()}), + Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/flat.tga"), + DebugTools::CompareImageToFile{_manager}); +} + +void MeshVkTest::cmdDrawIndexed() { + Mesh mesh{MeshLayout{MeshPrimitive::Triangles} + .addBinding(0, sizeof(Vector3)) + .addAttribute(0, 0, VertexFormat::Vector3, 0) + }; + { + Buffer buffer{device(), BufferCreateInfo{ + BufferUsage::VertexBuffer|BufferUsage::IndexBuffer, + /* Artifical offset at the beginning to test that the offset is + used correctly in both cases */ + 32 + 12*4 + sizeof(QuadIndexData) + }, MemoryFlag::HostVisible}; + Containers::Array data = buffer.dedicatedMemory().map(); + /** @todo ffs fucking casts!!! */ + Utility::copy(Containers::stridedArrayView(QuadData).slice(&Quad::position), + Containers::arrayCast(data.slice(32, 32 + 12*4))); + Utility::copy(Containers::arrayCast(QuadIndexData), + Containers::stridedArrayView(data).suffix(32 + 12*4)); + mesh.addVertexBuffer(0, buffer, 32) + .setIndexBuffer(std::move(buffer), 32 + 12*4, MeshIndexType::UnsignedShort) + .setCount(6); + } + + Shader shader{device(), ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/flat.spv")) + }}; + + ShaderSet shaderSet; + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); + + Pipeline pipeline{device(), RasterizationPipelineCreateInfo{ + shaderSet, mesh.layout(), _pipelineLayout, _renderPass, 0, 1} + .setViewport({{}, Vector2{_framebuffer.size().xy()}}) + }; + + CommandBuffer cmd = _pool.allocate(); + cmd.begin() + .beginRenderPass(Vk::RenderPassBeginInfo{_renderPass, _framebuffer} + .clearColor(0, 0x1f1f1f_srgbf) + ) + .bindPipeline(pipeline) + .draw(mesh) + .endRenderPass() + .copyImageToBuffer({_color, Vk::ImageLayout::TransferSource, _pixels, { + Vk::BufferImageCopy2D{0, Vk::ImageAspect::Color, 0, {{}, _framebuffer.size().xy()}} + }}) + .pipelineBarrier(Vk::PipelineStage::Transfer, Vk::PipelineStage::Host, { + {Vk::Access::TransferWrite, Vk::Access::HostRead, _pixels} + }) + .end(); + + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + CORRADE_COMPARE_WITH((ImageView2D{Magnum::PixelFormat::RGBA8Unorm, + _framebuffer.size().xy(), + _pixels.dedicatedMemory().mapRead()}), + Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/flat.tga"), + DebugTools::CompareImageToFile{_manager}); +} + +void MeshVkTest::cmdDrawTwoAttributes() { + Mesh mesh{MeshLayout{MeshPrimitive::TriangleStrip} + .addBinding(0, sizeof(Quad)) + .addAttribute(0, 0, VertexFormat::Vector3, offsetof(Quad, position)) + .addAttribute(1, 0, VertexFormat::Vector3, offsetof(Quad, color)) + }; + { + Buffer buffer{device(), BufferCreateInfo{ + BufferUsage::VertexBuffer, + sizeof(QuadData) + }, MemoryFlag::HostVisible}; + /** @todo ffs fucking casts!!! */ + Utility::copy(Containers::arrayCast(QuadData), + Containers::stridedArrayView(buffer.dedicatedMemory().map())); + mesh.addVertexBuffer(0, std::move(buffer), 0) + .setCount(4); + } + + Shader shader{device(), ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/vertexcolor.spv")) + }}; + + ShaderSet shaderSet; + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); + + Pipeline pipeline{device(), RasterizationPipelineCreateInfo{ + shaderSet, mesh.layout(), _pipelineLayout, _renderPass, 0, 1} + .setViewport({{}, Vector2{_framebuffer.size().xy()}}) + }; + + CommandBuffer cmd = _pool.allocate(); + cmd.begin() + .beginRenderPass(Vk::RenderPassBeginInfo{_renderPass, _framebuffer} + .clearColor(0, 0x1f1f1f_srgbf) + ) + .bindPipeline(pipeline) + .draw(mesh) + .endRenderPass() + .copyImageToBuffer({_color, Vk::ImageLayout::TransferSource, _pixels, { + Vk::BufferImageCopy2D{0, Vk::ImageAspect::Color, 0, {{}, _framebuffer.size().xy()}} + }}) + .pipelineBarrier(Vk::PipelineStage::Transfer, Vk::PipelineStage::Host, { + {Vk::Access::TransferWrite, Vk::Access::HostRead, _pixels} + }) + .end(); + + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + CORRADE_COMPARE_WITH((ImageView2D{Magnum::PixelFormat::RGBA8Unorm, + _framebuffer.size().xy(), + _pixels.dedicatedMemory().mapRead()}), + Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/vertexcolor.tga"), + /* ARM Mali (Android) has some minor off-by-one differences */ + (DebugTools::CompareImageToFile{_manager, 0.5f, 0.012f})); +} + +void MeshVkTest::cmdDrawTwoAttributesTwoBindings() { + Mesh mesh{MeshLayout{MeshPrimitive::TriangleStrip} + .addBinding(0, sizeof(Vector3)) + .addBinding(1, sizeof(Vector3)) + .addAttribute(0, 0, VertexFormat::Vector3, 0) + .addAttribute(1, 1, VertexFormat::Vector3, 0) + }; + { + Buffer positions{device(), BufferCreateInfo{ + BufferUsage::VertexBuffer, + sizeof(Vector3)*4 + }, MemoryFlag::HostVisible}; + Buffer colors{device(), BufferCreateInfo{ + BufferUsage::VertexBuffer, + sizeof(Vector3)*4 + }, MemoryFlag::HostVisible}; + /** @todo ffs fucking casts!!! */ + Utility::copy(Containers::stridedArrayView(QuadData).slice(&Quad::position), + Containers::arrayCast(Containers::arrayView(positions.dedicatedMemory().map()))); + Utility::copy(Containers::stridedArrayView(QuadData).slice(&Quad::color), + Containers::arrayCast(Containers::arrayView(colors.dedicatedMemory().map()))); + mesh.addVertexBuffer(0, std::move(positions), 0) + .addVertexBuffer(1, std::move(colors), 0) + .setCount(4); + } + + Shader shader{device(), ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/vertexcolor.spv")) + }}; + + ShaderSet shaderSet; + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); + + Pipeline pipeline{device(), RasterizationPipelineCreateInfo{ + shaderSet, mesh.layout(), _pipelineLayout, _renderPass, 0, 1} + .setViewport({{}, Vector2{_framebuffer.size().xy()}}) + }; + + CommandBuffer cmd = _pool.allocate(); + cmd.begin() + .beginRenderPass(Vk::RenderPassBeginInfo{_renderPass, _framebuffer} + .clearColor(0, 0x1f1f1f_srgbf) + ) + .bindPipeline(pipeline) + .draw(mesh) + .endRenderPass() + .copyImageToBuffer({_color, Vk::ImageLayout::TransferSource, _pixels, { + Vk::BufferImageCopy2D{0, Vk::ImageAspect::Color, 0, {{}, _framebuffer.size().xy()}} + }}) + .pipelineBarrier(Vk::PipelineStage::Transfer, Vk::PipelineStage::Host, { + {Vk::Access::TransferWrite, Vk::Access::HostRead, _pixels} + }) + .end(); + + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + CORRADE_COMPARE_WITH((ImageView2D{Magnum::PixelFormat::RGBA8Unorm, + _framebuffer.size().xy(), + _pixels.dedicatedMemory().mapRead()}), + Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/vertexcolor.tga"), + /* ARM Mali (Android) has some minor off-by-one differences */ + (DebugTools::CompareImageToFile{_manager, 0.5f, 0.012f})); +} + +void MeshVkTest::cmdDrawNullBindingRobustness2() { + if(!(_deviceRobustness2.enabledFeatures() & DeviceFeature::NullDescriptor)) + CORRADE_SKIP("DeviceFeature::NullDescriptor not supported, can't test."); + + Mesh mesh{MeshLayout{MeshPrimitive::TriangleStrip} + .addBinding(0, sizeof(Vector3)) + .addBinding(1, sizeof(Vector3)) + .addAttribute(0, 0, VertexFormat::Vector3, 0) + .addAttribute(1, 1, VertexFormat::Vector3, 0) + }; + { + Buffer positions{_deviceRobustness2, BufferCreateInfo{ + BufferUsage::VertexBuffer, + sizeof(Vector3)*4 + }, MemoryFlag::HostVisible}; + /** @todo ffs fucking casts!!! */ + Utility::copy(Containers::stridedArrayView(QuadData).slice(&Quad::position), + Containers::arrayCast(Containers::arrayView(positions.dedicatedMemory().map()))); + mesh.addVertexBuffer(0, std::move(positions), 0) + .setCount(4); + } + + Shader shader{_deviceRobustness2, ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/vertexcolor.spv")) + }}; + + ShaderSet shaderSet; + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); + + Pipeline pipeline{_deviceRobustness2, RasterizationPipelineCreateInfo{ + shaderSet, mesh.layout(), _pipelineLayout, _renderPass, 0, 1} + .setViewport({{}, Vector2{_framebuffer.size().xy()}}) + }; + + CommandBuffer cmd = _pool.allocate(); + cmd.begin() + .beginRenderPass(Vk::RenderPassBeginInfo{_renderPass, _framebuffer} + .clearColor(0, 0x1f1f1f_srgbf) + ) + .bindPipeline(pipeline) + .draw(mesh) + .endRenderPass() + .copyImageToBuffer({_color, Vk::ImageLayout::TransferSource, _pixels, { + Vk::BufferImageCopy2D{0, Vk::ImageAspect::Color, 0, {{}, _framebuffer.size().xy()}} + }}) + .pipelineBarrier(Vk::PipelineStage::Transfer, Vk::PipelineStage::Host, { + {Vk::Access::TransferWrite, Vk::Access::HostRead, _pixels} + }) + .end(); + + _queue.submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + CORRADE_COMPARE_WITH((ImageView2D{Magnum::PixelFormat::RGBA8Unorm, + _framebuffer.size().xy(), + _pixels.dedicatedMemory().mapRead()}), + Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/nullcolor.tga"), + /* ARM Mali (Android) has some minor off-by-one differences */ + (DebugTools::CompareImageToFile{_manager})); +} + +void MeshVkTest::cmdDrawZeroCount() { + auto&& data = CmdDrawZeroCountData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + Mesh mesh{MeshLayout{MeshPrimitive::Triangles} + .addBinding(0, sizeof(Vector3)) + .addAttribute(0, 0, VertexFormat::Vector3, 0) + }; + /* Deliberately not setting up any buffer -- the draw() should be a no-op + and thus no draw validation (and error messages) should happen */ + mesh.setCount(data.count) + .setInstanceCount(data.instanceCount); + + Shader shader{device(), ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/flat.spv")) + }}; + + ShaderSet shaderSet; + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); + + Pipeline pipeline{device(), RasterizationPipelineCreateInfo{ + shaderSet, mesh.layout(), _pipelineLayout, _renderPass, 0, 1} + .setViewport({{}, Vector2{_framebuffer.size().xy()}}) + }; + + CommandBuffer cmd = _pool.allocate(); + cmd.begin() + .beginRenderPass(Vk::RenderPassBeginInfo{_renderPass, _framebuffer} + .clearColor(0, 0x1f1f1f_srgbf) + ) + .bindPipeline(pipeline) + .draw(mesh) + .endRenderPass() + .copyImageToBuffer({_color, Vk::ImageLayout::TransferSource, _pixels, { + Vk::BufferImageCopy2D{0, Vk::ImageAspect::Color, 0, {{}, _framebuffer.size().xy()}} + }}) + .pipelineBarrier(Vk::PipelineStage::Transfer, Vk::PipelineStage::Host, { + {Vk::Access::TransferWrite, Vk::Access::HostRead, _pixels} + }) + .end(); + + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + CORRADE_COMPARE_WITH((ImageView2D{Magnum::PixelFormat::RGBA8Unorm, + _framebuffer.size().xy(), + _pixels.dedicatedMemory().mapRead()}), + Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/noop.tga"), + DebugTools::CompareImageToFile{_manager}); +} + +void MeshVkTest::cmdDrawNoCountSet() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Mesh mesh{MeshLayout{MeshPrimitive::Triangles}}; + + Shader shader{device(), ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/noop.spv")) + }}; + + ShaderSet shaderSet; + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); + + Pipeline pipeline{device(), RasterizationPipelineCreateInfo{ + shaderSet, mesh.layout(), _pipelineLayout, _renderPass, 0, 1} + .setViewport({{}, Vector2{_framebuffer.size().xy()}}) + }; + + CommandBuffer cmd = _pool.allocate(); + cmd.begin() + .beginRenderPass(Vk::RenderPassBeginInfo{_renderPass, _framebuffer} + .clearColor(0, 0x1f1f1f_srgbf) + ) + .bindPipeline(pipeline); + + std::ostringstream out; + Error redirectError{&out}; + cmd.draw(mesh); + CORRADE_COMPARE(out.str(), "Vk::CommandBuffer::draw(): Mesh::setCount() was never called, probably a mistake?\n"); +} + +void MeshVkTest::cmdDrawDynamicPrimitive() { + if(!(_deviceExtendedDynamicState.enabledFeatures() & DeviceFeature::ExtendedDynamicState)) + CORRADE_SKIP("DeviceFeature::ExtendedDynamicState not supported, can't test."); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + Mesh mesh{MeshLayout{MeshPrimitive::TriangleStrip} + .addBinding(0, sizeof(Vector3)) + .addAttribute(0, 0, VertexFormat::Vector3, 0) + }; + { + Buffer buffer{_deviceExtendedDynamicState, BufferCreateInfo{ + BufferUsage::VertexBuffer, + sizeof(Vector3)*4 + }, MemoryFlag::HostVisible}; + /** @todo ffs fucking casts!!! */ + Utility::copy( + Containers::stridedArrayView(QuadData).slice(&Quad::position), + Containers::arrayCast(Containers::arrayView(buffer.dedicatedMemory().map()))); + mesh.addVertexBuffer(0, std::move(buffer), 0) + .setCount(4); + } + + Shader shader{_deviceExtendedDynamicState, ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/flat.spv")) + }}; + + ShaderSet shaderSet; + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); + + /* Create the pipeline with Triangles while the mesh is TriangleStrip */ + MeshLayout pipelineLayout{MeshPrimitive::Triangles}; + pipelineLayout + .addBinding(0, sizeof(Vector3)) + .addAttribute(0, 0, VertexFormat::Vector3, 0); + Pipeline pipeline{_deviceExtendedDynamicState, RasterizationPipelineCreateInfo{ + shaderSet, pipelineLayout, _pipelineLayout, _renderPass, 0, 1} + .setViewport({{}, Vector2{_framebuffer.size().xy()}}) + .setDynamicStates(DynamicRasterizationState::MeshPrimitive) + }; + + CommandBuffer cmd = _pool.allocate(); + cmd.begin() + .beginRenderPass(Vk::RenderPassBeginInfo{_renderPass, _framebuffer} + .clearColor(0, 0x1f1f1f_srgbf) + ) + .bindPipeline(pipeline) + .draw(mesh) + .endRenderPass() + .copyImageToBuffer({_color, Vk::ImageLayout::TransferSource, _pixels, { + Vk::BufferImageCopy2D{0, Vk::ImageAspect::Color, 0, {{}, _framebuffer.size().xy()}} + }}) + .pipelineBarrier(Vk::PipelineStage::Transfer, Vk::PipelineStage::Host, { + {Vk::Access::TransferWrite, Vk::Access::HostRead, _pixels} + }) + .end(); + + _queue.submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + CORRADE_COMPARE_WITH((ImageView2D{Magnum::PixelFormat::RGBA8Unorm, + _framebuffer.size().xy(), + _pixels.dedicatedMemory().mapRead()}), + Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/flat.tga"), + DebugTools::CompareImageToFile{_manager}); +} + +void MeshVkTest::cmdDrawDynamicStride() { + if(!(_deviceExtendedDynamicState.enabledFeatures() & DeviceFeature::ExtendedDynamicState)) + CORRADE_SKIP("DeviceFeature::ExtendedDynamicState not supported, can't test."); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + Mesh mesh{MeshLayout{MeshPrimitive::TriangleStrip} + .addBinding(0, sizeof(Vector3)) + .addAttribute(0, 0, VertexFormat::Vector3, 0) + }; + { + Buffer buffer{_deviceExtendedDynamicState, BufferCreateInfo{ + BufferUsage::VertexBuffer, + sizeof(Vector3)*4 + }, MemoryFlag::HostVisible}; + /** @todo ffs fucking casts!!! */ + Utility::copy( + Containers::stridedArrayView(QuadData).slice(&Quad::position), + Containers::arrayCast(Containers::arrayView(buffer.dedicatedMemory().map()))); + mesh.addVertexBuffer(0, std::move(buffer), 0) + .setCount(4); + } + + Shader shader{_deviceExtendedDynamicState, ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/flat.spv")) + }}; + + ShaderSet shaderSet; + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); + + /* Create the pipeline with a 1 kB stride, while the actual stride is + different */ + MeshLayout pipelineLayout{MeshPrimitive::TriangleStrip}; + pipelineLayout + .addBinding(0, 1024) + .addAttribute(0, 0, VertexFormat::Vector3, 0); + Pipeline pipeline{_deviceExtendedDynamicState, RasterizationPipelineCreateInfo{ + shaderSet, pipelineLayout, _pipelineLayout, _renderPass, 0, 1} + .setViewport({{}, Vector2{_framebuffer.size().xy()}}) + .setDynamicStates(DynamicRasterizationState::VertexInputBindingStride) + }; + + CommandBuffer cmd = _pool.allocate(); + cmd.begin() + .beginRenderPass(Vk::RenderPassBeginInfo{_renderPass, _framebuffer} + .clearColor(0, 0x1f1f1f_srgbf) + ) + .bindPipeline(pipeline) + .draw(mesh) + .endRenderPass() + .copyImageToBuffer({_color, Vk::ImageLayout::TransferSource, _pixels, { + Vk::BufferImageCopy2D{0, Vk::ImageAspect::Color, 0, {{}, _framebuffer.size().xy()}} + }}) + .pipelineBarrier(Vk::PipelineStage::Transfer, Vk::PipelineStage::Host, { + {Vk::Access::TransferWrite, Vk::Access::HostRead, _pixels} + }) + .end(); + + _queue.submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + CORRADE_COMPARE_WITH((ImageView2D{Magnum::PixelFormat::RGBA8Unorm, + _framebuffer.size().xy(), + _pixels.dedicatedMemory().mapRead()}), + Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/flat.tga"), + DebugTools::CompareImageToFile{_manager}); +} + +void MeshVkTest::cmdDrawDynamicStrideInsufficientImplementation() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + if(device().isExtensionEnabled()) + CORRADE_SKIP("VK_EXT_extended_dynamic_state enabled, can't test."); + + Mesh mesh{MeshLayout{MeshPrimitive::TriangleStrip} + .addBinding(0, sizeof(Vector3)) + .addAttribute(0, 0, VertexFormat::Vector3, 0) + }; + { + Buffer buffer{device(), BufferCreateInfo{ + BufferUsage::VertexBuffer, + sizeof(Vector3)*4 + }, MemoryFlag::HostVisible}; + /** @todo ffs fucking casts!!! */ + Utility::copy( + Containers::stridedArrayView(QuadData).slice(&Quad::position), + Containers::arrayCast(Containers::arrayView(buffer.dedicatedMemory().map()))); + mesh.addVertexBuffer(0, std::move(buffer), 0) + .setCount(4); + } + + Shader shader{device(), ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "MeshTestFiles/flat.spv")) + }}; + + ShaderSet shaderSet; + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); + + /* Create a pipeline without any dynamic state and then wrap it with fake + enabled vertex input binding stride -- doing so directly would trigger + validation layer failures (using dynamic state from a non-enabled ext), + which we don't want */ + Pipeline pipeline{device(), RasterizationPipelineCreateInfo{ + shaderSet, mesh.layout(), _pipelineLayout, _renderPass, 0, 1} + .setViewport({{}, Vector2{_framebuffer.size().xy()}}) + }; + Pipeline fakeDynamicStatePipeline = Pipeline::wrap(device(), + PipelineBindPoint::Rasterization, pipeline, + DynamicRasterizationState::VertexInputBindingStride); + + CommandBuffer cmd = _pool.allocate(); + cmd.begin() + .beginRenderPass(Vk::RenderPassBeginInfo{_renderPass, _framebuffer} + .clearColor(0, 0x1f1f1f_srgbf) + ) + .bindPipeline(fakeDynamicStatePipeline); + + std::ostringstream out; + Error redirectError{&out}; + cmd.draw(mesh); + CORRADE_COMPARE(out.str(), "Vk::CommandBuffer::draw(): dynamic strides supplied for an implementation without extended dynamic state\n"); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::MeshVkTest) diff --git a/src/Magnum/Vk/Test/configure.h.cmake b/src/Magnum/Vk/Test/configure.h.cmake index 78fdf20a1..cbb448d13 100644 --- a/src/Magnum/Vk/Test/configure.h.cmake +++ b/src/Magnum/Vk/Test/configure.h.cmake @@ -23,4 +23,6 @@ DEALINGS IN THE SOFTWARE. */ +#cmakedefine ANYIMAGEIMPORTER_PLUGIN_FILENAME "${ANYIMAGEIMPORTER_PLUGIN_FILENAME}" +#cmakedefine TGAIMPORTER_PLUGIN_FILENAME "${TGAIMPORTER_PLUGIN_FILENAME}" #define VK_TEST_DIR "${VK_TEST_DIR}" diff --git a/src/Magnum/Vk/VertexFormat.h b/src/Magnum/Vk/VertexFormat.h index 6aaae1243..55e6c65f7 100644 --- a/src/Magnum/Vk/VertexFormat.h +++ b/src/Magnum/Vk/VertexFormat.h @@ -363,6 +363,7 @@ cast to @ref VertexFormat. Not all generic vertex formats have a Vulkan equivalent and this function expects that given format is available. Use @ref hasVertexFormat() to query availability of given format. +@see @ref meshIndexType(), @ref meshPrimitive() */ MAGNUM_VK_EXPORT VertexFormat vertexFormat(Magnum::VertexFormat format); diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 50207fcdb..2d3fc2810 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -97,6 +97,8 @@ enum class MemoryFlag: UnsignedInt; typedef Containers::EnumSet MemoryFlags; enum class MemoryHeapFlag: UnsignedInt; typedef Containers::EnumSet MemoryHeapFlags; +class Mesh; +enum class MeshIndexType: Int; class MeshLayout; enum class MeshPrimitive: Int; class Pipeline; From 34cc5e90e5e2026efe10a1de2bc24c5c24ca2900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 9 Feb 2021 00:15:22 +0100 Subject: [PATCH 13/44] Vk: remove redundant namespace prefixes from a test. --- src/Magnum/Vk/Test/PipelineVkTest.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Magnum/Vk/Test/PipelineVkTest.cpp b/src/Magnum/Vk/Test/PipelineVkTest.cpp index 789286055..a10c84751 100644 --- a/src/Magnum/Vk/Test/PipelineVkTest.cpp +++ b/src/Magnum/Vk/Test/PipelineVkTest.cpp @@ -125,8 +125,8 @@ void PipelineVkTest::constructRasterization() { MeshLayout meshLayout{MeshPrimitive::Triangles}; meshLayout .addBinding(0, 2*4*4) - .addAttribute(0, 0, Vk::VertexFormat::Vector4, 0) - .addAttribute(1, 0, Vk::VertexFormat::Vector4, 4*4); + .addAttribute(0, 0, VertexFormat::Vector4, 0) + .addAttribute(1, 0, VertexFormat::Vector4, 4*4); PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; @@ -195,8 +195,8 @@ void PipelineVkTest::constructRasterizationViewportNotSetDiscardEnabled() { MeshLayout meshLayout{MeshPrimitive::Triangles}; meshLayout .addBinding(0, 2*4*4) - .addAttribute(0, 0, Vk::VertexFormat::Vector4, 0) - .addAttribute(1, 0, Vk::VertexFormat::Vector4, 4*4); + .addAttribute(0, 0, VertexFormat::Vector4, 0) + .addAttribute(1, 0, VertexFormat::Vector4, 4*4); PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; @@ -238,8 +238,8 @@ void PipelineVkTest::constructRasterizationViewportNotSetDynamic() { MeshLayout meshLayout{MeshPrimitive::Triangles}; meshLayout .addBinding(0, 2*4*4) - .addAttribute(0, 0, Vk::VertexFormat::Vector4, 0) - .addAttribute(1, 0, Vk::VertexFormat::Vector4, 4*4); + .addAttribute(0, 0, VertexFormat::Vector4, 0) + .addAttribute(1, 0, VertexFormat::Vector4, 4*4); PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; @@ -308,8 +308,8 @@ void PipelineVkTest::constructMove() { MeshLayout meshLayout{MeshPrimitive::Triangles}; meshLayout .addBinding(0, 2*4*4) - .addAttribute(0, 0, Vk::VertexFormat::Vector4, 0) - .addAttribute(1, 0, Vk::VertexFormat::Vector4, 4*4); + .addAttribute(0, 0, VertexFormat::Vector4, 0) + .addAttribute(1, 0, VertexFormat::Vector4, 4*4); PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; @@ -368,8 +368,8 @@ void PipelineVkTest::wrapRasterization() { MeshLayout meshLayout{MeshPrimitive::Triangles}; meshLayout .addBinding(0, 2*4*4) - .addAttribute(0, 0, Vk::VertexFormat::Vector4, 0) - .addAttribute(1, 0, Vk::VertexFormat::Vector4, 4*4); + .addAttribute(0, 0, VertexFormat::Vector4, 0) + .addAttribute(1, 0, VertexFormat::Vector4, 4*4); PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; @@ -472,8 +472,8 @@ void PipelineVkTest::cmdBindRasterization() { MeshLayout meshLayout{MeshPrimitive::Triangles}; meshLayout .addBinding(0, 2*4*4) - .addAttribute(0, 0, Vk::VertexFormat::Vector4, 0) - .addAttribute(1, 0, Vk::VertexFormat::Vector4, 4*4); + .addAttribute(0, 0, VertexFormat::Vector4, 0) + .addAttribute(1, 0, VertexFormat::Vector4, 4*4); PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; From eb91cf768a05ead0c7c2ed974b6637764baeba5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 9 Feb 2021 00:15:40 +0100 Subject: [PATCH 14/44] Vk: fix irrelevant copypasted code + comment in a test. --- src/Magnum/Vk/Test/PipelineVkTest.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Magnum/Vk/Test/PipelineVkTest.cpp b/src/Magnum/Vk/Test/PipelineVkTest.cpp index a10c84751..52412bc7d 100644 --- a/src/Magnum/Vk/Test/PipelineVkTest.cpp +++ b/src/Magnum/Vk/Test/PipelineVkTest.cpp @@ -451,8 +451,7 @@ void PipelineVkTest::dynamicRasterizationStatesNotRasterization() { void PipelineVkTest::cmdBindRasterization() { CommandPool pool{device(), CommandPoolCreateInfo{ - /* This might blow up if queue() isn't the one matching this family */ - device().properties().pickQueueFamily(QueueFlag::Graphics|QueueFlag::Compute)}}; + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; RenderPass renderPass{device(), RenderPassCreateInfo{} .setAttachments({ From 997d8014105e4e2a2a6d899ed720f44c549d3f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 9 Feb 2021 00:16:27 +0100 Subject: [PATCH 15/44] Vk: put a test in a correct IDE folder. Why it isn't 2067 already so I can use CMake 3.10 or which one that allows me to implicitly set the folder for a directory. --- src/Magnum/Vk/Test/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index ab7e1d679..3effaf840 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -150,6 +150,7 @@ set_target_properties( VkResultTest VkRenderPassTest VkShaderTest + VkShaderSetTest VkStructureHelpersTest VkVersionTest VkVertexFormatTest From afe4fb76e25bf814894ba4ef7b6d8ed05069805b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 9 Feb 2021 00:22:34 +0100 Subject: [PATCH 16/44] Vk: implicitly enable VK_EXT_extended_dynamic_state. All the heavyweight features from this extension have to be explicitly enabled through device features, so it's okay to have it always. --- src/Magnum/Vk/Device.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index ec74756e1..0f719eea2 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -191,10 +191,12 @@ DeviceCreateInfo::DeviceCreateInfo(DeviceProperties& deviceProperties, const Ext addEnabledExtensions(); } - /* Enable the KHR_copy_commands2 extension. Not in any Vulkan version - yet. */ + /* Enable the KHR_copy_commands2 and EXT_extended_dynamic_state + extensions. Not in any Vulkan version yet. */ if(extensionProperties->isSupported()) addEnabledExtensions(); + if(extensionProperties->isSupported()) + addEnabledExtensions(); /* Enable the KHR_portability_subset extension, which *has to be* enabled when available. Not enabling any of its features though, From a1d968c643f2c69c5e113602bdda1c3fecb2f2b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 9 Feb 2021 22:18:54 +0100 Subject: [PATCH 17/44] external: add a SPIR-V header. Commit faa570afbc91ac73d594d787486bcf8f2df1ace0 from https://github.com/KhronosGroup/SPIRV-Headers. --- src/MagnumExternal/Vulkan/CMakeLists.txt | 2 +- src/MagnumExternal/Vulkan/spirv.h | 2282 ++++++++++++++++++++++ 2 files changed, 2283 insertions(+), 1 deletion(-) create mode 100644 src/MagnumExternal/Vulkan/spirv.h diff --git a/src/MagnumExternal/Vulkan/CMakeLists.txt b/src/MagnumExternal/Vulkan/CMakeLists.txt index 05cf87f51..36bffb9dc 100644 --- a/src/MagnumExternal/Vulkan/CMakeLists.txt +++ b/src/MagnumExternal/Vulkan/CMakeLists.txt @@ -33,4 +33,4 @@ if(NOT BUILD_STATIC OR BUILD_STATIC_PIC) endif() set_target_properties(MagnumFlextVkObjects PROPERTIES FOLDER "MagnumExternal/Vulkan") -install(FILES flextVk.h flextVkGlobal.h DESTINATION ${MAGNUM_EXTERNAL_INCLUDE_INSTALL_DIR}/Vulkan) +install(FILES flextVk.h flextVkGlobal.h spirv.h DESTINATION ${MAGNUM_EXTERNAL_INCLUDE_INSTALL_DIR}/Vulkan) diff --git a/src/MagnumExternal/Vulkan/spirv.h b/src/MagnumExternal/Vulkan/spirv.h new file mode 100644 index 000000000..41a650bf8 --- /dev/null +++ b/src/MagnumExternal/Vulkan/spirv.h @@ -0,0 +1,2282 @@ +/* +** 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. +*/ + +/* +** This header is automatically generated by the same tool that creates +** the Binary Section of the SPIR-V specification. +*/ + +/* +** Enumeration tokens for SPIR-V, in various styles: +** C, C++, C++11, JSON, Lua, Python, C#, D +** +** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +** - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +** - C# will use enum classes in the Specification class located in the "Spv" namespace, +** e.g.: Spv.Specification.SourceLanguage.GLSL +** - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +** +** Some tokens act like mask values, which can be OR'd together, +** while others are mutually exclusive. The mask-like ones have +** "Mask" in their name, and a parallel enum that has the shift +** amount (1 << x) for each corresponding enumerant. +*/ + +#ifndef spirv_H +#define spirv_H + +typedef unsigned int SpvId; + +#define SPV_VERSION 0x10500 +#define SPV_REVISION 4 + +static const unsigned int SpvMagicNumber = 0x07230203; +static const unsigned int SpvVersion = 0x00010500; +static const unsigned int SpvRevision = 4; +static const unsigned int SpvOpCodeMask = 0xffff; +static const unsigned int SpvWordCountShift = 16; + +typedef enum SpvSourceLanguage_ { + SpvSourceLanguageUnknown = 0, + SpvSourceLanguageESSL = 1, + SpvSourceLanguageGLSL = 2, + SpvSourceLanguageOpenCL_C = 3, + SpvSourceLanguageOpenCL_CPP = 4, + SpvSourceLanguageHLSL = 5, + SpvSourceLanguageMax = 0x7fffffff, +} SpvSourceLanguage; + +typedef enum SpvExecutionModel_ { + SpvExecutionModelVertex = 0, + SpvExecutionModelTessellationControl = 1, + SpvExecutionModelTessellationEvaluation = 2, + SpvExecutionModelGeometry = 3, + SpvExecutionModelFragment = 4, + SpvExecutionModelGLCompute = 5, + SpvExecutionModelKernel = 6, + SpvExecutionModelTaskNV = 5267, + SpvExecutionModelMeshNV = 5268, + SpvExecutionModelRayGenerationKHR = 5313, + SpvExecutionModelRayGenerationNV = 5313, + SpvExecutionModelIntersectionKHR = 5314, + SpvExecutionModelIntersectionNV = 5314, + SpvExecutionModelAnyHitKHR = 5315, + SpvExecutionModelAnyHitNV = 5315, + SpvExecutionModelClosestHitKHR = 5316, + SpvExecutionModelClosestHitNV = 5316, + SpvExecutionModelMissKHR = 5317, + SpvExecutionModelMissNV = 5317, + SpvExecutionModelCallableKHR = 5318, + SpvExecutionModelCallableNV = 5318, + SpvExecutionModelMax = 0x7fffffff, +} SpvExecutionModel; + +typedef enum SpvAddressingModel_ { + SpvAddressingModelLogical = 0, + SpvAddressingModelPhysical32 = 1, + SpvAddressingModelPhysical64 = 2, + SpvAddressingModelPhysicalStorageBuffer64 = 5348, + SpvAddressingModelPhysicalStorageBuffer64EXT = 5348, + SpvAddressingModelMax = 0x7fffffff, +} SpvAddressingModel; + +typedef enum SpvMemoryModel_ { + SpvMemoryModelSimple = 0, + SpvMemoryModelGLSL450 = 1, + SpvMemoryModelOpenCL = 2, + SpvMemoryModelVulkan = 3, + SpvMemoryModelVulkanKHR = 3, + SpvMemoryModelMax = 0x7fffffff, +} SpvMemoryModel; + +typedef enum SpvExecutionMode_ { + SpvExecutionModeInvocations = 0, + SpvExecutionModeSpacingEqual = 1, + SpvExecutionModeSpacingFractionalEven = 2, + SpvExecutionModeSpacingFractionalOdd = 3, + SpvExecutionModeVertexOrderCw = 4, + SpvExecutionModeVertexOrderCcw = 5, + SpvExecutionModePixelCenterInteger = 6, + SpvExecutionModeOriginUpperLeft = 7, + SpvExecutionModeOriginLowerLeft = 8, + SpvExecutionModeEarlyFragmentTests = 9, + SpvExecutionModePointMode = 10, + SpvExecutionModeXfb = 11, + SpvExecutionModeDepthReplacing = 12, + SpvExecutionModeDepthGreater = 14, + SpvExecutionModeDepthLess = 15, + SpvExecutionModeDepthUnchanged = 16, + SpvExecutionModeLocalSize = 17, + SpvExecutionModeLocalSizeHint = 18, + SpvExecutionModeInputPoints = 19, + SpvExecutionModeInputLines = 20, + SpvExecutionModeInputLinesAdjacency = 21, + SpvExecutionModeTriangles = 22, + SpvExecutionModeInputTrianglesAdjacency = 23, + SpvExecutionModeQuads = 24, + SpvExecutionModeIsolines = 25, + SpvExecutionModeOutputVertices = 26, + SpvExecutionModeOutputPoints = 27, + SpvExecutionModeOutputLineStrip = 28, + SpvExecutionModeOutputTriangleStrip = 29, + SpvExecutionModeVecTypeHint = 30, + SpvExecutionModeContractionOff = 31, + SpvExecutionModeInitializer = 33, + SpvExecutionModeFinalizer = 34, + SpvExecutionModeSubgroupSize = 35, + SpvExecutionModeSubgroupsPerWorkgroup = 36, + SpvExecutionModeSubgroupsPerWorkgroupId = 37, + SpvExecutionModeLocalSizeId = 38, + SpvExecutionModeLocalSizeHintId = 39, + SpvExecutionModePostDepthCoverage = 4446, + SpvExecutionModeDenormPreserve = 4459, + SpvExecutionModeDenormFlushToZero = 4460, + SpvExecutionModeSignedZeroInfNanPreserve = 4461, + SpvExecutionModeRoundingModeRTE = 4462, + SpvExecutionModeRoundingModeRTZ = 4463, + SpvExecutionModeStencilRefReplacingEXT = 5027, + SpvExecutionModeOutputLinesNV = 5269, + SpvExecutionModeOutputPrimitivesNV = 5270, + SpvExecutionModeDerivativeGroupQuadsNV = 5289, + SpvExecutionModeDerivativeGroupLinearNV = 5290, + SpvExecutionModeOutputTrianglesNV = 5298, + SpvExecutionModePixelInterlockOrderedEXT = 5366, + SpvExecutionModePixelInterlockUnorderedEXT = 5367, + SpvExecutionModeSampleInterlockOrderedEXT = 5368, + SpvExecutionModeSampleInterlockUnorderedEXT = 5369, + SpvExecutionModeShadingRateInterlockOrderedEXT = 5370, + SpvExecutionModeShadingRateInterlockUnorderedEXT = 5371, + SpvExecutionModeSharedLocalMemorySizeINTEL = 5618, + SpvExecutionModeRoundingModeRTPINTEL = 5620, + SpvExecutionModeRoundingModeRTNINTEL = 5621, + SpvExecutionModeFloatingPointModeALTINTEL = 5622, + SpvExecutionModeFloatingPointModeIEEEINTEL = 5623, + SpvExecutionModeMaxWorkgroupSizeINTEL = 5893, + SpvExecutionModeMaxWorkDimINTEL = 5894, + SpvExecutionModeNoGlobalOffsetINTEL = 5895, + SpvExecutionModeNumSIMDWorkitemsINTEL = 5896, + SpvExecutionModeSchedulerTargetFmaxMhzINTEL = 5903, + SpvExecutionModeMax = 0x7fffffff, +} SpvExecutionMode; + +typedef enum SpvStorageClass_ { + SpvStorageClassUniformConstant = 0, + SpvStorageClassInput = 1, + SpvStorageClassUniform = 2, + SpvStorageClassOutput = 3, + SpvStorageClassWorkgroup = 4, + SpvStorageClassCrossWorkgroup = 5, + SpvStorageClassPrivate = 6, + SpvStorageClassFunction = 7, + SpvStorageClassGeneric = 8, + SpvStorageClassPushConstant = 9, + SpvStorageClassAtomicCounter = 10, + SpvStorageClassImage = 11, + SpvStorageClassStorageBuffer = 12, + SpvStorageClassCallableDataKHR = 5328, + SpvStorageClassCallableDataNV = 5328, + SpvStorageClassIncomingCallableDataKHR = 5329, + SpvStorageClassIncomingCallableDataNV = 5329, + SpvStorageClassRayPayloadKHR = 5338, + SpvStorageClassRayPayloadNV = 5338, + SpvStorageClassHitAttributeKHR = 5339, + SpvStorageClassHitAttributeNV = 5339, + SpvStorageClassIncomingRayPayloadKHR = 5342, + SpvStorageClassIncomingRayPayloadNV = 5342, + SpvStorageClassShaderRecordBufferKHR = 5343, + SpvStorageClassShaderRecordBufferNV = 5343, + SpvStorageClassPhysicalStorageBuffer = 5349, + SpvStorageClassPhysicalStorageBufferEXT = 5349, + SpvStorageClassCodeSectionINTEL = 5605, + SpvStorageClassDeviceOnlyINTEL = 5936, + SpvStorageClassHostOnlyINTEL = 5937, + SpvStorageClassMax = 0x7fffffff, +} SpvStorageClass; + +typedef enum SpvDim_ { + SpvDim1D = 0, + SpvDim2D = 1, + SpvDim3D = 2, + SpvDimCube = 3, + SpvDimRect = 4, + SpvDimBuffer = 5, + SpvDimSubpassData = 6, + SpvDimMax = 0x7fffffff, +} SpvDim; + +typedef enum SpvSamplerAddressingMode_ { + SpvSamplerAddressingModeNone = 0, + SpvSamplerAddressingModeClampToEdge = 1, + SpvSamplerAddressingModeClamp = 2, + SpvSamplerAddressingModeRepeat = 3, + SpvSamplerAddressingModeRepeatMirrored = 4, + SpvSamplerAddressingModeMax = 0x7fffffff, +} SpvSamplerAddressingMode; + +typedef enum SpvSamplerFilterMode_ { + SpvSamplerFilterModeNearest = 0, + SpvSamplerFilterModeLinear = 1, + SpvSamplerFilterModeMax = 0x7fffffff, +} SpvSamplerFilterMode; + +typedef enum SpvImageFormat_ { + SpvImageFormatUnknown = 0, + SpvImageFormatRgba32f = 1, + SpvImageFormatRgba16f = 2, + SpvImageFormatR32f = 3, + SpvImageFormatRgba8 = 4, + SpvImageFormatRgba8Snorm = 5, + SpvImageFormatRg32f = 6, + SpvImageFormatRg16f = 7, + SpvImageFormatR11fG11fB10f = 8, + SpvImageFormatR16f = 9, + SpvImageFormatRgba16 = 10, + SpvImageFormatRgb10A2 = 11, + SpvImageFormatRg16 = 12, + SpvImageFormatRg8 = 13, + SpvImageFormatR16 = 14, + SpvImageFormatR8 = 15, + SpvImageFormatRgba16Snorm = 16, + SpvImageFormatRg16Snorm = 17, + SpvImageFormatRg8Snorm = 18, + SpvImageFormatR16Snorm = 19, + SpvImageFormatR8Snorm = 20, + SpvImageFormatRgba32i = 21, + SpvImageFormatRgba16i = 22, + SpvImageFormatRgba8i = 23, + SpvImageFormatR32i = 24, + SpvImageFormatRg32i = 25, + SpvImageFormatRg16i = 26, + SpvImageFormatRg8i = 27, + SpvImageFormatR16i = 28, + SpvImageFormatR8i = 29, + SpvImageFormatRgba32ui = 30, + SpvImageFormatRgba16ui = 31, + SpvImageFormatRgba8ui = 32, + SpvImageFormatR32ui = 33, + SpvImageFormatRgb10a2ui = 34, + SpvImageFormatRg32ui = 35, + SpvImageFormatRg16ui = 36, + SpvImageFormatRg8ui = 37, + SpvImageFormatR16ui = 38, + SpvImageFormatR8ui = 39, + SpvImageFormatR64ui = 40, + SpvImageFormatR64i = 41, + SpvImageFormatMax = 0x7fffffff, +} SpvImageFormat; + +typedef enum SpvImageChannelOrder_ { + SpvImageChannelOrderR = 0, + SpvImageChannelOrderA = 1, + SpvImageChannelOrderRG = 2, + SpvImageChannelOrderRA = 3, + SpvImageChannelOrderRGB = 4, + SpvImageChannelOrderRGBA = 5, + SpvImageChannelOrderBGRA = 6, + SpvImageChannelOrderARGB = 7, + SpvImageChannelOrderIntensity = 8, + SpvImageChannelOrderLuminance = 9, + SpvImageChannelOrderRx = 10, + SpvImageChannelOrderRGx = 11, + SpvImageChannelOrderRGBx = 12, + SpvImageChannelOrderDepth = 13, + SpvImageChannelOrderDepthStencil = 14, + SpvImageChannelOrdersRGB = 15, + SpvImageChannelOrdersRGBx = 16, + SpvImageChannelOrdersRGBA = 17, + SpvImageChannelOrdersBGRA = 18, + SpvImageChannelOrderABGR = 19, + SpvImageChannelOrderMax = 0x7fffffff, +} SpvImageChannelOrder; + +typedef enum SpvImageChannelDataType_ { + SpvImageChannelDataTypeSnormInt8 = 0, + SpvImageChannelDataTypeSnormInt16 = 1, + SpvImageChannelDataTypeUnormInt8 = 2, + SpvImageChannelDataTypeUnormInt16 = 3, + SpvImageChannelDataTypeUnormShort565 = 4, + SpvImageChannelDataTypeUnormShort555 = 5, + SpvImageChannelDataTypeUnormInt101010 = 6, + SpvImageChannelDataTypeSignedInt8 = 7, + SpvImageChannelDataTypeSignedInt16 = 8, + SpvImageChannelDataTypeSignedInt32 = 9, + SpvImageChannelDataTypeUnsignedInt8 = 10, + SpvImageChannelDataTypeUnsignedInt16 = 11, + SpvImageChannelDataTypeUnsignedInt32 = 12, + SpvImageChannelDataTypeHalfFloat = 13, + SpvImageChannelDataTypeFloat = 14, + SpvImageChannelDataTypeUnormInt24 = 15, + SpvImageChannelDataTypeUnormInt101010_2 = 16, + SpvImageChannelDataTypeMax = 0x7fffffff, +} SpvImageChannelDataType; + +typedef enum SpvImageOperandsShift_ { + SpvImageOperandsBiasShift = 0, + SpvImageOperandsLodShift = 1, + SpvImageOperandsGradShift = 2, + SpvImageOperandsConstOffsetShift = 3, + SpvImageOperandsOffsetShift = 4, + SpvImageOperandsConstOffsetsShift = 5, + SpvImageOperandsSampleShift = 6, + SpvImageOperandsMinLodShift = 7, + SpvImageOperandsMakeTexelAvailableShift = 8, + SpvImageOperandsMakeTexelAvailableKHRShift = 8, + SpvImageOperandsMakeTexelVisibleShift = 9, + SpvImageOperandsMakeTexelVisibleKHRShift = 9, + SpvImageOperandsNonPrivateTexelShift = 10, + SpvImageOperandsNonPrivateTexelKHRShift = 10, + SpvImageOperandsVolatileTexelShift = 11, + SpvImageOperandsVolatileTexelKHRShift = 11, + SpvImageOperandsSignExtendShift = 12, + SpvImageOperandsZeroExtendShift = 13, + SpvImageOperandsMax = 0x7fffffff, +} SpvImageOperandsShift; + +typedef enum SpvImageOperandsMask_ { + SpvImageOperandsMaskNone = 0, + SpvImageOperandsBiasMask = 0x00000001, + SpvImageOperandsLodMask = 0x00000002, + SpvImageOperandsGradMask = 0x00000004, + SpvImageOperandsConstOffsetMask = 0x00000008, + SpvImageOperandsOffsetMask = 0x00000010, + SpvImageOperandsConstOffsetsMask = 0x00000020, + SpvImageOperandsSampleMask = 0x00000040, + SpvImageOperandsMinLodMask = 0x00000080, + SpvImageOperandsMakeTexelAvailableMask = 0x00000100, + SpvImageOperandsMakeTexelAvailableKHRMask = 0x00000100, + SpvImageOperandsMakeTexelVisibleMask = 0x00000200, + SpvImageOperandsMakeTexelVisibleKHRMask = 0x00000200, + SpvImageOperandsNonPrivateTexelMask = 0x00000400, + SpvImageOperandsNonPrivateTexelKHRMask = 0x00000400, + SpvImageOperandsVolatileTexelMask = 0x00000800, + SpvImageOperandsVolatileTexelKHRMask = 0x00000800, + SpvImageOperandsSignExtendMask = 0x00001000, + SpvImageOperandsZeroExtendMask = 0x00002000, +} SpvImageOperandsMask; + +typedef enum SpvFPFastMathModeShift_ { + SpvFPFastMathModeNotNaNShift = 0, + SpvFPFastMathModeNotInfShift = 1, + SpvFPFastMathModeNSZShift = 2, + SpvFPFastMathModeAllowRecipShift = 3, + SpvFPFastMathModeFastShift = 4, + SpvFPFastMathModeAllowContractFastINTELShift = 16, + SpvFPFastMathModeAllowReassocINTELShift = 17, + SpvFPFastMathModeMax = 0x7fffffff, +} SpvFPFastMathModeShift; + +typedef enum SpvFPFastMathModeMask_ { + SpvFPFastMathModeMaskNone = 0, + SpvFPFastMathModeNotNaNMask = 0x00000001, + SpvFPFastMathModeNotInfMask = 0x00000002, + SpvFPFastMathModeNSZMask = 0x00000004, + SpvFPFastMathModeAllowRecipMask = 0x00000008, + SpvFPFastMathModeFastMask = 0x00000010, + SpvFPFastMathModeAllowContractFastINTELMask = 0x00010000, + SpvFPFastMathModeAllowReassocINTELMask = 0x00020000, +} SpvFPFastMathModeMask; + +typedef enum SpvFPRoundingMode_ { + SpvFPRoundingModeRTE = 0, + SpvFPRoundingModeRTZ = 1, + SpvFPRoundingModeRTP = 2, + SpvFPRoundingModeRTN = 3, + SpvFPRoundingModeMax = 0x7fffffff, +} SpvFPRoundingMode; + +typedef enum SpvLinkageType_ { + SpvLinkageTypeExport = 0, + SpvLinkageTypeImport = 1, + SpvLinkageTypeMax = 0x7fffffff, +} SpvLinkageType; + +typedef enum SpvAccessQualifier_ { + SpvAccessQualifierReadOnly = 0, + SpvAccessQualifierWriteOnly = 1, + SpvAccessQualifierReadWrite = 2, + SpvAccessQualifierMax = 0x7fffffff, +} SpvAccessQualifier; + +typedef enum SpvFunctionParameterAttribute_ { + SpvFunctionParameterAttributeZext = 0, + SpvFunctionParameterAttributeSext = 1, + SpvFunctionParameterAttributeByVal = 2, + SpvFunctionParameterAttributeSret = 3, + SpvFunctionParameterAttributeNoAlias = 4, + SpvFunctionParameterAttributeNoCapture = 5, + SpvFunctionParameterAttributeNoWrite = 6, + SpvFunctionParameterAttributeNoReadWrite = 7, + SpvFunctionParameterAttributeMax = 0x7fffffff, +} SpvFunctionParameterAttribute; + +typedef enum SpvDecoration_ { + SpvDecorationRelaxedPrecision = 0, + SpvDecorationSpecId = 1, + SpvDecorationBlock = 2, + SpvDecorationBufferBlock = 3, + SpvDecorationRowMajor = 4, + SpvDecorationColMajor = 5, + SpvDecorationArrayStride = 6, + SpvDecorationMatrixStride = 7, + SpvDecorationGLSLShared = 8, + SpvDecorationGLSLPacked = 9, + SpvDecorationCPacked = 10, + SpvDecorationBuiltIn = 11, + SpvDecorationNoPerspective = 13, + SpvDecorationFlat = 14, + SpvDecorationPatch = 15, + SpvDecorationCentroid = 16, + SpvDecorationSample = 17, + SpvDecorationInvariant = 18, + SpvDecorationRestrict = 19, + SpvDecorationAliased = 20, + SpvDecorationVolatile = 21, + SpvDecorationConstant = 22, + SpvDecorationCoherent = 23, + SpvDecorationNonWritable = 24, + SpvDecorationNonReadable = 25, + SpvDecorationUniform = 26, + SpvDecorationUniformId = 27, + SpvDecorationSaturatedConversion = 28, + SpvDecorationStream = 29, + SpvDecorationLocation = 30, + SpvDecorationComponent = 31, + SpvDecorationIndex = 32, + SpvDecorationBinding = 33, + SpvDecorationDescriptorSet = 34, + SpvDecorationOffset = 35, + SpvDecorationXfbBuffer = 36, + SpvDecorationXfbStride = 37, + SpvDecorationFuncParamAttr = 38, + SpvDecorationFPRoundingMode = 39, + SpvDecorationFPFastMathMode = 40, + SpvDecorationLinkageAttributes = 41, + SpvDecorationNoContraction = 42, + SpvDecorationInputAttachmentIndex = 43, + SpvDecorationAlignment = 44, + SpvDecorationMaxByteOffset = 45, + SpvDecorationAlignmentId = 46, + SpvDecorationMaxByteOffsetId = 47, + SpvDecorationNoSignedWrap = 4469, + SpvDecorationNoUnsignedWrap = 4470, + SpvDecorationExplicitInterpAMD = 4999, + SpvDecorationOverrideCoverageNV = 5248, + SpvDecorationPassthroughNV = 5250, + SpvDecorationViewportRelativeNV = 5252, + SpvDecorationSecondaryViewportRelativeNV = 5256, + SpvDecorationPerPrimitiveNV = 5271, + SpvDecorationPerViewNV = 5272, + SpvDecorationPerTaskNV = 5273, + SpvDecorationPerVertexNV = 5285, + SpvDecorationNonUniform = 5300, + SpvDecorationNonUniformEXT = 5300, + SpvDecorationRestrictPointer = 5355, + SpvDecorationRestrictPointerEXT = 5355, + SpvDecorationAliasedPointer = 5356, + SpvDecorationAliasedPointerEXT = 5356, + SpvDecorationSIMTCallINTEL = 5599, + SpvDecorationReferencedIndirectlyINTEL = 5602, + SpvDecorationClobberINTEL = 5607, + SpvDecorationSideEffectsINTEL = 5608, + SpvDecorationVectorComputeVariableINTEL = 5624, + SpvDecorationFuncParamIOKindINTEL = 5625, + SpvDecorationVectorComputeFunctionINTEL = 5626, + SpvDecorationStackCallINTEL = 5627, + SpvDecorationGlobalVariableOffsetINTEL = 5628, + SpvDecorationCounterBuffer = 5634, + SpvDecorationHlslCounterBufferGOOGLE = 5634, + SpvDecorationHlslSemanticGOOGLE = 5635, + SpvDecorationUserSemantic = 5635, + SpvDecorationUserTypeGOOGLE = 5636, + SpvDecorationFunctionRoundingModeINTEL = 5822, + SpvDecorationFunctionDenormModeINTEL = 5823, + SpvDecorationRegisterINTEL = 5825, + SpvDecorationMemoryINTEL = 5826, + SpvDecorationNumbanksINTEL = 5827, + SpvDecorationBankwidthINTEL = 5828, + SpvDecorationMaxPrivateCopiesINTEL = 5829, + SpvDecorationSinglepumpINTEL = 5830, + SpvDecorationDoublepumpINTEL = 5831, + SpvDecorationMaxReplicatesINTEL = 5832, + SpvDecorationSimpleDualPortINTEL = 5833, + SpvDecorationMergeINTEL = 5834, + SpvDecorationBankBitsINTEL = 5835, + SpvDecorationForcePow2DepthINTEL = 5836, + SpvDecorationBurstCoalesceINTEL = 5899, + SpvDecorationCacheSizeINTEL = 5900, + SpvDecorationDontStaticallyCoalesceINTEL = 5901, + SpvDecorationPrefetchINTEL = 5902, + SpvDecorationStallEnableINTEL = 5905, + SpvDecorationFuseLoopsInFunctionINTEL = 5907, + SpvDecorationBufferLocationINTEL = 5921, + SpvDecorationIOPipeStorageINTEL = 5944, + SpvDecorationFunctionFloatingPointModeINTEL = 6080, + SpvDecorationSingleElementVectorINTEL = 6085, + SpvDecorationVectorComputeCallableFunctionINTEL = 6087, + SpvDecorationMax = 0x7fffffff, +} SpvDecoration; + +typedef enum SpvBuiltIn_ { + SpvBuiltInPosition = 0, + SpvBuiltInPointSize = 1, + SpvBuiltInClipDistance = 3, + SpvBuiltInCullDistance = 4, + SpvBuiltInVertexId = 5, + SpvBuiltInInstanceId = 6, + SpvBuiltInPrimitiveId = 7, + SpvBuiltInInvocationId = 8, + SpvBuiltInLayer = 9, + SpvBuiltInViewportIndex = 10, + SpvBuiltInTessLevelOuter = 11, + SpvBuiltInTessLevelInner = 12, + SpvBuiltInTessCoord = 13, + SpvBuiltInPatchVertices = 14, + SpvBuiltInFragCoord = 15, + SpvBuiltInPointCoord = 16, + SpvBuiltInFrontFacing = 17, + SpvBuiltInSampleId = 18, + SpvBuiltInSamplePosition = 19, + SpvBuiltInSampleMask = 20, + SpvBuiltInFragDepth = 22, + SpvBuiltInHelperInvocation = 23, + SpvBuiltInNumWorkgroups = 24, + SpvBuiltInWorkgroupSize = 25, + SpvBuiltInWorkgroupId = 26, + SpvBuiltInLocalInvocationId = 27, + SpvBuiltInGlobalInvocationId = 28, + SpvBuiltInLocalInvocationIndex = 29, + SpvBuiltInWorkDim = 30, + SpvBuiltInGlobalSize = 31, + SpvBuiltInEnqueuedWorkgroupSize = 32, + SpvBuiltInGlobalOffset = 33, + SpvBuiltInGlobalLinearId = 34, + SpvBuiltInSubgroupSize = 36, + SpvBuiltInSubgroupMaxSize = 37, + SpvBuiltInNumSubgroups = 38, + SpvBuiltInNumEnqueuedSubgroups = 39, + SpvBuiltInSubgroupId = 40, + SpvBuiltInSubgroupLocalInvocationId = 41, + SpvBuiltInVertexIndex = 42, + SpvBuiltInInstanceIndex = 43, + SpvBuiltInSubgroupEqMask = 4416, + SpvBuiltInSubgroupEqMaskKHR = 4416, + SpvBuiltInSubgroupGeMask = 4417, + SpvBuiltInSubgroupGeMaskKHR = 4417, + SpvBuiltInSubgroupGtMask = 4418, + SpvBuiltInSubgroupGtMaskKHR = 4418, + SpvBuiltInSubgroupLeMask = 4419, + SpvBuiltInSubgroupLeMaskKHR = 4419, + SpvBuiltInSubgroupLtMask = 4420, + SpvBuiltInSubgroupLtMaskKHR = 4420, + SpvBuiltInBaseVertex = 4424, + SpvBuiltInBaseInstance = 4425, + SpvBuiltInDrawIndex = 4426, + SpvBuiltInPrimitiveShadingRateKHR = 4432, + SpvBuiltInDeviceIndex = 4438, + SpvBuiltInViewIndex = 4440, + SpvBuiltInShadingRateKHR = 4444, + SpvBuiltInBaryCoordNoPerspAMD = 4992, + SpvBuiltInBaryCoordNoPerspCentroidAMD = 4993, + SpvBuiltInBaryCoordNoPerspSampleAMD = 4994, + SpvBuiltInBaryCoordSmoothAMD = 4995, + SpvBuiltInBaryCoordSmoothCentroidAMD = 4996, + SpvBuiltInBaryCoordSmoothSampleAMD = 4997, + SpvBuiltInBaryCoordPullModelAMD = 4998, + SpvBuiltInFragStencilRefEXT = 5014, + SpvBuiltInViewportMaskNV = 5253, + SpvBuiltInSecondaryPositionNV = 5257, + SpvBuiltInSecondaryViewportMaskNV = 5258, + SpvBuiltInPositionPerViewNV = 5261, + SpvBuiltInViewportMaskPerViewNV = 5262, + SpvBuiltInFullyCoveredEXT = 5264, + SpvBuiltInTaskCountNV = 5274, + SpvBuiltInPrimitiveCountNV = 5275, + SpvBuiltInPrimitiveIndicesNV = 5276, + SpvBuiltInClipDistancePerViewNV = 5277, + SpvBuiltInCullDistancePerViewNV = 5278, + SpvBuiltInLayerPerViewNV = 5279, + SpvBuiltInMeshViewCountNV = 5280, + SpvBuiltInMeshViewIndicesNV = 5281, + SpvBuiltInBaryCoordNV = 5286, + SpvBuiltInBaryCoordNoPerspNV = 5287, + SpvBuiltInFragSizeEXT = 5292, + SpvBuiltInFragmentSizeNV = 5292, + SpvBuiltInFragInvocationCountEXT = 5293, + SpvBuiltInInvocationsPerPixelNV = 5293, + SpvBuiltInLaunchIdKHR = 5319, + SpvBuiltInLaunchIdNV = 5319, + SpvBuiltInLaunchSizeKHR = 5320, + SpvBuiltInLaunchSizeNV = 5320, + SpvBuiltInWorldRayOriginKHR = 5321, + SpvBuiltInWorldRayOriginNV = 5321, + SpvBuiltInWorldRayDirectionKHR = 5322, + SpvBuiltInWorldRayDirectionNV = 5322, + SpvBuiltInObjectRayOriginKHR = 5323, + SpvBuiltInObjectRayOriginNV = 5323, + SpvBuiltInObjectRayDirectionKHR = 5324, + SpvBuiltInObjectRayDirectionNV = 5324, + SpvBuiltInRayTminKHR = 5325, + SpvBuiltInRayTminNV = 5325, + SpvBuiltInRayTmaxKHR = 5326, + SpvBuiltInRayTmaxNV = 5326, + SpvBuiltInInstanceCustomIndexKHR = 5327, + SpvBuiltInInstanceCustomIndexNV = 5327, + SpvBuiltInObjectToWorldKHR = 5330, + SpvBuiltInObjectToWorldNV = 5330, + SpvBuiltInWorldToObjectKHR = 5331, + SpvBuiltInWorldToObjectNV = 5331, + SpvBuiltInHitTNV = 5332, + SpvBuiltInHitKindKHR = 5333, + SpvBuiltInHitKindNV = 5333, + SpvBuiltInIncomingRayFlagsKHR = 5351, + SpvBuiltInIncomingRayFlagsNV = 5351, + SpvBuiltInRayGeometryIndexKHR = 5352, + SpvBuiltInWarpsPerSMNV = 5374, + SpvBuiltInSMCountNV = 5375, + SpvBuiltInWarpIDNV = 5376, + SpvBuiltInSMIDNV = 5377, + SpvBuiltInMax = 0x7fffffff, +} SpvBuiltIn; + +typedef enum SpvSelectionControlShift_ { + SpvSelectionControlFlattenShift = 0, + SpvSelectionControlDontFlattenShift = 1, + SpvSelectionControlMax = 0x7fffffff, +} SpvSelectionControlShift; + +typedef enum SpvSelectionControlMask_ { + SpvSelectionControlMaskNone = 0, + SpvSelectionControlFlattenMask = 0x00000001, + SpvSelectionControlDontFlattenMask = 0x00000002, +} SpvSelectionControlMask; + +typedef enum SpvLoopControlShift_ { + SpvLoopControlUnrollShift = 0, + SpvLoopControlDontUnrollShift = 1, + SpvLoopControlDependencyInfiniteShift = 2, + SpvLoopControlDependencyLengthShift = 3, + SpvLoopControlMinIterationsShift = 4, + SpvLoopControlMaxIterationsShift = 5, + SpvLoopControlIterationMultipleShift = 6, + SpvLoopControlPeelCountShift = 7, + SpvLoopControlPartialCountShift = 8, + SpvLoopControlInitiationIntervalINTELShift = 16, + SpvLoopControlMaxConcurrencyINTELShift = 17, + SpvLoopControlDependencyArrayINTELShift = 18, + SpvLoopControlPipelineEnableINTELShift = 19, + SpvLoopControlLoopCoalesceINTELShift = 20, + SpvLoopControlMaxInterleavingINTELShift = 21, + SpvLoopControlSpeculatedIterationsINTELShift = 22, + SpvLoopControlNoFusionINTELShift = 23, + SpvLoopControlMax = 0x7fffffff, +} SpvLoopControlShift; + +typedef enum SpvLoopControlMask_ { + SpvLoopControlMaskNone = 0, + SpvLoopControlUnrollMask = 0x00000001, + SpvLoopControlDontUnrollMask = 0x00000002, + SpvLoopControlDependencyInfiniteMask = 0x00000004, + SpvLoopControlDependencyLengthMask = 0x00000008, + SpvLoopControlMinIterationsMask = 0x00000010, + SpvLoopControlMaxIterationsMask = 0x00000020, + SpvLoopControlIterationMultipleMask = 0x00000040, + SpvLoopControlPeelCountMask = 0x00000080, + SpvLoopControlPartialCountMask = 0x00000100, + SpvLoopControlInitiationIntervalINTELMask = 0x00010000, + SpvLoopControlMaxConcurrencyINTELMask = 0x00020000, + SpvLoopControlDependencyArrayINTELMask = 0x00040000, + SpvLoopControlPipelineEnableINTELMask = 0x00080000, + SpvLoopControlLoopCoalesceINTELMask = 0x00100000, + SpvLoopControlMaxInterleavingINTELMask = 0x00200000, + SpvLoopControlSpeculatedIterationsINTELMask = 0x00400000, + SpvLoopControlNoFusionINTELMask = 0x00800000, +} SpvLoopControlMask; + +typedef enum SpvFunctionControlShift_ { + SpvFunctionControlInlineShift = 0, + SpvFunctionControlDontInlineShift = 1, + SpvFunctionControlPureShift = 2, + SpvFunctionControlConstShift = 3, + SpvFunctionControlMax = 0x7fffffff, +} SpvFunctionControlShift; + +typedef enum SpvFunctionControlMask_ { + SpvFunctionControlMaskNone = 0, + SpvFunctionControlInlineMask = 0x00000001, + SpvFunctionControlDontInlineMask = 0x00000002, + SpvFunctionControlPureMask = 0x00000004, + SpvFunctionControlConstMask = 0x00000008, +} SpvFunctionControlMask; + +typedef enum SpvMemorySemanticsShift_ { + SpvMemorySemanticsAcquireShift = 1, + SpvMemorySemanticsReleaseShift = 2, + SpvMemorySemanticsAcquireReleaseShift = 3, + SpvMemorySemanticsSequentiallyConsistentShift = 4, + SpvMemorySemanticsUniformMemoryShift = 6, + SpvMemorySemanticsSubgroupMemoryShift = 7, + SpvMemorySemanticsWorkgroupMemoryShift = 8, + SpvMemorySemanticsCrossWorkgroupMemoryShift = 9, + SpvMemorySemanticsAtomicCounterMemoryShift = 10, + SpvMemorySemanticsImageMemoryShift = 11, + SpvMemorySemanticsOutputMemoryShift = 12, + SpvMemorySemanticsOutputMemoryKHRShift = 12, + SpvMemorySemanticsMakeAvailableShift = 13, + SpvMemorySemanticsMakeAvailableKHRShift = 13, + SpvMemorySemanticsMakeVisibleShift = 14, + SpvMemorySemanticsMakeVisibleKHRShift = 14, + SpvMemorySemanticsVolatileShift = 15, + SpvMemorySemanticsMax = 0x7fffffff, +} SpvMemorySemanticsShift; + +typedef enum SpvMemorySemanticsMask_ { + SpvMemorySemanticsMaskNone = 0, + SpvMemorySemanticsAcquireMask = 0x00000002, + SpvMemorySemanticsReleaseMask = 0x00000004, + SpvMemorySemanticsAcquireReleaseMask = 0x00000008, + SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010, + SpvMemorySemanticsUniformMemoryMask = 0x00000040, + SpvMemorySemanticsSubgroupMemoryMask = 0x00000080, + SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100, + SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400, + SpvMemorySemanticsImageMemoryMask = 0x00000800, + SpvMemorySemanticsOutputMemoryMask = 0x00001000, + SpvMemorySemanticsOutputMemoryKHRMask = 0x00001000, + SpvMemorySemanticsMakeAvailableMask = 0x00002000, + SpvMemorySemanticsMakeAvailableKHRMask = 0x00002000, + SpvMemorySemanticsMakeVisibleMask = 0x00004000, + SpvMemorySemanticsMakeVisibleKHRMask = 0x00004000, + SpvMemorySemanticsVolatileMask = 0x00008000, +} SpvMemorySemanticsMask; + +typedef enum SpvMemoryAccessShift_ { + SpvMemoryAccessVolatileShift = 0, + SpvMemoryAccessAlignedShift = 1, + SpvMemoryAccessNontemporalShift = 2, + SpvMemoryAccessMakePointerAvailableShift = 3, + SpvMemoryAccessMakePointerAvailableKHRShift = 3, + SpvMemoryAccessMakePointerVisibleShift = 4, + SpvMemoryAccessMakePointerVisibleKHRShift = 4, + SpvMemoryAccessNonPrivatePointerShift = 5, + SpvMemoryAccessNonPrivatePointerKHRShift = 5, + SpvMemoryAccessMax = 0x7fffffff, +} SpvMemoryAccessShift; + +typedef enum SpvMemoryAccessMask_ { + SpvMemoryAccessMaskNone = 0, + SpvMemoryAccessVolatileMask = 0x00000001, + SpvMemoryAccessAlignedMask = 0x00000002, + SpvMemoryAccessNontemporalMask = 0x00000004, + SpvMemoryAccessMakePointerAvailableMask = 0x00000008, + SpvMemoryAccessMakePointerAvailableKHRMask = 0x00000008, + SpvMemoryAccessMakePointerVisibleMask = 0x00000010, + SpvMemoryAccessMakePointerVisibleKHRMask = 0x00000010, + SpvMemoryAccessNonPrivatePointerMask = 0x00000020, + SpvMemoryAccessNonPrivatePointerKHRMask = 0x00000020, +} SpvMemoryAccessMask; + +typedef enum SpvScope_ { + SpvScopeCrossDevice = 0, + SpvScopeDevice = 1, + SpvScopeWorkgroup = 2, + SpvScopeSubgroup = 3, + SpvScopeInvocation = 4, + SpvScopeQueueFamily = 5, + SpvScopeQueueFamilyKHR = 5, + SpvScopeShaderCallKHR = 6, + SpvScopeMax = 0x7fffffff, +} SpvScope; + +typedef enum SpvGroupOperation_ { + SpvGroupOperationReduce = 0, + SpvGroupOperationInclusiveScan = 1, + SpvGroupOperationExclusiveScan = 2, + SpvGroupOperationClusteredReduce = 3, + SpvGroupOperationPartitionedReduceNV = 6, + SpvGroupOperationPartitionedInclusiveScanNV = 7, + SpvGroupOperationPartitionedExclusiveScanNV = 8, + SpvGroupOperationMax = 0x7fffffff, +} SpvGroupOperation; + +typedef enum SpvKernelEnqueueFlags_ { + SpvKernelEnqueueFlagsNoWait = 0, + SpvKernelEnqueueFlagsWaitKernel = 1, + SpvKernelEnqueueFlagsWaitWorkGroup = 2, + SpvKernelEnqueueFlagsMax = 0x7fffffff, +} SpvKernelEnqueueFlags; + +typedef enum SpvKernelProfilingInfoShift_ { + SpvKernelProfilingInfoCmdExecTimeShift = 0, + SpvKernelProfilingInfoMax = 0x7fffffff, +} SpvKernelProfilingInfoShift; + +typedef enum SpvKernelProfilingInfoMask_ { + SpvKernelProfilingInfoMaskNone = 0, + SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001, +} SpvKernelProfilingInfoMask; + +typedef enum SpvCapability_ { + SpvCapabilityMatrix = 0, + SpvCapabilityShader = 1, + SpvCapabilityGeometry = 2, + SpvCapabilityTessellation = 3, + SpvCapabilityAddresses = 4, + SpvCapabilityLinkage = 5, + SpvCapabilityKernel = 6, + SpvCapabilityVector16 = 7, + SpvCapabilityFloat16Buffer = 8, + SpvCapabilityFloat16 = 9, + SpvCapabilityFloat64 = 10, + SpvCapabilityInt64 = 11, + SpvCapabilityInt64Atomics = 12, + SpvCapabilityImageBasic = 13, + SpvCapabilityImageReadWrite = 14, + SpvCapabilityImageMipmap = 15, + SpvCapabilityPipes = 17, + SpvCapabilityGroups = 18, + SpvCapabilityDeviceEnqueue = 19, + SpvCapabilityLiteralSampler = 20, + SpvCapabilityAtomicStorage = 21, + SpvCapabilityInt16 = 22, + SpvCapabilityTessellationPointSize = 23, + SpvCapabilityGeometryPointSize = 24, + SpvCapabilityImageGatherExtended = 25, + SpvCapabilityStorageImageMultisample = 27, + SpvCapabilityUniformBufferArrayDynamicIndexing = 28, + SpvCapabilitySampledImageArrayDynamicIndexing = 29, + SpvCapabilityStorageBufferArrayDynamicIndexing = 30, + SpvCapabilityStorageImageArrayDynamicIndexing = 31, + SpvCapabilityClipDistance = 32, + SpvCapabilityCullDistance = 33, + SpvCapabilityImageCubeArray = 34, + SpvCapabilitySampleRateShading = 35, + SpvCapabilityImageRect = 36, + SpvCapabilitySampledRect = 37, + SpvCapabilityGenericPointer = 38, + SpvCapabilityInt8 = 39, + SpvCapabilityInputAttachment = 40, + SpvCapabilitySparseResidency = 41, + SpvCapabilityMinLod = 42, + SpvCapabilitySampled1D = 43, + SpvCapabilityImage1D = 44, + SpvCapabilitySampledCubeArray = 45, + SpvCapabilitySampledBuffer = 46, + SpvCapabilityImageBuffer = 47, + SpvCapabilityImageMSArray = 48, + SpvCapabilityStorageImageExtendedFormats = 49, + SpvCapabilityImageQuery = 50, + SpvCapabilityDerivativeControl = 51, + SpvCapabilityInterpolationFunction = 52, + SpvCapabilityTransformFeedback = 53, + SpvCapabilityGeometryStreams = 54, + SpvCapabilityStorageImageReadWithoutFormat = 55, + SpvCapabilityStorageImageWriteWithoutFormat = 56, + SpvCapabilityMultiViewport = 57, + SpvCapabilitySubgroupDispatch = 58, + SpvCapabilityNamedBarrier = 59, + SpvCapabilityPipeStorage = 60, + SpvCapabilityGroupNonUniform = 61, + SpvCapabilityGroupNonUniformVote = 62, + SpvCapabilityGroupNonUniformArithmetic = 63, + SpvCapabilityGroupNonUniformBallot = 64, + SpvCapabilityGroupNonUniformShuffle = 65, + SpvCapabilityGroupNonUniformShuffleRelative = 66, + SpvCapabilityGroupNonUniformClustered = 67, + SpvCapabilityGroupNonUniformQuad = 68, + SpvCapabilityShaderLayer = 69, + SpvCapabilityShaderViewportIndex = 70, + SpvCapabilityFragmentShadingRateKHR = 4422, + SpvCapabilitySubgroupBallotKHR = 4423, + SpvCapabilityDrawParameters = 4427, + SpvCapabilityWorkgroupMemoryExplicitLayoutKHR = 4428, + SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR = 4429, + SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR = 4430, + SpvCapabilitySubgroupVoteKHR = 4431, + SpvCapabilityStorageBuffer16BitAccess = 4433, + SpvCapabilityStorageUniformBufferBlock16 = 4433, + SpvCapabilityStorageUniform16 = 4434, + SpvCapabilityUniformAndStorageBuffer16BitAccess = 4434, + SpvCapabilityStoragePushConstant16 = 4435, + SpvCapabilityStorageInputOutput16 = 4436, + SpvCapabilityDeviceGroup = 4437, + SpvCapabilityMultiView = 4439, + SpvCapabilityVariablePointersStorageBuffer = 4441, + SpvCapabilityVariablePointers = 4442, + SpvCapabilityAtomicStorageOps = 4445, + SpvCapabilitySampleMaskPostDepthCoverage = 4447, + SpvCapabilityStorageBuffer8BitAccess = 4448, + SpvCapabilityUniformAndStorageBuffer8BitAccess = 4449, + SpvCapabilityStoragePushConstant8 = 4450, + SpvCapabilityDenormPreserve = 4464, + SpvCapabilityDenormFlushToZero = 4465, + SpvCapabilitySignedZeroInfNanPreserve = 4466, + SpvCapabilityRoundingModeRTE = 4467, + SpvCapabilityRoundingModeRTZ = 4468, + SpvCapabilityRayQueryProvisionalKHR = 4471, + SpvCapabilityRayQueryKHR = 4472, + SpvCapabilityRayTraversalPrimitiveCullingKHR = 4478, + SpvCapabilityRayTracingKHR = 4479, + SpvCapabilityFloat16ImageAMD = 5008, + SpvCapabilityImageGatherBiasLodAMD = 5009, + SpvCapabilityFragmentMaskAMD = 5010, + SpvCapabilityStencilExportEXT = 5013, + SpvCapabilityImageReadWriteLodAMD = 5015, + SpvCapabilityInt64ImageEXT = 5016, + SpvCapabilityShaderClockKHR = 5055, + SpvCapabilitySampleMaskOverrideCoverageNV = 5249, + SpvCapabilityGeometryShaderPassthroughNV = 5251, + SpvCapabilityShaderViewportIndexLayerEXT = 5254, + SpvCapabilityShaderViewportIndexLayerNV = 5254, + SpvCapabilityShaderViewportMaskNV = 5255, + SpvCapabilityShaderStereoViewNV = 5259, + SpvCapabilityPerViewAttributesNV = 5260, + SpvCapabilityFragmentFullyCoveredEXT = 5265, + SpvCapabilityMeshShadingNV = 5266, + SpvCapabilityImageFootprintNV = 5282, + SpvCapabilityFragmentBarycentricNV = 5284, + SpvCapabilityComputeDerivativeGroupQuadsNV = 5288, + SpvCapabilityFragmentDensityEXT = 5291, + SpvCapabilityShadingRateNV = 5291, + SpvCapabilityGroupNonUniformPartitionedNV = 5297, + SpvCapabilityShaderNonUniform = 5301, + SpvCapabilityShaderNonUniformEXT = 5301, + SpvCapabilityRuntimeDescriptorArray = 5302, + SpvCapabilityRuntimeDescriptorArrayEXT = 5302, + SpvCapabilityInputAttachmentArrayDynamicIndexing = 5303, + SpvCapabilityInputAttachmentArrayDynamicIndexingEXT = 5303, + SpvCapabilityUniformTexelBufferArrayDynamicIndexing = 5304, + SpvCapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304, + SpvCapabilityStorageTexelBufferArrayDynamicIndexing = 5305, + SpvCapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305, + SpvCapabilityUniformBufferArrayNonUniformIndexing = 5306, + SpvCapabilityUniformBufferArrayNonUniformIndexingEXT = 5306, + SpvCapabilitySampledImageArrayNonUniformIndexing = 5307, + SpvCapabilitySampledImageArrayNonUniformIndexingEXT = 5307, + SpvCapabilityStorageBufferArrayNonUniformIndexing = 5308, + SpvCapabilityStorageBufferArrayNonUniformIndexingEXT = 5308, + SpvCapabilityStorageImageArrayNonUniformIndexing = 5309, + SpvCapabilityStorageImageArrayNonUniformIndexingEXT = 5309, + SpvCapabilityInputAttachmentArrayNonUniformIndexing = 5310, + SpvCapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310, + SpvCapabilityUniformTexelBufferArrayNonUniformIndexing = 5311, + SpvCapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311, + SpvCapabilityStorageTexelBufferArrayNonUniformIndexing = 5312, + SpvCapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312, + SpvCapabilityRayTracingNV = 5340, + SpvCapabilityVulkanMemoryModel = 5345, + SpvCapabilityVulkanMemoryModelKHR = 5345, + SpvCapabilityVulkanMemoryModelDeviceScope = 5346, + SpvCapabilityVulkanMemoryModelDeviceScopeKHR = 5346, + SpvCapabilityPhysicalStorageBufferAddresses = 5347, + SpvCapabilityPhysicalStorageBufferAddressesEXT = 5347, + SpvCapabilityComputeDerivativeGroupLinearNV = 5350, + SpvCapabilityRayTracingProvisionalKHR = 5353, + SpvCapabilityCooperativeMatrixNV = 5357, + SpvCapabilityFragmentShaderSampleInterlockEXT = 5363, + SpvCapabilityFragmentShaderShadingRateInterlockEXT = 5372, + SpvCapabilityShaderSMBuiltinsNV = 5373, + SpvCapabilityFragmentShaderPixelInterlockEXT = 5378, + SpvCapabilityDemoteToHelperInvocationEXT = 5379, + SpvCapabilitySubgroupShuffleINTEL = 5568, + SpvCapabilitySubgroupBufferBlockIOINTEL = 5569, + SpvCapabilitySubgroupImageBlockIOINTEL = 5570, + SpvCapabilitySubgroupImageMediaBlockIOINTEL = 5579, + SpvCapabilityRoundToInfinityINTEL = 5582, + SpvCapabilityFloatingPointModeINTEL = 5583, + SpvCapabilityIntegerFunctions2INTEL = 5584, + SpvCapabilityFunctionPointersINTEL = 5603, + SpvCapabilityIndirectReferencesINTEL = 5604, + SpvCapabilityAsmINTEL = 5606, + SpvCapabilityVectorComputeINTEL = 5617, + SpvCapabilityVectorAnyINTEL = 5619, + SpvCapabilitySubgroupAvcMotionEstimationINTEL = 5696, + SpvCapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697, + SpvCapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698, + SpvCapabilityVariableLengthArrayINTEL = 5817, + SpvCapabilityFunctionFloatControlINTEL = 5821, + SpvCapabilityFPGAMemoryAttributesINTEL = 5824, + SpvCapabilityFPFastMathModeINTEL = 5837, + SpvCapabilityArbitraryPrecisionIntegersINTEL = 5844, + SpvCapabilityUnstructuredLoopControlsINTEL = 5886, + SpvCapabilityFPGALoopControlsINTEL = 5888, + SpvCapabilityKernelAttributesINTEL = 5892, + SpvCapabilityFPGAKernelAttributesINTEL = 5897, + SpvCapabilityFPGAMemoryAccessesINTEL = 5898, + SpvCapabilityFPGAClusterAttributesINTEL = 5904, + SpvCapabilityLoopFuseINTEL = 5906, + SpvCapabilityFPGABufferLocationINTEL = 5920, + SpvCapabilityUSMStorageClassesINTEL = 5935, + SpvCapabilityIOPipesINTEL = 5943, + SpvCapabilityBlockingPipesINTEL = 5945, + SpvCapabilityFPGARegINTEL = 5948, + SpvCapabilityAtomicFloat32AddEXT = 6033, + SpvCapabilityAtomicFloat64AddEXT = 6034, + SpvCapabilityLongConstantCompositeINTEL = 6089, + SpvCapabilityMax = 0x7fffffff, +} SpvCapability; + +typedef enum SpvRayFlagsShift_ { + SpvRayFlagsOpaqueKHRShift = 0, + SpvRayFlagsNoOpaqueKHRShift = 1, + SpvRayFlagsTerminateOnFirstHitKHRShift = 2, + SpvRayFlagsSkipClosestHitShaderKHRShift = 3, + SpvRayFlagsCullBackFacingTrianglesKHRShift = 4, + SpvRayFlagsCullFrontFacingTrianglesKHRShift = 5, + SpvRayFlagsCullOpaqueKHRShift = 6, + SpvRayFlagsCullNoOpaqueKHRShift = 7, + SpvRayFlagsSkipTrianglesKHRShift = 8, + SpvRayFlagsSkipAABBsKHRShift = 9, + SpvRayFlagsMax = 0x7fffffff, +} SpvRayFlagsShift; + +typedef enum SpvRayFlagsMask_ { + SpvRayFlagsMaskNone = 0, + SpvRayFlagsOpaqueKHRMask = 0x00000001, + SpvRayFlagsNoOpaqueKHRMask = 0x00000002, + SpvRayFlagsTerminateOnFirstHitKHRMask = 0x00000004, + SpvRayFlagsSkipClosestHitShaderKHRMask = 0x00000008, + SpvRayFlagsCullBackFacingTrianglesKHRMask = 0x00000010, + SpvRayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020, + SpvRayFlagsCullOpaqueKHRMask = 0x00000040, + SpvRayFlagsCullNoOpaqueKHRMask = 0x00000080, + SpvRayFlagsSkipTrianglesKHRMask = 0x00000100, + SpvRayFlagsSkipAABBsKHRMask = 0x00000200, +} SpvRayFlagsMask; + +typedef enum SpvRayQueryIntersection_ { + SpvRayQueryIntersectionRayQueryCandidateIntersectionKHR = 0, + SpvRayQueryIntersectionRayQueryCommittedIntersectionKHR = 1, + SpvRayQueryIntersectionMax = 0x7fffffff, +} SpvRayQueryIntersection; + +typedef enum SpvRayQueryCommittedIntersectionType_ { + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0, + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR = 1, + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR = 2, + SpvRayQueryCommittedIntersectionTypeMax = 0x7fffffff, +} SpvRayQueryCommittedIntersectionType; + +typedef enum SpvRayQueryCandidateIntersectionType_ { + SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR = 0, + SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1, + SpvRayQueryCandidateIntersectionTypeMax = 0x7fffffff, +} SpvRayQueryCandidateIntersectionType; + +typedef enum SpvFragmentShadingRateShift_ { + SpvFragmentShadingRateVertical2PixelsShift = 0, + SpvFragmentShadingRateVertical4PixelsShift = 1, + SpvFragmentShadingRateHorizontal2PixelsShift = 2, + SpvFragmentShadingRateHorizontal4PixelsShift = 3, + SpvFragmentShadingRateMax = 0x7fffffff, +} SpvFragmentShadingRateShift; + +typedef enum SpvFragmentShadingRateMask_ { + SpvFragmentShadingRateMaskNone = 0, + SpvFragmentShadingRateVertical2PixelsMask = 0x00000001, + SpvFragmentShadingRateVertical4PixelsMask = 0x00000002, + SpvFragmentShadingRateHorizontal2PixelsMask = 0x00000004, + SpvFragmentShadingRateHorizontal4PixelsMask = 0x00000008, +} SpvFragmentShadingRateMask; + +typedef enum SpvFPDenormMode_ { + SpvFPDenormModePreserve = 0, + SpvFPDenormModeFlushToZero = 1, + SpvFPDenormModeMax = 0x7fffffff, +} SpvFPDenormMode; + +typedef enum SpvFPOperationMode_ { + SpvFPOperationModeIEEE = 0, + SpvFPOperationModeALT = 1, + SpvFPOperationModeMax = 0x7fffffff, +} SpvFPOperationMode; + +typedef enum SpvOp_ { + SpvOpNop = 0, + SpvOpUndef = 1, + SpvOpSourceContinued = 2, + SpvOpSource = 3, + SpvOpSourceExtension = 4, + SpvOpName = 5, + SpvOpMemberName = 6, + SpvOpString = 7, + SpvOpLine = 8, + SpvOpExtension = 10, + SpvOpExtInstImport = 11, + SpvOpExtInst = 12, + SpvOpMemoryModel = 14, + SpvOpEntryPoint = 15, + SpvOpExecutionMode = 16, + SpvOpCapability = 17, + SpvOpTypeVoid = 19, + SpvOpTypeBool = 20, + SpvOpTypeInt = 21, + SpvOpTypeFloat = 22, + SpvOpTypeVector = 23, + SpvOpTypeMatrix = 24, + SpvOpTypeImage = 25, + SpvOpTypeSampler = 26, + SpvOpTypeSampledImage = 27, + SpvOpTypeArray = 28, + SpvOpTypeRuntimeArray = 29, + SpvOpTypeStruct = 30, + SpvOpTypeOpaque = 31, + SpvOpTypePointer = 32, + SpvOpTypeFunction = 33, + SpvOpTypeEvent = 34, + SpvOpTypeDeviceEvent = 35, + SpvOpTypeReserveId = 36, + SpvOpTypeQueue = 37, + SpvOpTypePipe = 38, + SpvOpTypeForwardPointer = 39, + SpvOpConstantTrue = 41, + SpvOpConstantFalse = 42, + SpvOpConstant = 43, + SpvOpConstantComposite = 44, + SpvOpConstantSampler = 45, + SpvOpConstantNull = 46, + SpvOpSpecConstantTrue = 48, + SpvOpSpecConstantFalse = 49, + SpvOpSpecConstant = 50, + SpvOpSpecConstantComposite = 51, + SpvOpSpecConstantOp = 52, + SpvOpFunction = 54, + SpvOpFunctionParameter = 55, + SpvOpFunctionEnd = 56, + SpvOpFunctionCall = 57, + SpvOpVariable = 59, + SpvOpImageTexelPointer = 60, + SpvOpLoad = 61, + SpvOpStore = 62, + SpvOpCopyMemory = 63, + SpvOpCopyMemorySized = 64, + SpvOpAccessChain = 65, + SpvOpInBoundsAccessChain = 66, + SpvOpPtrAccessChain = 67, + SpvOpArrayLength = 68, + SpvOpGenericPtrMemSemantics = 69, + SpvOpInBoundsPtrAccessChain = 70, + SpvOpDecorate = 71, + SpvOpMemberDecorate = 72, + SpvOpDecorationGroup = 73, + SpvOpGroupDecorate = 74, + SpvOpGroupMemberDecorate = 75, + SpvOpVectorExtractDynamic = 77, + SpvOpVectorInsertDynamic = 78, + SpvOpVectorShuffle = 79, + SpvOpCompositeConstruct = 80, + SpvOpCompositeExtract = 81, + SpvOpCompositeInsert = 82, + SpvOpCopyObject = 83, + SpvOpTranspose = 84, + SpvOpSampledImage = 86, + SpvOpImageSampleImplicitLod = 87, + SpvOpImageSampleExplicitLod = 88, + SpvOpImageSampleDrefImplicitLod = 89, + SpvOpImageSampleDrefExplicitLod = 90, + SpvOpImageSampleProjImplicitLod = 91, + SpvOpImageSampleProjExplicitLod = 92, + SpvOpImageSampleProjDrefImplicitLod = 93, + SpvOpImageSampleProjDrefExplicitLod = 94, + SpvOpImageFetch = 95, + SpvOpImageGather = 96, + SpvOpImageDrefGather = 97, + SpvOpImageRead = 98, + SpvOpImageWrite = 99, + SpvOpImage = 100, + SpvOpImageQueryFormat = 101, + SpvOpImageQueryOrder = 102, + SpvOpImageQuerySizeLod = 103, + SpvOpImageQuerySize = 104, + SpvOpImageQueryLod = 105, + SpvOpImageQueryLevels = 106, + SpvOpImageQuerySamples = 107, + SpvOpConvertFToU = 109, + SpvOpConvertFToS = 110, + SpvOpConvertSToF = 111, + SpvOpConvertUToF = 112, + SpvOpUConvert = 113, + SpvOpSConvert = 114, + SpvOpFConvert = 115, + SpvOpQuantizeToF16 = 116, + SpvOpConvertPtrToU = 117, + SpvOpSatConvertSToU = 118, + SpvOpSatConvertUToS = 119, + SpvOpConvertUToPtr = 120, + SpvOpPtrCastToGeneric = 121, + SpvOpGenericCastToPtr = 122, + SpvOpGenericCastToPtrExplicit = 123, + SpvOpBitcast = 124, + SpvOpSNegate = 126, + SpvOpFNegate = 127, + SpvOpIAdd = 128, + SpvOpFAdd = 129, + SpvOpISub = 130, + SpvOpFSub = 131, + SpvOpIMul = 132, + SpvOpFMul = 133, + SpvOpUDiv = 134, + SpvOpSDiv = 135, + SpvOpFDiv = 136, + SpvOpUMod = 137, + SpvOpSRem = 138, + SpvOpSMod = 139, + SpvOpFRem = 140, + SpvOpFMod = 141, + SpvOpVectorTimesScalar = 142, + SpvOpMatrixTimesScalar = 143, + SpvOpVectorTimesMatrix = 144, + SpvOpMatrixTimesVector = 145, + SpvOpMatrixTimesMatrix = 146, + SpvOpOuterProduct = 147, + SpvOpDot = 148, + SpvOpIAddCarry = 149, + SpvOpISubBorrow = 150, + SpvOpUMulExtended = 151, + SpvOpSMulExtended = 152, + SpvOpAny = 154, + SpvOpAll = 155, + SpvOpIsNan = 156, + SpvOpIsInf = 157, + SpvOpIsFinite = 158, + SpvOpIsNormal = 159, + SpvOpSignBitSet = 160, + SpvOpLessOrGreater = 161, + SpvOpOrdered = 162, + SpvOpUnordered = 163, + SpvOpLogicalEqual = 164, + SpvOpLogicalNotEqual = 165, + SpvOpLogicalOr = 166, + SpvOpLogicalAnd = 167, + SpvOpLogicalNot = 168, + SpvOpSelect = 169, + SpvOpIEqual = 170, + SpvOpINotEqual = 171, + SpvOpUGreaterThan = 172, + SpvOpSGreaterThan = 173, + SpvOpUGreaterThanEqual = 174, + SpvOpSGreaterThanEqual = 175, + SpvOpULessThan = 176, + SpvOpSLessThan = 177, + SpvOpULessThanEqual = 178, + SpvOpSLessThanEqual = 179, + SpvOpFOrdEqual = 180, + SpvOpFUnordEqual = 181, + SpvOpFOrdNotEqual = 182, + SpvOpFUnordNotEqual = 183, + SpvOpFOrdLessThan = 184, + SpvOpFUnordLessThan = 185, + SpvOpFOrdGreaterThan = 186, + SpvOpFUnordGreaterThan = 187, + SpvOpFOrdLessThanEqual = 188, + SpvOpFUnordLessThanEqual = 189, + SpvOpFOrdGreaterThanEqual = 190, + SpvOpFUnordGreaterThanEqual = 191, + SpvOpShiftRightLogical = 194, + SpvOpShiftRightArithmetic = 195, + SpvOpShiftLeftLogical = 196, + SpvOpBitwiseOr = 197, + SpvOpBitwiseXor = 198, + SpvOpBitwiseAnd = 199, + SpvOpNot = 200, + SpvOpBitFieldInsert = 201, + SpvOpBitFieldSExtract = 202, + SpvOpBitFieldUExtract = 203, + SpvOpBitReverse = 204, + SpvOpBitCount = 205, + SpvOpDPdx = 207, + SpvOpDPdy = 208, + SpvOpFwidth = 209, + SpvOpDPdxFine = 210, + SpvOpDPdyFine = 211, + SpvOpFwidthFine = 212, + SpvOpDPdxCoarse = 213, + SpvOpDPdyCoarse = 214, + SpvOpFwidthCoarse = 215, + SpvOpEmitVertex = 218, + SpvOpEndPrimitive = 219, + SpvOpEmitStreamVertex = 220, + SpvOpEndStreamPrimitive = 221, + SpvOpControlBarrier = 224, + SpvOpMemoryBarrier = 225, + SpvOpAtomicLoad = 227, + SpvOpAtomicStore = 228, + SpvOpAtomicExchange = 229, + SpvOpAtomicCompareExchange = 230, + SpvOpAtomicCompareExchangeWeak = 231, + SpvOpAtomicIIncrement = 232, + SpvOpAtomicIDecrement = 233, + SpvOpAtomicIAdd = 234, + SpvOpAtomicISub = 235, + SpvOpAtomicSMin = 236, + SpvOpAtomicUMin = 237, + SpvOpAtomicSMax = 238, + SpvOpAtomicUMax = 239, + SpvOpAtomicAnd = 240, + SpvOpAtomicOr = 241, + SpvOpAtomicXor = 242, + SpvOpPhi = 245, + SpvOpLoopMerge = 246, + SpvOpSelectionMerge = 247, + SpvOpLabel = 248, + SpvOpBranch = 249, + SpvOpBranchConditional = 250, + SpvOpSwitch = 251, + SpvOpKill = 252, + SpvOpReturn = 253, + SpvOpReturnValue = 254, + SpvOpUnreachable = 255, + SpvOpLifetimeStart = 256, + SpvOpLifetimeStop = 257, + SpvOpGroupAsyncCopy = 259, + SpvOpGroupWaitEvents = 260, + SpvOpGroupAll = 261, + SpvOpGroupAny = 262, + SpvOpGroupBroadcast = 263, + SpvOpGroupIAdd = 264, + SpvOpGroupFAdd = 265, + SpvOpGroupFMin = 266, + SpvOpGroupUMin = 267, + SpvOpGroupSMin = 268, + SpvOpGroupFMax = 269, + SpvOpGroupUMax = 270, + SpvOpGroupSMax = 271, + SpvOpReadPipe = 274, + SpvOpWritePipe = 275, + SpvOpReservedReadPipe = 276, + SpvOpReservedWritePipe = 277, + SpvOpReserveReadPipePackets = 278, + SpvOpReserveWritePipePackets = 279, + SpvOpCommitReadPipe = 280, + SpvOpCommitWritePipe = 281, + SpvOpIsValidReserveId = 282, + SpvOpGetNumPipePackets = 283, + SpvOpGetMaxPipePackets = 284, + SpvOpGroupReserveReadPipePackets = 285, + SpvOpGroupReserveWritePipePackets = 286, + SpvOpGroupCommitReadPipe = 287, + SpvOpGroupCommitWritePipe = 288, + SpvOpEnqueueMarker = 291, + SpvOpEnqueueKernel = 292, + SpvOpGetKernelNDrangeSubGroupCount = 293, + SpvOpGetKernelNDrangeMaxSubGroupSize = 294, + SpvOpGetKernelWorkGroupSize = 295, + SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296, + SpvOpRetainEvent = 297, + SpvOpReleaseEvent = 298, + SpvOpCreateUserEvent = 299, + SpvOpIsValidEvent = 300, + SpvOpSetUserEventStatus = 301, + SpvOpCaptureEventProfilingInfo = 302, + SpvOpGetDefaultQueue = 303, + SpvOpBuildNDRange = 304, + SpvOpImageSparseSampleImplicitLod = 305, + SpvOpImageSparseSampleExplicitLod = 306, + SpvOpImageSparseSampleDrefImplicitLod = 307, + SpvOpImageSparseSampleDrefExplicitLod = 308, + SpvOpImageSparseSampleProjImplicitLod = 309, + SpvOpImageSparseSampleProjExplicitLod = 310, + SpvOpImageSparseSampleProjDrefImplicitLod = 311, + SpvOpImageSparseSampleProjDrefExplicitLod = 312, + SpvOpImageSparseFetch = 313, + SpvOpImageSparseGather = 314, + SpvOpImageSparseDrefGather = 315, + SpvOpImageSparseTexelsResident = 316, + SpvOpNoLine = 317, + SpvOpAtomicFlagTestAndSet = 318, + SpvOpAtomicFlagClear = 319, + SpvOpImageSparseRead = 320, + SpvOpSizeOf = 321, + SpvOpTypePipeStorage = 322, + SpvOpConstantPipeStorage = 323, + SpvOpCreatePipeFromPipeStorage = 324, + SpvOpGetKernelLocalSizeForSubgroupCount = 325, + SpvOpGetKernelMaxNumSubgroups = 326, + SpvOpTypeNamedBarrier = 327, + SpvOpNamedBarrierInitialize = 328, + SpvOpMemoryNamedBarrier = 329, + SpvOpModuleProcessed = 330, + SpvOpExecutionModeId = 331, + SpvOpDecorateId = 332, + SpvOpGroupNonUniformElect = 333, + SpvOpGroupNonUniformAll = 334, + SpvOpGroupNonUniformAny = 335, + SpvOpGroupNonUniformAllEqual = 336, + SpvOpGroupNonUniformBroadcast = 337, + SpvOpGroupNonUniformBroadcastFirst = 338, + SpvOpGroupNonUniformBallot = 339, + SpvOpGroupNonUniformInverseBallot = 340, + SpvOpGroupNonUniformBallotBitExtract = 341, + SpvOpGroupNonUniformBallotBitCount = 342, + SpvOpGroupNonUniformBallotFindLSB = 343, + SpvOpGroupNonUniformBallotFindMSB = 344, + SpvOpGroupNonUniformShuffle = 345, + SpvOpGroupNonUniformShuffleXor = 346, + SpvOpGroupNonUniformShuffleUp = 347, + SpvOpGroupNonUniformShuffleDown = 348, + SpvOpGroupNonUniformIAdd = 349, + SpvOpGroupNonUniformFAdd = 350, + SpvOpGroupNonUniformIMul = 351, + SpvOpGroupNonUniformFMul = 352, + SpvOpGroupNonUniformSMin = 353, + SpvOpGroupNonUniformUMin = 354, + SpvOpGroupNonUniformFMin = 355, + SpvOpGroupNonUniformSMax = 356, + SpvOpGroupNonUniformUMax = 357, + SpvOpGroupNonUniformFMax = 358, + SpvOpGroupNonUniformBitwiseAnd = 359, + SpvOpGroupNonUniformBitwiseOr = 360, + SpvOpGroupNonUniformBitwiseXor = 361, + SpvOpGroupNonUniformLogicalAnd = 362, + SpvOpGroupNonUniformLogicalOr = 363, + SpvOpGroupNonUniformLogicalXor = 364, + SpvOpGroupNonUniformQuadBroadcast = 365, + SpvOpGroupNonUniformQuadSwap = 366, + SpvOpCopyLogical = 400, + SpvOpPtrEqual = 401, + SpvOpPtrNotEqual = 402, + SpvOpPtrDiff = 403, + SpvOpTerminateInvocation = 4416, + SpvOpSubgroupBallotKHR = 4421, + SpvOpSubgroupFirstInvocationKHR = 4422, + SpvOpSubgroupAllKHR = 4428, + SpvOpSubgroupAnyKHR = 4429, + SpvOpSubgroupAllEqualKHR = 4430, + SpvOpSubgroupReadInvocationKHR = 4432, + SpvOpTraceRayKHR = 4445, + SpvOpExecuteCallableKHR = 4446, + SpvOpConvertUToAccelerationStructureKHR = 4447, + SpvOpIgnoreIntersectionKHR = 4448, + SpvOpTerminateRayKHR = 4449, + SpvOpTypeRayQueryKHR = 4472, + SpvOpRayQueryInitializeKHR = 4473, + SpvOpRayQueryTerminateKHR = 4474, + SpvOpRayQueryGenerateIntersectionKHR = 4475, + SpvOpRayQueryConfirmIntersectionKHR = 4476, + SpvOpRayQueryProceedKHR = 4477, + SpvOpRayQueryGetIntersectionTypeKHR = 4479, + SpvOpGroupIAddNonUniformAMD = 5000, + SpvOpGroupFAddNonUniformAMD = 5001, + SpvOpGroupFMinNonUniformAMD = 5002, + SpvOpGroupUMinNonUniformAMD = 5003, + SpvOpGroupSMinNonUniformAMD = 5004, + SpvOpGroupFMaxNonUniformAMD = 5005, + SpvOpGroupUMaxNonUniformAMD = 5006, + SpvOpGroupSMaxNonUniformAMD = 5007, + SpvOpFragmentMaskFetchAMD = 5011, + SpvOpFragmentFetchAMD = 5012, + SpvOpReadClockKHR = 5056, + SpvOpImageSampleFootprintNV = 5283, + SpvOpGroupNonUniformPartitionNV = 5296, + SpvOpWritePackedPrimitiveIndices4x8NV = 5299, + SpvOpReportIntersectionKHR = 5334, + SpvOpReportIntersectionNV = 5334, + SpvOpIgnoreIntersectionNV = 5335, + SpvOpTerminateRayNV = 5336, + SpvOpTraceNV = 5337, + SpvOpTypeAccelerationStructureKHR = 5341, + SpvOpTypeAccelerationStructureNV = 5341, + SpvOpExecuteCallableNV = 5344, + SpvOpTypeCooperativeMatrixNV = 5358, + SpvOpCooperativeMatrixLoadNV = 5359, + SpvOpCooperativeMatrixStoreNV = 5360, + SpvOpCooperativeMatrixMulAddNV = 5361, + SpvOpCooperativeMatrixLengthNV = 5362, + SpvOpBeginInvocationInterlockEXT = 5364, + SpvOpEndInvocationInterlockEXT = 5365, + SpvOpDemoteToHelperInvocationEXT = 5380, + SpvOpIsHelperInvocationEXT = 5381, + SpvOpSubgroupShuffleINTEL = 5571, + SpvOpSubgroupShuffleDownINTEL = 5572, + SpvOpSubgroupShuffleUpINTEL = 5573, + SpvOpSubgroupShuffleXorINTEL = 5574, + SpvOpSubgroupBlockReadINTEL = 5575, + SpvOpSubgroupBlockWriteINTEL = 5576, + SpvOpSubgroupImageBlockReadINTEL = 5577, + SpvOpSubgroupImageBlockWriteINTEL = 5578, + SpvOpSubgroupImageMediaBlockReadINTEL = 5580, + SpvOpSubgroupImageMediaBlockWriteINTEL = 5581, + SpvOpUCountLeadingZerosINTEL = 5585, + SpvOpUCountTrailingZerosINTEL = 5586, + SpvOpAbsISubINTEL = 5587, + SpvOpAbsUSubINTEL = 5588, + SpvOpIAddSatINTEL = 5589, + SpvOpUAddSatINTEL = 5590, + SpvOpIAverageINTEL = 5591, + SpvOpUAverageINTEL = 5592, + SpvOpIAverageRoundedINTEL = 5593, + SpvOpUAverageRoundedINTEL = 5594, + SpvOpISubSatINTEL = 5595, + SpvOpUSubSatINTEL = 5596, + SpvOpIMul32x16INTEL = 5597, + SpvOpUMul32x16INTEL = 5598, + SpvOpConstFunctionPointerINTEL = 5600, + SpvOpFunctionPointerCallINTEL = 5601, + SpvOpAsmTargetINTEL = 5609, + SpvOpAsmINTEL = 5610, + SpvOpAsmCallINTEL = 5611, + SpvOpDecorateString = 5632, + SpvOpDecorateStringGOOGLE = 5632, + SpvOpMemberDecorateString = 5633, + SpvOpMemberDecorateStringGOOGLE = 5633, + SpvOpVmeImageINTEL = 5699, + SpvOpTypeVmeImageINTEL = 5700, + SpvOpTypeAvcImePayloadINTEL = 5701, + SpvOpTypeAvcRefPayloadINTEL = 5702, + SpvOpTypeAvcSicPayloadINTEL = 5703, + SpvOpTypeAvcMcePayloadINTEL = 5704, + SpvOpTypeAvcMceResultINTEL = 5705, + SpvOpTypeAvcImeResultINTEL = 5706, + SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + SpvOpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + SpvOpTypeAvcImeDualReferenceStreaminINTEL = 5710, + SpvOpTypeAvcRefResultINTEL = 5711, + SpvOpTypeAvcSicResultINTEL = 5712, + SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + SpvOpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + SpvOpSubgroupAvcMceConvertToImeResultINTEL = 5733, + SpvOpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + SpvOpSubgroupAvcMceConvertToRefResultINTEL = 5735, + SpvOpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + SpvOpSubgroupAvcMceConvertToSicResultINTEL = 5737, + SpvOpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + SpvOpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + SpvOpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + SpvOpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + SpvOpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + SpvOpSubgroupAvcImeInitializeINTEL = 5747, + SpvOpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + SpvOpSubgroupAvcImeSetDualReferenceINTEL = 5749, + SpvOpSubgroupAvcImeRefWindowSizeINTEL = 5750, + SpvOpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + SpvOpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + SpvOpSubgroupAvcImeSetWeightedSadINTEL = 5756, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + SpvOpSubgroupAvcImeConvertToMceResultINTEL = 5765, + SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + SpvOpSubgroupAvcImeGetBorderReachedINTEL = 5776, + SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + SpvOpSubgroupAvcFmeInitializeINTEL = 5781, + SpvOpSubgroupAvcBmeInitializeINTEL = 5782, + SpvOpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + SpvOpSubgroupAvcRefConvertToMceResultINTEL = 5790, + SpvOpSubgroupAvcSicInitializeINTEL = 5791, + SpvOpSubgroupAvcSicConfigureSkcINTEL = 5792, + SpvOpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + SpvOpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + SpvOpSubgroupAvcSicEvaluateIpeINTEL = 5803, + SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + SpvOpSubgroupAvcSicConvertToMceResultINTEL = 5808, + SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + SpvOpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + SpvOpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + SpvOpVariableLengthArrayINTEL = 5818, + SpvOpSaveMemoryINTEL = 5819, + SpvOpRestoreMemoryINTEL = 5820, + SpvOpLoopControlINTEL = 5887, + SpvOpPtrCastToCrossWorkgroupINTEL = 5934, + SpvOpCrossWorkgroupCastToPtrINTEL = 5938, + SpvOpReadPipeBlockingINTEL = 5946, + SpvOpWritePipeBlockingINTEL = 5947, + SpvOpFPGARegINTEL = 5949, + SpvOpRayQueryGetRayTMinKHR = 6016, + SpvOpRayQueryGetRayFlagsKHR = 6017, + SpvOpRayQueryGetIntersectionTKHR = 6018, + SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + SpvOpRayQueryGetIntersectionInstanceIdKHR = 6020, + SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + SpvOpRayQueryGetIntersectionGeometryIndexKHR = 6022, + SpvOpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + SpvOpRayQueryGetIntersectionBarycentricsKHR = 6024, + SpvOpRayQueryGetIntersectionFrontFaceKHR = 6025, + SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + SpvOpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + SpvOpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + SpvOpRayQueryGetWorldRayDirectionKHR = 6029, + SpvOpRayQueryGetWorldRayOriginKHR = 6030, + SpvOpRayQueryGetIntersectionObjectToWorldKHR = 6031, + SpvOpRayQueryGetIntersectionWorldToObjectKHR = 6032, + SpvOpAtomicFAddEXT = 6035, + SpvOpTypeBufferSurfaceINTEL = 6086, + SpvOpTypeStructContinuedINTEL = 6090, + SpvOpConstantCompositeContinuedINTEL = 6091, + SpvOpSpecConstantCompositeContinuedINTEL = 6092, + SpvOpMax = 0x7fffffff, +} SpvOp; + +#ifdef SPV_ENABLE_UTILITY_CODE +inline void SpvHasResultAndType(SpvOp opcode, bool *hasResult, bool *hasResultType) { + *hasResult = *hasResultType = false; + switch (opcode) { + default: /* unknown opcode */ break; + case SpvOpNop: *hasResult = false; *hasResultType = false; break; + case SpvOpUndef: *hasResult = true; *hasResultType = true; break; + case SpvOpSourceContinued: *hasResult = false; *hasResultType = false; break; + case SpvOpSource: *hasResult = false; *hasResultType = false; break; + case SpvOpSourceExtension: *hasResult = false; *hasResultType = false; break; + case SpvOpName: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberName: *hasResult = false; *hasResultType = false; break; + case SpvOpString: *hasResult = true; *hasResultType = false; break; + case SpvOpLine: *hasResult = false; *hasResultType = false; break; + case SpvOpExtension: *hasResult = false; *hasResultType = false; break; + case SpvOpExtInstImport: *hasResult = true; *hasResultType = false; break; + case SpvOpExtInst: *hasResult = true; *hasResultType = true; break; + case SpvOpMemoryModel: *hasResult = false; *hasResultType = false; break; + case SpvOpEntryPoint: *hasResult = false; *hasResultType = false; break; + case SpvOpExecutionMode: *hasResult = false; *hasResultType = false; break; + case SpvOpCapability: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeVoid: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeBool: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeInt: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeFloat: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeVector: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeMatrix: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeImage: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeSampler: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeSampledImage: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeArray: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeRuntimeArray: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeStruct: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeOpaque: *hasResult = true; *hasResultType = false; break; + case SpvOpTypePointer: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeFunction: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeEvent: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeDeviceEvent: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeReserveId: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeQueue: *hasResult = true; *hasResultType = false; break; + case SpvOpTypePipe: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeForwardPointer: *hasResult = false; *hasResultType = false; break; + case SpvOpConstantTrue: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantFalse: *hasResult = true; *hasResultType = true; break; + case SpvOpConstant: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantComposite: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantSampler: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantNull: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantTrue: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantFalse: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstant: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantComposite: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantOp: *hasResult = true; *hasResultType = true; break; + case SpvOpFunction: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionParameter: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionEnd: *hasResult = false; *hasResultType = false; break; + case SpvOpFunctionCall: *hasResult = true; *hasResultType = true; break; + case SpvOpVariable: *hasResult = true; *hasResultType = true; break; + case SpvOpImageTexelPointer: *hasResult = true; *hasResultType = true; break; + case SpvOpLoad: *hasResult = true; *hasResultType = true; break; + case SpvOpStore: *hasResult = false; *hasResultType = false; break; + case SpvOpCopyMemory: *hasResult = false; *hasResultType = false; break; + case SpvOpCopyMemorySized: *hasResult = false; *hasResultType = false; break; + case SpvOpAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpInBoundsAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpArrayLength: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericPtrMemSemantics: *hasResult = true; *hasResultType = true; break; + case SpvOpInBoundsPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpDecorationGroup: *hasResult = true; *hasResultType = false; break; + case SpvOpGroupDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupMemberDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpVectorExtractDynamic: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorInsertDynamic: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorShuffle: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeConstruct: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeInsert: *hasResult = true; *hasResultType = true; break; + case SpvOpCopyObject: *hasResult = true; *hasResultType = true; break; + case SpvOpTranspose: *hasResult = true; *hasResultType = true; break; + case SpvOpSampledImage: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageFetch: *hasResult = true; *hasResultType = true; break; + case SpvOpImageGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageDrefGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageRead: *hasResult = true; *hasResultType = true; break; + case SpvOpImageWrite: *hasResult = false; *hasResultType = false; break; + case SpvOpImage: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryFormat: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryOrder: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySizeLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySize: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryLevels: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySamples: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertFToU: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertFToS: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertSToF: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToF: *hasResult = true; *hasResultType = true; break; + case SpvOpUConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpSConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpFConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpQuantizeToF16: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertPtrToU: *hasResult = true; *hasResultType = true; break; + case SpvOpSatConvertSToU: *hasResult = true; *hasResultType = true; break; + case SpvOpSatConvertUToS: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToPtr: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrCastToGeneric: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericCastToPtr: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericCastToPtrExplicit: *hasResult = true; *hasResultType = true; break; + case SpvOpBitcast: *hasResult = true; *hasResultType = true; break; + case SpvOpSNegate: *hasResult = true; *hasResultType = true; break; + case SpvOpFNegate: *hasResult = true; *hasResultType = true; break; + case SpvOpIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpISub: *hasResult = true; *hasResultType = true; break; + case SpvOpFSub: *hasResult = true; *hasResultType = true; break; + case SpvOpIMul: *hasResult = true; *hasResultType = true; break; + case SpvOpFMul: *hasResult = true; *hasResultType = true; break; + case SpvOpUDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpSDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpFDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpUMod: *hasResult = true; *hasResultType = true; break; + case SpvOpSRem: *hasResult = true; *hasResultType = true; break; + case SpvOpSMod: *hasResult = true; *hasResultType = true; break; + case SpvOpFRem: *hasResult = true; *hasResultType = true; break; + case SpvOpFMod: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorTimesScalar: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesScalar: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorTimesMatrix: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesVector: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesMatrix: *hasResult = true; *hasResultType = true; break; + case SpvOpOuterProduct: *hasResult = true; *hasResultType = true; break; + case SpvOpDot: *hasResult = true; *hasResultType = true; break; + case SpvOpIAddCarry: *hasResult = true; *hasResultType = true; break; + case SpvOpISubBorrow: *hasResult = true; *hasResultType = true; break; + case SpvOpUMulExtended: *hasResult = true; *hasResultType = true; break; + case SpvOpSMulExtended: *hasResult = true; *hasResultType = true; break; + case SpvOpAny: *hasResult = true; *hasResultType = true; break; + case SpvOpAll: *hasResult = true; *hasResultType = true; break; + case SpvOpIsNan: *hasResult = true; *hasResultType = true; break; + case SpvOpIsInf: *hasResult = true; *hasResultType = true; break; + case SpvOpIsFinite: *hasResult = true; *hasResultType = true; break; + case SpvOpIsNormal: *hasResult = true; *hasResultType = true; break; + case SpvOpSignBitSet: *hasResult = true; *hasResultType = true; break; + case SpvOpLessOrGreater: *hasResult = true; *hasResultType = true; break; + case SpvOpOrdered: *hasResult = true; *hasResultType = true; break; + case SpvOpUnordered: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalOr: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalNot: *hasResult = true; *hasResultType = true; break; + case SpvOpSelect: *hasResult = true; *hasResultType = true; break; + case SpvOpIEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpINotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpUGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpSGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpUGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpSGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpULessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpSLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpULessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpSLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftRightLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftRightArithmetic: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftLeftLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseOr: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseXor: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpNot: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldInsert: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldSExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldUExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpBitReverse: *hasResult = true; *hasResultType = true; break; + case SpvOpBitCount: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdx: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdy: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidth: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdxFine: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdyFine: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidthFine: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdxCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdyCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidthCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpEmitVertex: *hasResult = false; *hasResultType = false; break; + case SpvOpEndPrimitive: *hasResult = false; *hasResultType = false; break; + case SpvOpEmitStreamVertex: *hasResult = false; *hasResultType = false; break; + case SpvOpEndStreamPrimitive: *hasResult = false; *hasResultType = false; break; + case SpvOpControlBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpMemoryBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicLoad: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicStore: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicExchange: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicCompareExchange: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicCompareExchangeWeak: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIIncrement: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIDecrement: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicISub: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicOr: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicXor: *hasResult = true; *hasResultType = true; break; + case SpvOpPhi: *hasResult = true; *hasResultType = true; break; + case SpvOpLoopMerge: *hasResult = false; *hasResultType = false; break; + case SpvOpSelectionMerge: *hasResult = false; *hasResultType = false; break; + case SpvOpLabel: *hasResult = true; *hasResultType = false; break; + case SpvOpBranch: *hasResult = false; *hasResultType = false; break; + case SpvOpBranchConditional: *hasResult = false; *hasResultType = false; break; + case SpvOpSwitch: *hasResult = false; *hasResultType = false; break; + case SpvOpKill: *hasResult = false; *hasResultType = false; break; + case SpvOpReturn: *hasResult = false; *hasResultType = false; break; + case SpvOpReturnValue: *hasResult = false; *hasResultType = false; break; + case SpvOpUnreachable: *hasResult = false; *hasResultType = false; break; + case SpvOpLifetimeStart: *hasResult = false; *hasResultType = false; break; + case SpvOpLifetimeStop: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupAsyncCopy: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupWaitEvents: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupAll: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupAny: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpReadPipe: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReservedReadPipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReservedWritePipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case SpvOpCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case SpvOpIsValidReserveId: *hasResult = true; *hasResultType = true; break; + case SpvOpGetNumPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGetMaxPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case SpvOpEnqueueMarker: *hasResult = true; *hasResultType = true; break; + case SpvOpEnqueueKernel: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelNDrangeSubGroupCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelNDrangeMaxSubGroupSize: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelWorkGroupSize: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelPreferredWorkGroupSizeMultiple: *hasResult = true; *hasResultType = true; break; + case SpvOpRetainEvent: *hasResult = false; *hasResultType = false; break; + case SpvOpReleaseEvent: *hasResult = false; *hasResultType = false; break; + case SpvOpCreateUserEvent: *hasResult = true; *hasResultType = true; break; + case SpvOpIsValidEvent: *hasResult = true; *hasResultType = true; break; + case SpvOpSetUserEventStatus: *hasResult = false; *hasResultType = false; break; + case SpvOpCaptureEventProfilingInfo: *hasResult = false; *hasResultType = false; break; + case SpvOpGetDefaultQueue: *hasResult = true; *hasResultType = true; break; + case SpvOpBuildNDRange: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseFetch: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseDrefGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseTexelsResident: *hasResult = true; *hasResultType = true; break; + case SpvOpNoLine: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicFlagTestAndSet: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFlagClear: *hasResult = false; *hasResultType = false; break; + case SpvOpImageSparseRead: *hasResult = true; *hasResultType = true; break; + case SpvOpSizeOf: *hasResult = true; *hasResultType = true; break; + case SpvOpTypePipeStorage: *hasResult = true; *hasResultType = false; break; + case SpvOpConstantPipeStorage: *hasResult = true; *hasResultType = true; break; + case SpvOpCreatePipeFromPipeStorage: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelLocalSizeForSubgroupCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelMaxNumSubgroups: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeNamedBarrier: *hasResult = true; *hasResultType = false; break; + case SpvOpNamedBarrierInitialize: *hasResult = true; *hasResultType = true; break; + case SpvOpMemoryNamedBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpModuleProcessed: *hasResult = false; *hasResultType = false; break; + case SpvOpExecutionModeId: *hasResult = false; *hasResultType = false; break; + case SpvOpDecorateId: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupNonUniformElect: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAll: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAny: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAllEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBroadcastFirst: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallot: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformInverseBallot: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotBitExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotBitCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotFindLSB: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotFindMSB: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffle: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleUp: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleDown: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformIMul: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMul: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseOr: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalOr: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformQuadBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformQuadSwap: *hasResult = true; *hasResultType = true; break; + case SpvOpCopyLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrDiff: *hasResult = true; *hasResultType = true; break; + case SpvOpTerminateInvocation: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupBallotKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupFirstInvocationKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAllKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAnyKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAllEqualKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupReadInvocationKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpTraceRayKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpExecuteCallableKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpConvertUToAccelerationStructureKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpIgnoreIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpTerminateRayKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeRayQueryKHR: *hasResult = true; *hasResultType = false; break; + case SpvOpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupIAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpFragmentMaskFetchAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpFragmentFetchAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpReadClockKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleFootprintNV: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformPartitionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePackedPrimitiveIndices4x8NV: *hasResult = false; *hasResultType = false; break; + case SpvOpReportIntersectionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTerminateRayNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTraceNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeAccelerationStructureNV: *hasResult = true; *hasResultType = false; break; + case SpvOpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; + case SpvOpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixStoreNV: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixLengthNV: *hasResult = true; *hasResultType = true; break; + case SpvOpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpDemoteToHelperInvocationEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleXorINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupImageBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAbsISubINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAverageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAverageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpISubSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpConstFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionPointerCallINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAsmTargetINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAsmINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAsmCallINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpDecorateString: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberDecorateString: *hasResult = false; *hasResultType = false; break; + case SpvOpVmeImageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcRefPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcSicPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcMcePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcMceResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcRefResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcSicResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToImePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToImeResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToRefPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToRefResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToSicPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToSicResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMajorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMinorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterDirectionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeRefWindowSizeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeAdjustRefOffsetINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetWeightedSadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetBorderReachedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcFmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcBmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureSkcINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureIpeLumaINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateIpeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetIpeChromaModeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpVariableLengthArrayINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSaveMemoryINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpRestoreMemoryINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpLoopControlINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpPtrCastToCrossWorkgroupINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpCrossWorkgroupCastToPtrINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpReadPipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFPGARegINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFAddEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeBufferSurfaceINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeStructContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSpecConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break; + } +} +#endif /* SPV_ENABLE_UTILITY_CODE */ + +#endif + From fa08cda9e562cad8d39b8850f81c72170e5fba60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 11 Feb 2021 13:29:01 +0100 Subject: [PATCH 18/44] ShaderTools: fart. --- src/Magnum/ShaderTools/ShaderTools.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Magnum/ShaderTools/ShaderTools.h b/src/Magnum/ShaderTools/ShaderTools.h index 3d901a533..701bb83f4 100644 --- a/src/Magnum/ShaderTools/ShaderTools.h +++ b/src/Magnum/ShaderTools/ShaderTools.h @@ -1,5 +1,5 @@ -#ifndef Magnum_Trade_ShaderTools_h -#define Magnum_Trade_ShaderTools_h +#ifndef Magnum_ShaderTools_ShaderTools_h +#define Magnum_ShaderTools_ShaderTools_h /* This file is part of Magnum. @@ -29,7 +29,7 @@ * @brief Forward declarations for the @ref Magnum::ShaderTools namespace */ -namespace Magnum { namespace Trade { +namespace Magnum { namespace ShaderTools { #ifndef DOXYGEN_GENERATING_OUTPUT class AbstractConverter; From 97d1c17ae18b97ca5ec359bf3bdca225a5c2fd69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 11 Feb 2021 13:38:26 +0100 Subject: [PATCH 19/44] ShaderTools: move the Stage enum to a dedicated header. Will be used by SPIR-V reflection APIs, which don't need the whole converter interface and all the pluginmanager stuff dragged along. --- doc/snippets/MagnumShaderTools.cpp | 1 + src/Magnum/ShaderTools/AbstractConverter.cpp | 28 ------ src/Magnum/ShaderTools/AbstractConverter.h | 56 ++---------- src/Magnum/ShaderTools/CMakeLists.txt | 4 +- src/Magnum/ShaderTools/ShaderTools.h | 3 + src/Magnum/ShaderTools/Stage.cpp | 60 +++++++++++++ src/Magnum/ShaderTools/Stage.h | 89 +++++++++++++++++++ .../Test/AbstractConverterTest.cpp | 12 +-- src/Magnum/ShaderTools/Test/CMakeLists.txt | 3 +- src/Magnum/ShaderTools/Test/StageTest.cpp | 53 +++++++++++ src/Magnum/ShaderTools/shaderconverter.cpp | 1 + .../Test/AnyConverterTest.cpp | 1 + 12 files changed, 221 insertions(+), 90 deletions(-) create mode 100644 src/Magnum/ShaderTools/Stage.cpp create mode 100644 src/Magnum/ShaderTools/Stage.h create mode 100644 src/Magnum/ShaderTools/Test/StageTest.cpp diff --git a/doc/snippets/MagnumShaderTools.cpp b/doc/snippets/MagnumShaderTools.cpp index f900e9d01..2a2757bbc 100644 --- a/doc/snippets/MagnumShaderTools.cpp +++ b/doc/snippets/MagnumShaderTools.cpp @@ -31,6 +31,7 @@ #include "Magnum/FileCallback.h" #include "Magnum/ShaderTools/AbstractConverter.h" +#include "Magnum/ShaderTools/Stage.h" #define DOXYGEN_IGNORE(...) __VA_ARGS__ diff --git a/src/Magnum/ShaderTools/AbstractConverter.cpp b/src/Magnum/ShaderTools/AbstractConverter.cpp index 8a57fe8aa..0443bb7db 100644 --- a/src/Magnum/ShaderTools/AbstractConverter.cpp +++ b/src/Magnum/ShaderTools/AbstractConverter.cpp @@ -741,32 +741,4 @@ Debug& operator<<(Debug& debug, const Format value) { return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; } -Debug& operator<<(Debug& debug, const Stage value) { - debug << "ShaderTools::Stage" << Debug::nospace; - - switch(value) { - /* LCOV_EXCL_START */ - #define _c(v) case Stage::v: return debug << "::" #v; - _c(Unspecified) - _c(Vertex) - _c(Fragment) - _c(Geometry) - _c(TessellationControl) - _c(TessellationEvaluation) - _c(Compute) - _c(RayGeneration) - _c(RayAnyHit) - _c(RayClosestHit) - _c(RayMiss) - _c(RayIntersection) - _c(RayCallable) - _c(MeshTask) - _c(Mesh) - #undef _c - /* LCOV_EXCL_STOP */ - } - - return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; -} - }} diff --git a/src/Magnum/ShaderTools/AbstractConverter.h b/src/Magnum/ShaderTools/AbstractConverter.h index bd57a7451..5dd8b9694 100644 --- a/src/Magnum/ShaderTools/AbstractConverter.h +++ b/src/Magnum/ShaderTools/AbstractConverter.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::ShaderTools::AbstractConverter, enum @ref Magnum::ShaderTools::ConverterFeature, @ref Magnum::ShaderTools::ConverterFlag, @ref Magnum::ShaderTools::Format, @ref Magnum::ShaderTools::Stage, enum set @ref Magnum::ShaderTools::ConverterFeatures, @ref Magnum::ShaderTools::ConverterFlags + * @brief Class @ref Magnum::ShaderTools::AbstractConverter, enum @ref Magnum::ShaderTools::ConverterFeature, @ref Magnum::ShaderTools::ConverterFlag, @ref Magnum::ShaderTools::Format, enum set @ref Magnum::ShaderTools::ConverterFeatures, @ref Magnum::ShaderTools::ConverterFlags * @m_since_latest */ @@ -34,8 +34,13 @@ #include #include "Magnum/Magnum.h" +#include "Magnum/ShaderTools/ShaderTools.h" #include "Magnum/ShaderTools/visibility.h" +#ifdef MAGNUM_BUILD_DEPRECATED +#include "Magnum/ShaderTools/Stage.h" +#endif + namespace Magnum { namespace ShaderTools { /** @@ -264,55 +269,6 @@ enum class Format: UnsignedInt { */ MAGNUM_SHADERTOOLS_EXPORT Debug& operator<<(Debug& debug, Format value); -/** -@brief Shader stage -@m_since_latest - -@see @ref AbstractConverter -*/ -enum class Stage: UnsignedInt { - /** - * Unspecified stage. When used in the - * @ref AbstractConverter::validateFile(), - * @ref AbstractConverter::convertFileToFile() "convertFileToFile()", - * @ref AbstractConverter::convertFileToData() "convertFileToData()", - * @ref AbstractConverter::linkFilesToFile() "linkFilesToFile()" or - * @ref AbstractConverter::linkFilesToData() "linkFilesToData()" APIs, - * particular plugins may attempt to detect the stage from filename, the - * shader stage might also be encoded directly in certain - * @ref Format "Format"s. Leaving the stage unspecified might limit - * validation and conversion capabilities, see documentation of a - * particular converter for concrete behavior. - * - * This value is guaranteed to be @cpp 0 @ce, which means you're encouraged - * to simply use @cpp {} @ce in function calls and elsewhere. - */ - Unspecified = 0, - - Vertex, /**< Vertex stage */ - Fragment, /**< Fragment stage */ - Geometry, /**< Geometry stage */ - TessellationControl, /**< Tessellation control stage */ - TessellationEvaluation, /**< Tessellation evaluation stage */ - Compute, /**< Compute stage */ - - RayGeneration, /**< Ray generation stage */ - RayAnyHit, /**< Ray any hit stage */ - RayClosestHit, /**< Ray closest hit stage */ - RayMiss, /**< Ray miss stage */ - RayIntersection, /**< Ray intersection stage */ - RayCallable, /**< Ray callable stage */ - - MeshTask, /**< Mesh task stage */ - Mesh /**< Mesh stage */ -}; - -/** -@debugoperatorenum{Stage} -@m_since_latest -*/ -MAGNUM_SHADERTOOLS_EXPORT Debug& operator<<(Debug& debug, Stage value); - /** @brief Base for shader converter plugins @m_since_latest diff --git a/src/Magnum/ShaderTools/CMakeLists.txt b/src/Magnum/ShaderTools/CMakeLists.txt index 02a9ccb6d..041995e5d 100644 --- a/src/Magnum/ShaderTools/CMakeLists.txt +++ b/src/Magnum/ShaderTools/CMakeLists.txt @@ -30,11 +30,13 @@ set(MagnumShaderTools_SRCS ) # Files compiled with different flags for main library and unit test library set(MagnumShaderTools_GracefulAssert_SRCS - AbstractConverter.cpp) + AbstractConverter.cpp + Stage.cpp) set(MagnumShaderTools_HEADERS AbstractConverter.h ShaderTools.h + Stage.h visibility.h) diff --git a/src/Magnum/ShaderTools/ShaderTools.h b/src/Magnum/ShaderTools/ShaderTools.h index 701bb83f4..6440c1d65 100644 --- a/src/Magnum/ShaderTools/ShaderTools.h +++ b/src/Magnum/ShaderTools/ShaderTools.h @@ -29,11 +29,14 @@ * @brief Forward declarations for the @ref Magnum::ShaderTools namespace */ +#include "Magnum/Types.h" + namespace Magnum { namespace ShaderTools { #ifndef DOXYGEN_GENERATING_OUTPUT class AbstractConverter; #endif +enum class Stage: UnsignedInt; }} diff --git a/src/Magnum/ShaderTools/Stage.cpp b/src/Magnum/ShaderTools/Stage.cpp new file mode 100644 index 000000000..84fb6d6dd --- /dev/null +++ b/src/Magnum/ShaderTools/Stage.cpp @@ -0,0 +1,60 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + 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. +*/ + +#include "Stage.h" + +#include + +namespace Magnum { namespace ShaderTools { + +Debug& operator<<(Debug& debug, const Stage value) { + debug << "ShaderTools::Stage" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(v) case Stage::v: return debug << "::" #v; + _c(Unspecified) + _c(Vertex) + _c(Fragment) + _c(Geometry) + _c(TessellationControl) + _c(TessellationEvaluation) + _c(Compute) + _c(RayGeneration) + _c(RayAnyHit) + _c(RayClosestHit) + _c(RayMiss) + _c(RayIntersection) + _c(RayCallable) + _c(MeshTask) + _c(Mesh) + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; +} + +}} diff --git a/src/Magnum/ShaderTools/Stage.h b/src/Magnum/ShaderTools/Stage.h new file mode 100644 index 000000000..ebc44e7b5 --- /dev/null +++ b/src/Magnum/ShaderTools/Stage.h @@ -0,0 +1,89 @@ +#ifndef Magnum_ShaderTools_Stage_h +#define Magnum_ShaderTools_Stage_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Enum @ref Magnum::ShaderTools::Stage + * @m_since_latest + */ + +#include "Magnum/Magnum.h" +#include "Magnum/ShaderTools/visibility.h" + +namespace Magnum { namespace ShaderTools { + +/** +@brief Shader stage +@m_since_latest + +@see @ref AbstractConverter +*/ +enum class Stage: UnsignedInt { + /** + * Unspecified stage. When used in the + * @ref AbstractConverter::validateFile(), + * @ref AbstractConverter::convertFileToFile() "convertFileToFile()", + * @ref AbstractConverter::convertFileToData() "convertFileToData()", + * @ref AbstractConverter::linkFilesToFile() "linkFilesToFile()" or + * @ref AbstractConverter::linkFilesToData() "linkFilesToData()" APIs, + * particular plugins may attempt to detect the stage from filename, the + * shader stage might also be encoded directly in certain + * @ref Format "Format"s. Leaving the stage unspecified might limit + * validation and conversion capabilities, see documentation of a + * particular converter for concrete behavior. + * + * This value is guaranteed to be @cpp 0 @ce, which means you're encouraged + * to simply use @cpp {} @ce in function calls and elsewhere. + */ + Unspecified = 0, + + Vertex, /**< Vertex stage */ + Fragment, /**< Fragment stage */ + Geometry, /**< Geometry stage */ + TessellationControl, /**< Tessellation control stage */ + TessellationEvaluation, /**< Tessellation evaluation stage */ + Compute, /**< Compute stage */ + + RayGeneration, /**< Ray generation stage */ + RayAnyHit, /**< Ray any hit stage */ + RayClosestHit, /**< Ray closest hit stage */ + RayMiss, /**< Ray miss stage */ + RayIntersection, /**< Ray intersection stage */ + RayCallable, /**< Ray callable stage */ + + MeshTask, /**< Mesh task stage */ + Mesh /**< Mesh stage */ +}; + +/** +@debugoperatorenum{Stage} +@m_since_latest +*/ +MAGNUM_SHADERTOOLS_EXPORT Debug& operator<<(Debug& debug, Stage value); + +}} + +#endif diff --git a/src/Magnum/ShaderTools/Test/AbstractConverterTest.cpp b/src/Magnum/ShaderTools/Test/AbstractConverterTest.cpp index 20be04c71..56b75ac64 100644 --- a/src/Magnum/ShaderTools/Test/AbstractConverterTest.cpp +++ b/src/Magnum/ShaderTools/Test/AbstractConverterTest.cpp @@ -36,6 +36,7 @@ #include "Magnum/FileCallback.h" #include "Magnum/ShaderTools/AbstractConverter.h" +#include "Magnum/ShaderTools/Stage.h" #include "configure.h" @@ -180,7 +181,6 @@ struct AbstractConverterTest: TestSuite::Tester { void debugFlag(); void debugFlags(); void debugFormat(); - void debugStage(); }; AbstractConverterTest::AbstractConverterTest() { @@ -319,8 +319,7 @@ AbstractConverterTest::AbstractConverterTest() { &AbstractConverterTest::debugFeatures, &AbstractConverterTest::debugFlag, &AbstractConverterTest::debugFlags, - &AbstractConverterTest::debugFormat, - &AbstractConverterTest::debugStage}); + &AbstractConverterTest::debugFormat}); /* Create testing dir */ Utility::Directory::mkpath(SHADERTOOLS_TEST_OUTPUT_DIR); @@ -3428,13 +3427,6 @@ void AbstractConverterTest::debugFormat() { CORRADE_COMPARE(out.str(), "ShaderTools::Format::Glsl ShaderTools::Format(0xf0)\n"); } -void AbstractConverterTest::debugStage() { - std::ostringstream out; - - Debug{&out} << Stage::RayMiss << Stage(0xf0); - CORRADE_COMPARE(out.str(), "ShaderTools::Stage::RayMiss ShaderTools::Stage(0xf0)\n"); -} - }}}} CORRADE_TEST_MAIN(Magnum::ShaderTools::Test::AbstractConverterTest) diff --git a/src/Magnum/ShaderTools/Test/CMakeLists.txt b/src/Magnum/ShaderTools/Test/CMakeLists.txt index e0d394d58..a17fbfce7 100644 --- a/src/Magnum/ShaderTools/Test/CMakeLists.txt +++ b/src/Magnum/ShaderTools/Test/CMakeLists.txt @@ -37,9 +37,10 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake corrade_add_test(ShaderToolsAbstractConverterTest AbstractConverterTest.cpp LIBRARIES MagnumShaderToolsTestLib FILES file.dat another.dat) - target_include_directories(ShaderToolsAbstractConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +corrade_add_test(ShaderToolsStageTest StageTest.cpp LIBRARIES MagnumShaderTools) set_target_properties( ShaderToolsAbstractConverterTest + ShaderToolsStageTest PROPERTIES FOLDER "Magnum/ShaderTools/Test") diff --git a/src/Magnum/ShaderTools/Test/StageTest.cpp b/src/Magnum/ShaderTools/Test/StageTest.cpp new file mode 100644 index 000000000..fc4fb44c9 --- /dev/null +++ b/src/Magnum/ShaderTools/Test/StageTest.cpp @@ -0,0 +1,53 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + 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. +*/ + +#include +#include +#include + +#include "Magnum/ShaderTools/Stage.h" + +namespace Magnum { namespace ShaderTools { namespace Test { namespace { + +struct StageTest: TestSuite::Tester { + explicit StageTest(); + + void debug(); +}; + +StageTest::StageTest() { + addTests({&StageTest::debug}); +} + +void StageTest::debug() { + std::ostringstream out; + + Debug{&out} << Stage::RayMiss << Stage(0xf0); + CORRADE_COMPARE(out.str(), "ShaderTools::Stage::RayMiss ShaderTools::Stage(0xf0)\n"); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::ShaderTools::Test::StageTest) diff --git a/src/Magnum/ShaderTools/shaderconverter.cpp b/src/Magnum/ShaderTools/shaderconverter.cpp index 5b437b027..b2f722b34 100644 --- a/src/Magnum/ShaderTools/shaderconverter.cpp +++ b/src/Magnum/ShaderTools/shaderconverter.cpp @@ -33,6 +33,7 @@ #include "Magnum/Implementation/converterUtilities.h" #include "Magnum/Math/Functions.h" #include "Magnum/ShaderTools/AbstractConverter.h" +#include "Magnum/ShaderTools/Stage.h" namespace Magnum { diff --git a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp index 06452901d..af4469284 100644 --- a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp +++ b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp @@ -34,6 +34,7 @@ #include #include "Magnum/ShaderTools/AbstractConverter.h" +#include "Magnum/ShaderTools/Stage.h" #include "configure.h" From 9b5c62fa46fdfcc2db7144e82289ae52fd29c410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 11 Feb 2021 13:45:35 +0100 Subject: [PATCH 20/44] ShaderTools: recognize also the Kernel stage. --- src/Magnum/ShaderTools/Stage.cpp | 1 + src/Magnum/ShaderTools/Stage.h | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Magnum/ShaderTools/Stage.cpp b/src/Magnum/ShaderTools/Stage.cpp index 84fb6d6dd..9351679d1 100644 --- a/src/Magnum/ShaderTools/Stage.cpp +++ b/src/Magnum/ShaderTools/Stage.cpp @@ -50,6 +50,7 @@ Debug& operator<<(Debug& debug, const Stage value) { _c(RayCallable) _c(MeshTask) _c(Mesh) + _c(Kernel) #undef _c /* LCOV_EXCL_STOP */ } diff --git a/src/Magnum/ShaderTools/Stage.h b/src/Magnum/ShaderTools/Stage.h index ebc44e7b5..b4b78f027 100644 --- a/src/Magnum/ShaderTools/Stage.h +++ b/src/Magnum/ShaderTools/Stage.h @@ -75,7 +75,15 @@ enum class Stage: UnsignedInt { RayCallable, /**< Ray callable stage */ MeshTask, /**< Mesh task stage */ - Mesh /**< Mesh stage */ + Mesh, /**< Mesh stage */ + + /** + * Compute kernel. + * + * Compared to @ref Stage::Compute, which is used by graphics APIs such as + * Vulkan or OpenGL, this one is for use by OpenCL. + */ + Kernel }; /** From 50d187fe830295428f4e9a05d2c41b681a568263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 11 Feb 2021 14:19:29 +0100 Subject: [PATCH 21/44] {scene,shader,image}converter: make output argument treating consistent. If it's ignored, a warning is printed to catch accidents, but not an error since it should be possible to just append --info to existing command line to see what the input is about. --- src/Magnum/MeshTools/sceneconverter.cpp | 13 +++++++++++-- src/Magnum/ShaderTools/shaderconverter.cpp | 10 +++++----- src/Magnum/Trade/imageconverter.cpp | 19 +++++++++++++++++-- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/Magnum/MeshTools/sceneconverter.cpp b/src/Magnum/MeshTools/sceneconverter.cpp index 72278f7fa..8e1676c3b 100644 --- a/src/Magnum/MeshTools/sceneconverter.cpp +++ b/src/Magnum/MeshTools/sceneconverter.cpp @@ -86,7 +86,7 @@ magnum-sceneconverter [-h|--help] [-I|--importer IMPORTER] Arguments: - `input` --- input file -- `output` --- output file, ignored if `--info` is present +- `output` --- output file; ignored if `--info` is present - `-h`, `--help` --- display this help message and exit - `-I`, `--importer IMPORTER` --- scene importer plugin (default: @ref Trade::AnySceneImporter "AnySceneImporter") @@ -199,7 +199,7 @@ UnsignedInt namedAttributeId(const Trade::MeshData& mesh, UnsignedInt id) { int main(int argc, char** argv) { Utility::Arguments args; args.addArgument("input").setHelp("input", "input file") - .addArgument("output").setHelp("output", "output file, ignored if --info is present") + .addArgument("output").setHelp("output", "output file; ignored if --info is present") .addOption('I', "importer", "AnySceneImporter").setHelp("importer", "scene importer plugin") .addArrayOption('C', "converter").setHelp("converter", "scene converter plugin(s)") .addOption("plugin-dir").setHelp("plugin-dir", "override base plugin dir", "DIR") @@ -241,6 +241,15 @@ save its output; if no -C / --converter is specified, AnySceneConverter is used.)") .parse(argc, argv); + /* Generic checks */ + if(!args.value("output").isEmpty()) { + /* Not an error in this case, it should be possible to just append + --info to existing command line without having to remove anything. + But print a warning at least, it could also be a mistyped option. */ + if(args.isSet("info")) + Warning{} << "Ignoring output file for --info:" << args.value("output"); + } + PluginManager::Manager importerManager{ args.value("plugin-dir").empty() ? std::string{} : Utility::Directory::join(args.value("plugin-dir"), Trade::AbstractImporter::pluginSearchPaths()[0])}; diff --git a/src/Magnum/ShaderTools/shaderconverter.cpp b/src/Magnum/ShaderTools/shaderconverter.cpp index b2f722b34..543c08925 100644 --- a/src/Magnum/ShaderTools/shaderconverter.cpp +++ b/src/Magnum/ShaderTools/shaderconverter.cpp @@ -75,7 +75,7 @@ magnum-shaderconverter [-h|--help] [--validate] [--link] Arguments: - `input` --- input file(s) -- `output` --- output file, ignored if `--validate` is present. If neither +- `output` --- output file, disallowed for `--validate`. If neither `--validate` nor `--link` is present, corresponds to the @ref ShaderTools::AbstractConverter::convertFileToFile() function. - `-h`, `--help` --- display this help message and exit @@ -173,7 +173,7 @@ using namespace Magnum; int main(int argc, char** argv) { Utility::Arguments args; args.addArrayArgument("input").setHelp("input", "input file(s)") - .addArgument("output").setHelp("output", "output file, ignored if --validate is present") + .addArgument("output").setHelp("output", "output file, disallowed for --validate") .addBooleanOption("validate").setHelp("validate", "validate input") .addBooleanOption("link").setHelp("link", "link multiple input files together") .addArrayOption('C', "converter").setHelp("converter", "shader converter plugin(s)") @@ -232,9 +232,9 @@ see documentation of a particular converter for more information.)") .parse(argc, argv); /* Generic checks */ - if(args.isSet("validate")) { - if(!args.value("output").isEmpty()) { - Error{} << "Output file shouldn't be set for --validate"; + if(!args.value("output").isEmpty()) { + if(args.isSet("validate")) { + Error{} << "Output file shouldn't be set for --validate:" << args.value("output"); return 1; } } diff --git a/src/Magnum/Trade/imageconverter.cpp b/src/Magnum/Trade/imageconverter.cpp index 1af8efa24..99cd708b5 100644 --- a/src/Magnum/Trade/imageconverter.cpp +++ b/src/Magnum/Trade/imageconverter.cpp @@ -75,7 +75,8 @@ magnum-imageconverter [-h|--help] [-I|--importer IMPORTER] Arguments: - `input` --- input image -- `output` --- output image, ignored if `--in-place` or `--info` is present +- `output` --- output image; ignored if `--info` is present, disallowed for + `--in-place` - `-h`, `--help` --- display this help message and exit - `-I`, `--importer IMPORTER` --- image importer plugin (default: @ref Trade::AnyImageImporter "AnyImageImporter") @@ -145,7 +146,7 @@ using namespace Magnum; int main(int argc, char** argv) { Utility::Arguments args; args.addArgument("input").setHelp("input", "input image") - .addArgument("output").setHelp("output", "output image, ignored if --in-place or --info is present") + .addArgument("output").setHelp("output", "output image; ignored if --info is present, disallowed for --in-place") .addOption('I', "importer", "AnyImageImporter").setHelp("importer", "image importer plugin") .addOption('C', "converter", "AnyImageConverter").setHelp("converter", "image converter plugin") .addOption("plugin-dir").setHelp("plugin-dir", "override base plugin dir", "DIR") @@ -182,6 +183,20 @@ plugin configuration. If the = character is omitted, it's equivalent to saying key=true; configuration subgroups are delimited with /.)") .parse(argc, argv); + /* Generic checks */ + if(!args.value("output").isEmpty()) { + if(args.isSet("in-place")) { + Error{} << "Output file shouldn't be set for --in-place:" << args.value("output"); + return 1; + } + + /* Not an error in this case, it should be possible to just append + --info to existing command line without having to remove anything. + But print a warning at least, it could also be a mistyped option. */ + if(args.isSet("info")) + Warning{} << "Ignoring output file for --info:" << args.value("output"); + } + PluginManager::Manager importerManager{ args.value("plugin-dir").empty() ? std::string{} : Utility::Directory::join(args.value("plugin-dir"), Trade::AbstractImporter::pluginSearchPaths()[0])}; From cbdc5c9e215fc820a679ce855557294cc3121b91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 12 Feb 2021 11:27:04 +0100 Subject: [PATCH 22/44] ShaderTools: initial internal tools for SPIR-V reflection. Currently those will be needed mainly by the Vk library to patch around a SwiftShader bug. I'm not sure yet how the public API should look so it's all hidden in the Implementation namespace for now. --- src/Magnum/ShaderTools/CMakeLists.txt | 10 +- src/Magnum/ShaderTools/Implementation/spirv.h | 183 ++++++++++++ src/Magnum/ShaderTools/Test/CMakeLists.txt | 5 + src/Magnum/ShaderTools/Test/SpirvTest.cpp | 264 ++++++++++++++++++ .../Test/SpirvTestFiles/convert.sh | 5 + .../SpirvTestFiles/entrypoint-interface.spv | Bin 0 -> 412 bytes .../entrypoint-interface.spvasm | 23 ++ 7 files changed, 488 insertions(+), 2 deletions(-) create mode 100644 src/Magnum/ShaderTools/Implementation/spirv.h create mode 100644 src/Magnum/ShaderTools/Test/SpirvTest.cpp create mode 100755 src/Magnum/ShaderTools/Test/SpirvTestFiles/convert.sh create mode 100644 src/Magnum/ShaderTools/Test/SpirvTestFiles/entrypoint-interface.spv create mode 100644 src/Magnum/ShaderTools/Test/SpirvTestFiles/entrypoint-interface.spvasm diff --git a/src/Magnum/ShaderTools/CMakeLists.txt b/src/Magnum/ShaderTools/CMakeLists.txt index 041995e5d..11c51889b 100644 --- a/src/Magnum/ShaderTools/CMakeLists.txt +++ b/src/Magnum/ShaderTools/CMakeLists.txt @@ -40,6 +40,9 @@ set(MagnumShaderTools_HEADERS visibility.h) +set(MagnumShaderTools_PRIVATE_HEADERS + Implementation/spirv.h) + if(NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/configure.h) @@ -48,7 +51,8 @@ endif() # # Objects shared between main and test library # add_library(MagnumShaderToolsObjects OBJECT # ${MagnumShaderTools_SRCS} -# ${MagnumShaderTools_HEADERS}) +# # ${MagnumShaderTools_HEADERS} +# # ${MagnumShaderTools_PRIVATE_HEADERS}) # target_include_directories(MagnumShaderToolsObjects PUBLIC $) # if(NOT BUILD_STATIC) # target_compile_definitions(MagnumShaderToolsObjects PRIVATE "MagnumShaderToolsObjects_EXPORTS") @@ -61,7 +65,9 @@ endif() # Main ShaderTools library add_library(MagnumShaderTools ${SHARED_OR_STATIC} # $ - ${MagnumShaderTools_GracefulAssert_SRCS}) + ${MagnumShaderTools_GracefulAssert_SRCS} + ${MagnumShaderTools_HEADERS} + ${MagnumShaderTools_PRIVATE_HEADERS}) set_target_properties(MagnumShaderTools PROPERTIES DEBUG_POSTFIX "-d" FOLDER "Magnum/ShaderTools") diff --git a/src/Magnum/ShaderTools/Implementation/spirv.h b/src/Magnum/ShaderTools/Implementation/spirv.h new file mode 100644 index 000000000..610ef019e --- /dev/null +++ b/src/Magnum/ShaderTools/Implementation/spirv.h @@ -0,0 +1,183 @@ +#ifndef Magnum_ShaderTools_Implementation_spirv_h +#define Magnum_ShaderTools_Implementation_spirv_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + 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. +*/ + +#include +#include +#include +#include + +#include "Magnum/Magnum.h" +#include "MagnumExternal/Vulkan/spirv.h" + +namespace Magnum { namespace ShaderTools { namespace Implementation { namespace { + +/* This is used by both magnum-shaderconverter and the Vk library for + SwiftShader workarounds but we don't want the Vk library to depend on + ShaderTools, so the minimal needed subset is made header-only. + + Eventually this should be turned into a public API, but so far it's just a + bag of random functions with very specific usage patterns and isn't clear + yet how to expose a usable interface. Moreover, the SwiftShader patching + needs to mutate the original, which means the outputs are pointers to the + original data. */ + +/* If the code looks like a valid SPIR-V, returns everything after the + header. If not, nullptr. */ +Containers::ArrayView spirvData(const void* code, UnsignedInt size) { + const UnsignedInt* const spirv = static_cast(code); + /* Not >= 5*4 because just the header alone is useless also */ + return size % 4 == 0 && size > 5*4 && spirv[0] == SpvMagicNumber ? + Containers::ArrayView{spirv, size/4}.suffix(5) : nullptr; +} + +/* When an instruction is found, the `data` is advanced after it in order to + allow calling this function in a loop. When not found, `data` is left + untouched. */ +Containers::ArrayView spirvFindInstruction(Containers::ArrayView& data, const SpvOp op) { + /* Copy the view and iterate that. If we find the instruction, update the + passed `data` reference, if not, keep it as it was -- that way, if the + find fails, `data` won't become empty and can be used further */ + for(Containers::ArrayView dataIteration = data; !dataIteration.empty(); ) { + const UnsignedInt instructionSize = dataIteration[0] >> 16; + const UnsignedInt instructionOp = dataIteration[0] & 0xffff; + + /* Corrupted SPIR-V */ + /** @todo print a message here? */ + if(dataIteration.size() < instructionSize) { + data = dataIteration; + return nullptr; + } + + /* This is the instruction we're looking for, return it and update the + view to point after it. */ + if(instructionOp == op) { + data = dataIteration.suffix(instructionSize); + return dataIteration.prefix(instructionSize); + } + + /* Otherwise advance the view for next round */ + dataIteration = dataIteration.suffix(instructionSize); + } + + /* Nothing found. Leave the input data as-is. */ + return nullptr; +} + +struct SpirvEntrypoint { + Containers::Reference executionModel; + Containers::StringView name; + Containers::ArrayView interfaces; +}; + +/* When an entrypoint is found, the `data` is advanced after the instruction in + order to allow calling this function in a loop. When not found, `data` is + left untouched. Most of other SPIR-V code is meant to appear after the + entrypoints, so it's fine to feed the resulting `data` to + spirvEntrypointInterface() and others. */ +Containers::Optional spirvNextEntrypoint(Containers::ArrayView& data) { + while(const Containers::ArrayView entryPoint = spirvFindInstruction(data, SpvOpEntryPoint)) { + /* Expecting at least op, execution model, ID, name. If less, it's an + invalid SPIR-V. */ + /** @todo print a message here? */ + if(entryPoint.size() < 4) return {}; + + /* Find where the name ends and interface IDs start. According to the + spec, a string literal is null-terminated and all bytes after are + zeros as well, so it should be enough to check that the last byte is + zero. */ + Containers::ArrayView interfaces; + for(std::size_t i = 3; i != entryPoint.size(); ++i) { + if(entryPoint[i] >> 24 == 0) { + interfaces = entryPoint.suffix(i + 1); + break; + } + } + + return SpirvEntrypoint{ + *reinterpret_cast(entryPoint + 1), + reinterpret_cast(entryPoint + 3), + interfaces + }; + } + + return {}; +} + +struct SpirvEntrypointInterface { + /* If null, the interface might be for example builtin */ + const UnsignedInt* location; + /* If null, the SPIR-V is probably invalid */ + const SpvStorageClass* storageClass; +}; + +/* Unlike above, `data` isn't modified by this function -- because the + decoration and variable instructions are likely intermixed for different + entrypoint, it makes sense to restart the search from the beginning for each + entrypoint. + + The `out` array is expected to have the same size as entrypoint.interfaces + and be zero-initialized (so the not found data stay null). */ +void spirvEntrypointInterface(Containers::ArrayView data, const SpirvEntrypoint& entrypoint, Containers::ArrayView out) { + CORRADE_INTERNAL_ASSERT(out.size() == entrypoint.interfaces.size()); + + /* Find location decorations */ + while(const Containers::ArrayView decoration = spirvFindInstruction(data, SpvOpDecorate)) { + /* Expecting at least op, ID, SpvDecorationLocation, location. The + instruction can be three words, so if we get less than 4 it's not an + error. */ + if(decoration.size() < 4 || decoration[2] != SpvDecorationLocation) + continue; + + for(std::size_t i = 0; i != entrypoint.interfaces.size(); ++i) { + if(decoration[1] == entrypoint.interfaces[i]) { + out[i].location = decoration + 3; + break; + } + } + } + + /* Find storage classes. According to the spec, OpVariable is meant to + appear after OpDecorate, so we don't need to restart from the + beginning. */ + while(const Containers::ArrayView variable = spirvFindInstruction(data, SpvOpVariable)) { + /* Expecting at least op, result, ID, SpvStorageClass. If less, it's an + invalid SPIR-V. */ + /** @todo print a message here? */ + if(variable.size() < 4) return; + + for(std::size_t i = 0; i != entrypoint.interfaces.size(); ++i) { + if(variable[2] == entrypoint.interfaces[i]) { + out[i].storageClass = reinterpret_cast(variable + 3); + break; + } + } + } +} + +}}}} + +#endif diff --git a/src/Magnum/ShaderTools/Test/CMakeLists.txt b/src/Magnum/ShaderTools/Test/CMakeLists.txt index a17fbfce7..bb15276e9 100644 --- a/src/Magnum/ShaderTools/Test/CMakeLists.txt +++ b/src/Magnum/ShaderTools/Test/CMakeLists.txt @@ -38,9 +38,14 @@ corrade_add_test(ShaderToolsAbstractConverterTest AbstractConverterTest.cpp LIBRARIES MagnumShaderToolsTestLib FILES file.dat another.dat) target_include_directories(ShaderToolsAbstractConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +corrade_add_test(ShaderToolsSpirvTest SpirvTest.cpp + LIBRARIES MagnumShaderTools + FILES SpirvTestFiles/entrypoint-interface.spv) +target_include_directories(ShaderToolsSpirvTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) corrade_add_test(ShaderToolsStageTest StageTest.cpp LIBRARIES MagnumShaderTools) set_target_properties( ShaderToolsAbstractConverterTest + ShaderToolsSpirvTest ShaderToolsStageTest PROPERTIES FOLDER "Magnum/ShaderTools/Test") diff --git a/src/Magnum/ShaderTools/Test/SpirvTest.cpp b/src/Magnum/ShaderTools/Test/SpirvTest.cpp new file mode 100644 index 000000000..d2eae7a2d --- /dev/null +++ b/src/Magnum/ShaderTools/Test/SpirvTest.cpp @@ -0,0 +1,264 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + 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. +*/ + +#include +#include +#include +#include + +#include "Magnum/ShaderTools/Implementation/spirv.h" +#include "MagnumExternal/Vulkan/spirv.h" + +#include "configure.h" + +namespace Magnum { namespace ShaderTools { namespace Test { namespace { + +struct SpirvTest: TestSuite::Tester { + explicit SpirvTest(); + + void data(); + void dataInvalid(); + + void findInstruction(); + void findInstructionNotEnoughData(); + + void nextEntrypoint(); + void nextEntrypointInvalidInstruction(); + + void entrypointInterface(); + void entrypointInterfaceNothing(); +}; + +const UnsignedInt Data[] { + SpvMagicNumber, SpvVersion, 0, 66, 0, + 0 /* first instruction */ +}; + +const UnsignedInt JustHeader[]{ + SpvMagicNumber, SpvVersion, 0, 66, 0 +}; + +const UnsignedInt InvalidMagic[]{ + SpvMagicNumber + 1, SpvVersion, 0, 66, 0, + 0 /* first instruction */ +}; + +const struct { + const char* name; + Containers::ArrayView data; +} InvalidData[] { + {"empty", {}}, + {"just the header", JustHeader}, + {"invalid magic", InvalidMagic}, + {"size not divisible by four", Containers::arrayCast(Data).except(1)} +}; + +SpirvTest::SpirvTest() { + addTests({&SpirvTest::data}); + + addInstancedTests({&SpirvTest::dataInvalid}, + Containers::arraySize(InvalidData)); + + addTests({&SpirvTest::findInstruction, + &SpirvTest::findInstructionNotEnoughData, + + &SpirvTest::nextEntrypoint, + &SpirvTest::nextEntrypointInvalidInstruction, + + &SpirvTest::entrypointInterface, + &SpirvTest::entrypointInterfaceNothing}); +} + +void SpirvTest::data() { + CORRADE_COMPARE(Implementation::spirvData(Data, sizeof(Data)), Containers::arrayView(Data + 5, 1)); +} + +void SpirvTest::dataInvalid() { + auto&& data = InvalidData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + CORRADE_VERIFY(!Implementation::spirvData(data.data, data.data.size())); +} + +UnsignedInt op(UnsignedInt length, SpvOp op) { + return length << 16 | op; +} + +void SpirvTest::findInstruction() { + const UnsignedInt data[] { + op(3, SpvOpMemoryModel), SpvAddressingModelLogical, SpvMemoryModelGLSL450, + op(4, SpvOpDecorate), 12, SpvDecorationLocation, 0, + op(1, SpvOpNop), + op(1, SpvOpNop), + op(4, SpvOpDecorate), 13, SpvDecorationLocation, 1, + }; + Containers::ArrayView view = data; + + Containers::ArrayView decorate1 = Implementation::spirvFindInstruction(view, SpvOpDecorate); + CORRADE_COMPARE(decorate1.size(), 4); + CORRADE_COMPARE(decorate1.data(), data + 3); + CORRADE_COMPARE(view.data(), data + 7); + + /* Verify a single-word instruction works too */ + Containers::ArrayView nop = Implementation::spirvFindInstruction(view, SpvOpNop); + CORRADE_COMPARE(nop.size(), 1); + CORRADE_COMPARE(nop.data(), data + 7); + CORRADE_COMPARE(view.data(), data + 8); + + Containers::ArrayView decorate2 = Implementation::spirvFindInstruction(view, SpvOpDecorate); + CORRADE_COMPARE(decorate2.size(), 4); + CORRADE_COMPARE(decorate2.data(), data + 9); + CORRADE_COMPARE(view.data(), data + 13); + + /* We're at the end, there's no more OpDecorate instructions to find */ + CORRADE_VERIFY(!Implementation::spirvFindInstruction(view, SpvOpDecorate)); +} + +void SpirvTest::findInstructionNotEnoughData() { + const UnsignedInt data[] { + op(3, SpvOpMemoryModel), SpvAddressingModelLogical, SpvMemoryModelGLSL450, + /* Should be 4 */ + op(5, SpvOpDecorate), 12, SpvDecorationLocation, 0 + }; + Containers::ArrayView view = data; + + CORRADE_VERIFY(!Implementation::spirvFindInstruction(view, SpvOpDecorate)); + /* View gets set to the first invalid instruction */ + CORRADE_COMPARE(view.data(), data + 3); +} + +void SpirvTest::nextEntrypoint() { + Containers::Array data = Utility::Directory::read(Utility::Directory::join(SHADERTOOLS_TEST_DIR, "SpirvTestFiles/entrypoint-interface.spv")); + + /* The file is a full SPIR-V, strip the header first */ + Containers::ArrayView view = Implementation::spirvData(data, data.size()); + CORRADE_VERIFY(view); + + Containers::Optional vert = Implementation::spirvNextEntrypoint(view); + CORRADE_VERIFY(vert); + /* Verify that long names get recognized properly */ + CORRADE_COMPARE(vert->name, "vertexLongEntrypointName"); + CORRADE_COMPARE(vert->executionModel, SpvExecutionModelVertex); + /* We don't care about the contents, those would change with each assembly + anyway. Verified fully in entrypointInterface(). */ + CORRADE_COMPARE(vert->interfaces.size(), 4); + + Containers::Optional frag = Implementation::spirvNextEntrypoint(view); + CORRADE_VERIFY(frag); + CORRADE_COMPARE(frag->name, "fra"); + CORRADE_COMPARE(frag->executionModel, SpvExecutionModelFragment); + CORRADE_COMPARE(frag->interfaces.size(), 3); + + /* Only two entrypoints in this file */ + CORRADE_VERIFY(!Implementation::spirvNextEntrypoint(view)); +} + +void SpirvTest::nextEntrypointInvalidInstruction() { + const UnsignedInt data[] { + op(3, SpvOpMemoryModel), SpvAddressingModelLogical, SpvMemoryModelGLSL450, + + /* Should be 4 (missing name) */ + op(3, SpvOpEntryPoint), SpvExecutionModelVertex, 1 + }; + Containers::ArrayView view = data; + + CORRADE_VERIFY(!Implementation::spirvNextEntrypoint(view)); +} + +void SpirvTest::entrypointInterface() { + Containers::Array data = Utility::Directory::read(Utility::Directory::join(SHADERTOOLS_TEST_DIR, "SpirvTestFiles/entrypoint-interface.spv")); + + /* The file is a full SPIR-V, strip the header first */ + Containers::ArrayView view = Implementation::spirvData(data, data.size()); + CORRADE_VERIFY(view); + + Containers::Optional vert = Implementation::spirvNextEntrypoint(view); + CORRADE_VERIFY(vert); + CORRADE_COMPARE(vert->interfaces.size(), 4); + + Implementation::SpirvEntrypointInterface vertInterface[4]{}; + Implementation::spirvEntrypointInterface(view, *vert, vertInterface); + CORRADE_VERIFY(vertInterface[0].location); /* position */ + CORRADE_VERIFY(vertInterface[0].storageClass); + CORRADE_COMPARE(*vertInterface[0].location, 0); + CORRADE_COMPARE(*vertInterface[0].storageClass, SpvStorageClassInput); + + CORRADE_VERIFY(vertInterface[1].location); /* color */ + CORRADE_VERIFY(vertInterface[1].storageClass); + CORRADE_COMPARE(*vertInterface[1].location, 1); + CORRADE_COMPARE(*vertInterface[1].storageClass, SpvStorageClassInput); + + /* Verify that absence of location is handled properly */ + CORRADE_VERIFY(!vertInterface[2].location); /* gl_Position */ + CORRADE_VERIFY(vertInterface[2].storageClass); + CORRADE_COMPARE(*vertInterface[2].storageClass, SpvStorageClassOutput); + + CORRADE_VERIFY(vertInterface[3].location); /* interpolatedColorOut */ + CORRADE_VERIFY(vertInterface[2].storageClass); + CORRADE_COMPARE(*vertInterface[3].location, 0); + CORRADE_COMPARE(*vertInterface[2].storageClass, SpvStorageClassOutput); + + Containers::Optional frag = Implementation::spirvNextEntrypoint(view); + CORRADE_VERIFY(frag); + CORRADE_COMPARE(frag->interfaces.size(), 3); + + Implementation::SpirvEntrypointInterface fragInterface[3]{}; + Implementation::spirvEntrypointInterface(view, *frag, fragInterface); + CORRADE_VERIFY(fragInterface[0].location); /* interpolatedColorIn */ + CORRADE_VERIFY(fragInterface[0].storageClass); + CORRADE_COMPARE(*fragInterface[0].location, 0); + CORRADE_COMPARE(*fragInterface[0].storageClass, SpvStorageClassInput); + + CORRADE_VERIFY(fragInterface[1].location); /* fragmentColor */ + CORRADE_VERIFY(fragInterface[1].storageClass); + CORRADE_COMPARE(*fragInterface[1].location, 0); + CORRADE_COMPARE(*fragInterface[1].storageClass, SpvStorageClassOutput); + + /* Verify that absence of storageClass is handled properly */ + CORRADE_VERIFY(fragInterface[2].location); /* unknownFragmentInterface */ + CORRADE_VERIFY(!fragInterface[2].storageClass); + CORRADE_COMPARE(*fragInterface[2].location, 1); +} + +void SpirvTest::entrypointInterfaceNothing() { + const UnsignedInt data[] { + op(3, SpvOpMemoryModel), SpvAddressingModelLogical, SpvMemoryModelGLSL450, + + op(4, SpvOpEntryPoint), SpvExecutionModelGLCompute, 1, '\0' + }; + Containers::ArrayView view = data; + + Containers::Optional comp = Implementation::spirvNextEntrypoint(view); + CORRADE_VERIFY(comp); + CORRADE_VERIFY(comp->interfaces.empty()); + + Implementation::spirvEntrypointInterface(view, *comp, {}); + + /* Well, it shouldn't crash */ + CORRADE_VERIFY(true); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::ShaderTools::Test::SpirvTest) diff --git a/src/Magnum/ShaderTools/Test/SpirvTestFiles/convert.sh b/src/Magnum/ShaderTools/Test/SpirvTestFiles/convert.sh new file mode 100755 index 000000000..0e65958ac --- /dev/null +++ b/src/Magnum/ShaderTools/Test/SpirvTestFiles/convert.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +for i in $(ls *.spvasm); do + magnum-shaderconverter $i ${i%asm} +done diff --git a/src/Magnum/ShaderTools/Test/SpirvTestFiles/entrypoint-interface.spv b/src/Magnum/ShaderTools/Test/SpirvTestFiles/entrypoint-interface.spv new file mode 100644 index 0000000000000000000000000000000000000000..1b90af3a0bc454fc0343c258454ff3f9a619c0a7 GIT binary patch literal 412 zcmYk2%L>9U6hyDBTHmP%x|52Z;ac2?|IkGh7q$|L=-)dhT1a4$oO_eWv}v->%z9>) zS+1F}v?NrVEdtYhRlcp>=cc|L>(+ms&Ao0f<)h+2BBfGa8c0rF?1$3Tmm;%~G?sFm zq)zRmQ=FSh3>dF%pdaH4lYu@yaUajPk8j*3Psb~{PsBTA;7#^x?PppGX(Ru+R+wCc lX8r(gQ$PHfjMO?NW6WfLx$m^n@9x8RQ{z5N#+dm=_zReq5=sC7 literal 0 HcmV?d00001 diff --git a/src/Magnum/ShaderTools/Test/SpirvTestFiles/entrypoint-interface.spvasm b/src/Magnum/ShaderTools/Test/SpirvTestFiles/entrypoint-interface.spvasm new file mode 100644 index 000000000..2bb920daf --- /dev/null +++ b/src/Magnum/ShaderTools/Test/SpirvTestFiles/entrypoint-interface.spvasm @@ -0,0 +1,23 @@ + OpCapability Shader + OpEntryPoint Vertex %ver "vertexLongEntrypointName" %position %color %gl_Position %interpolatedColorOut + OpEntryPoint Fragment %fra "fra" %interpolatedColorIn %fragmentColor %unknownFragmentInterface + OpExecutionMode %fra OriginUpperLeft + OpDecorate %gl_Position BuiltIn Position + OpDecorate %position Location 0 + OpDecorate %color Location 1 + OpDecorate %fragmentColor Location 0 + OpDecorate %unknownFragmentInterface Location 1 + OpDecorate %interpolatedColorIn Location 0 + OpDecorate %interpolatedColorOut Location 0 + %void = OpTypeVoid + %10 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %position = OpVariable %_ptr_Input_v4float Input + %color = OpVariable %_ptr_Input_v4float Input +%interpolatedColorIn = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_Position = OpVariable %_ptr_Output_v4float Output +%interpolatedColorOut = OpVariable %_ptr_Output_v4float Output +%fragmentColor = OpVariable %_ptr_Output_v4float Output From cd48d28e6a520cf41e81996ff04ec10c924fa620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 12 Feb 2021 12:17:18 +0100 Subject: [PATCH 23/44] AnyShaderConverter: fart! --- src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp index af4469284..a0cc2faf2 100644 --- a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp +++ b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp @@ -305,7 +305,7 @@ void AnyConverterTest::convert() { } void AnyConverterTest::convertNotSupported() { - CORRADE_SKIP("No plugin that would support just validation exists."); + CORRADE_SKIP("No plugin that would support just conversion exists."); } void AnyConverterTest::convertPreprocessNotSupported() { From 9d07f6637d3a1f9f68c5f5286752b194ecc68db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 12 Feb 2021 12:22:44 +0100 Subject: [PATCH 24/44] AnySceneConverter,AnyShaderConverter: whoops, these belong elsewhere. Interesting how nobody complained about this being broken. Probably because nobody is using those? Hah. --- src/MagnumPlugins/AnySceneConverter/CMakeLists.txt | 6 +++--- src/MagnumPlugins/AnyShaderConverter/CMakeLists.txt | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/MagnumPlugins/AnySceneConverter/CMakeLists.txt b/src/MagnumPlugins/AnySceneConverter/CMakeLists.txt index 5ca708490..a19e4a18c 100644 --- a/src/MagnumPlugins/AnySceneConverter/CMakeLists.txt +++ b/src/MagnumPlugins/AnySceneConverter/CMakeLists.txt @@ -46,9 +46,9 @@ target_link_libraries(AnySceneConverter PUBLIC MagnumTrade) # Modify output location only if all are set, otherwise it makes no sense if(CMAKE_RUNTIME_OUTPUT_DIRECTORY AND CMAKE_LIBRARY_OUTPUT_DIRECTORY AND CMAKE_ARCHIVE_OUTPUT_DIRECTORY) set_target_properties(AnySceneConverter PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/magnum$<$:-d>/imageconverters - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/magnum$<$:-d>/imageconverters - ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/magnum$<$:-d>/imageconverters) + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/magnum$<$:-d>/sceneconverters + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/magnum$<$:-d>/sceneconverters + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/magnum$<$:-d>/sceneconverters) endif() install(FILES AnySceneConverter.h ${CMAKE_CURRENT_BINARY_DIR}/configure.h diff --git a/src/MagnumPlugins/AnyShaderConverter/CMakeLists.txt b/src/MagnumPlugins/AnyShaderConverter/CMakeLists.txt index f4c8e65d8..566631400 100644 --- a/src/MagnumPlugins/AnyShaderConverter/CMakeLists.txt +++ b/src/MagnumPlugins/AnyShaderConverter/CMakeLists.txt @@ -46,9 +46,9 @@ target_link_libraries(AnyShaderConverter PUBLIC MagnumShaderTools) # Modify output location only if all are set, otherwise it makes no sense if(CMAKE_RUNTIME_OUTPUT_DIRECTORY AND CMAKE_LIBRARY_OUTPUT_DIRECTORY AND CMAKE_ARCHIVE_OUTPUT_DIRECTORY) set_target_properties(AnyShaderConverter PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/magnum$<$:-d>/imageconverters - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/magnum$<$:-d>/imageconverters - ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/magnum$<$:-d>/imageconverters) + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/magnum$<$:-d>/shaderconverters + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/magnum$<$:-d>/shaderconverters + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/magnum$<$:-d>/shaderconverters) endif() install(FILES AnyConverter.h ${CMAKE_CURRENT_BINARY_DIR}/configure.h From 7220274ff3164e57c012b15d6b0cc58393f371c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 12 Feb 2021 12:23:58 +0100 Subject: [PATCH 25/44] AnyShaderConverter: take explicitly set formats into account. So if we convert foo.blah but explicitly tell it that it's a SPIR-V file, it actually treats it as such, instead of complaining that `blah` is not a known extension. First step towards being able to operate directly on data. --- .../AnyShaderConverter/AnyConverter.cpp | 53 +++++++++++++++---- .../AnyShaderConverter/AnyConverter.h | 6 ++- .../Test/AnyConverterTest.cpp | 48 +++++++++++++++++ 3 files changed, 94 insertions(+), 13 deletions(-) diff --git a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp index fd0c08e43..959de54ce 100644 --- a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp +++ b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp @@ -101,7 +101,25 @@ namespace { using namespace Containers::Literals; -Containers::StringView formatForExtension(const char* prefix, const Containers::StringView filename) { +Containers::StringView stringForFormat(const Format format) { + switch(format) { + #define _c(format) case Format::format: return #format ## _s; + _c(Glsl) + _c(Spirv) + _c(SpirvAssembly) + _c(Hlsl) + _c(Msl) + _c(Wgsl) + _c(Dxil) + #undef _c + + case Format::Unspecified: return {}; + } + + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ +} + +Format formatForExtension(const char* prefix, const Containers::StringView filename) { /** @todo lowercase only the extension, once Directory::split() is done */ const std::string normalized = Utility::String::lowercase(filename); @@ -126,7 +144,7 @@ Containers::StringView formatForExtension(const char* prefix, const Containers:: Utility::String::endsWith(normalized, ".asm.rcall") || Utility::String::endsWith(normalized, ".asm.mesh") || Utility::String::endsWith(normalized, ".asm.task")) - return "SpirvAssembly"_s; + return Format::SpirvAssembly; /* https://github.com/KhronosGroup/glslang/blob/3ce148638bdc3807316e358dee4a5c9583189ae7/StandAlone/StandAlone.cpp#L260-L274 */ else if(Utility::String::endsWith(normalized, ".glsl") || Utility::String::endsWith(normalized, ".vert") || @@ -143,9 +161,9 @@ Containers::StringView formatForExtension(const char* prefix, const Containers:: Utility::String::endsWith(normalized, ".rcall") || Utility::String::endsWith(normalized, ".mesh") || Utility::String::endsWith(normalized, ".task")) - return "Glsl"_s; + return Format::Glsl; else if(Utility::String::endsWith(normalized, ".spv")) - return "Spirv"_s; + return Format::Spirv; Error{} << prefix << "cannot determine the format of" << filename; return {}; @@ -156,9 +174,15 @@ Containers::StringView formatForExtension(const char* prefix, const Containers:: std::pair AnyConverter::doValidateFile(const Stage stage, const Containers::StringView filename) { CORRADE_INTERNAL_ASSERT(manager()); - /* Decide on a plugin name based on the extension */ - const Containers::StringView format = formatForExtension("ShaderTools::AnyConverter::validateFile():", filename); + /* Prefer the explicitly set input format. If not set, fall back to + detecting based on extension. */ + const Containers::StringView format = stringForFormat( + _state->inputFormat != Format::Unspecified ? _state->inputFormat : + formatForExtension("ShaderTools::AnyConverter::validateFile():", filename) + ); if(format.isEmpty()) return {}; + + /* Decide on a plugin name based on the format */ const std::string plugin = Utility::formatString("{}ShaderConverter", format); /* Try to load the plugin */ @@ -207,12 +231,19 @@ std::pair AnyConverter::doValidateFile(const Stage sta bool AnyConverter::doConvertFileToFile(const Stage stage, const Containers::StringView from, const Containers::StringView to) { CORRADE_INTERNAL_ASSERT(manager()); - /* Decide on a plugin name based on the input and output extension. This - might result in invalid combinations such as SpirvToGlslShaderConverter - which can't be really handled yet but I think that's okay for now */ - const Containers::StringView formatFrom = formatForExtension("ShaderTools::AnyConverter::convertFileToFile():", from); - const Containers::StringView formatTo = formatForExtension("ShaderTools::AnyConverter::convertFileToFile():", to); + /* Prefer the explicitly set input format. If not set, fall back to + detecting based on input and output extension. */ + const Containers::StringView formatFrom = stringForFormat( + _state->inputFormat != Format::Unspecified ? _state->inputFormat : formatForExtension("ShaderTools::AnyConverter::convertFileToFile():", from) + ); + const Containers::StringView formatTo = stringForFormat( + _state->outputFormat != Format::Unspecified ? _state->outputFormat : formatForExtension("ShaderTools::AnyConverter::convertFileToFile():", to) + ); if(formatFrom.isEmpty() || formatTo.isEmpty()) return {}; + + /* Decide on a plugin name based on the format. This might result in + invalid combinations such as SpirvToGlslShaderConverter which can't be + really handled yet but I think that's okay for now. */ const std::string plugin = Utility::formatString( formatFrom == formatTo ? "{}ShaderConverter" : "{}To{}ShaderConverter", formatFrom, formatTo); diff --git a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.h b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.h index 46521b32f..950e54b92 100644 --- a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.h +++ b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.h @@ -57,8 +57,10 @@ namespace Magnum { namespace ShaderTools { @m_keywords{AnyShaderConverter} -Detects file type based on file extension, loads corresponding plugin and then -tries to either validate or convert the file with it. Detected file formats: +Loads a plugin corresponding to a format either explicitly set using +@ref setInputFormat() / @ref setOutputFormat() or detected based on input / +output file extension plugin and then tries to either validate or convert the +file with it. These formats are detected based on extension: - GLSL (`*.glsl`, `*.vert`, `*.frag`, `*.geom`, `*.comp`, `*.tesc`, `*.tese`, `*.rgen`, `*.rint`, `*.rahit`, `*.rchit`, `*.rmiss`, `*.rcall`, `*.mesh`, diff --git a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp index a0cc2faf2..3bea57e53 100644 --- a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp +++ b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp @@ -64,7 +64,9 @@ struct AnyConverterTest: TestSuite::Tester { void convertPropagateOptimization(); void detectValidate(); + void detectValidateExplicitFormat(); void detectConvert(); + void detectConvertExplicitFormat(); void unknown(); @@ -120,9 +122,13 @@ AnyConverterTest::AnyConverterTest() { addInstancedTests({&AnyConverterTest::detectValidate}, Containers::arraySize(DetectValidateData)); + addTests({&AnyConverterTest::detectValidateExplicitFormat}); + addInstancedTests({&AnyConverterTest::detectConvert}, Containers::arraySize(DetectConvertData)); + addTests({&AnyConverterTest::detectConvertExplicitFormat}); + addTests({&AnyConverterTest::unknown}); /* Load the plugin directly from the build tree. Otherwise it's static and @@ -567,6 +573,27 @@ void AnyConverterTest::detectValidate() { #endif } +void AnyConverterTest::detectValidateExplicitFormat() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + /* It should pick up this format and not bother with the extension */ + converter->setInputFormat(Format::Hlsl); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_COMPARE(converter->validateFile({}, "file.spv"), + std::make_pair(false, "")); + #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin HlslShaderConverter is not static and was not found in nonexistent\n" + "ShaderTools::AnyConverter::validateFile(): cannot load the HlslShaderConverter plugin\n"); + #else + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin HlslShaderConverter was not found\n" + "ShaderTools::AnyConverter::validateFile(): cannot load the HlslShaderConverter plugin\n"); + #endif +} + void AnyConverterTest::detectConvert() { auto&& data = DetectConvertData[testCaseInstanceId()]; setTestCaseDescription(data.name); @@ -587,6 +614,27 @@ void AnyConverterTest::detectConvert() { #endif } +void AnyConverterTest::detectConvertExplicitFormat() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + /* It should pick up this format and not bother with the extension */ + converter->setInputFormat(Format::Hlsl); + converter->setOutputFormat(Format::Wgsl); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToFile({}, "file.spv", Utility::Directory::join(ANYSHADERCONVERTER_TEST_OUTPUT_DIR, "file.glsl"))); + #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin HlslToWgslShaderConverter is not static and was not found in nonexistent\n" + "ShaderTools::AnyConverter::convertFileToFile(): cannot load the HlslToWgslShaderConverter plugin\n"); + #else + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin HlslToWgslShaderConverter was not found\n" + "ShaderTools::AnyConverter::convertFileToFile(): cannot load the HlslToWgslShaderConverter plugin\n"); + #endif +} + void AnyConverterTest::unknown() { std::ostringstream output; Error redirectError{&output}; From bbb6fd69c07bbf7faed1b2d61df0a50c01b1d2ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 12 Feb 2021 12:47:40 +0100 Subject: [PATCH 26/44] AnyShaderConverter: test also unknown extension for conversion. And make it return right after the first error. Also clean up some old-style code -- the redirection should be restricted to the smallest scope possible. --- .../AnyShaderConverter/AnyConverter.cpp | 3 +- .../Test/AnyConverterTest.cpp | 48 +++++++++++++------ 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp index 959de54ce..226612626 100644 --- a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp +++ b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp @@ -236,10 +236,11 @@ bool AnyConverter::doConvertFileToFile(const Stage stage, const Containers::Stri const Containers::StringView formatFrom = stringForFormat( _state->inputFormat != Format::Unspecified ? _state->inputFormat : formatForExtension("ShaderTools::AnyConverter::convertFileToFile():", from) ); + if(formatFrom.isEmpty()) return {}; const Containers::StringView formatTo = stringForFormat( _state->outputFormat != Format::Unspecified ? _state->outputFormat : formatForExtension("ShaderTools::AnyConverter::convertFileToFile():", to) ); - if(formatFrom.isEmpty() || formatTo.isEmpty()) return {}; + if(formatTo.isEmpty()) return {}; /* Decide on a plugin name based on the format. This might result in invalid combinations such as SpirvToGlslShaderConverter which can't be diff --git a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp index 3bea57e53..0bc8e2975 100644 --- a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp +++ b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp @@ -44,6 +44,7 @@ struct AnyConverterTest: TestSuite::Tester { explicit AnyConverterTest(); void validate(); + void validateUnknown(); void validateNotSupported(); void validatePreprocessNotSupported(); void validatePropagateFlags(); @@ -52,6 +53,8 @@ struct AnyConverterTest: TestSuite::Tester { void validatePropagatePreprocess(); void convert(); + void convertUnknownInput(); + void convertUnknownOutput(); void convertNotSupported(); void convertPreprocessNotSupported(); void convertDebugInfoNotSupported(); @@ -68,8 +71,6 @@ struct AnyConverterTest: TestSuite::Tester { void detectConvert(); void detectConvertExplicitFormat(); - void unknown(); - /* Explicitly forbid system-wide plugin dependencies. Tests that need those have their own manager. */ PluginManager::Manager _manager{"nonexistent"}; @@ -100,6 +101,7 @@ constexpr struct { AnyConverterTest::AnyConverterTest() { addTests({&AnyConverterTest::validate, + &AnyConverterTest::validateUnknown, &AnyConverterTest::validateNotSupported, &AnyConverterTest::validatePreprocessNotSupported, &AnyConverterTest::validatePropagateFlags, @@ -108,6 +110,8 @@ AnyConverterTest::AnyConverterTest() { &AnyConverterTest::validatePropagatePreprocess, &AnyConverterTest::convert, + &AnyConverterTest::convertUnknownInput, + &AnyConverterTest::convertUnknownOutput, &AnyConverterTest::convertNotSupported, &AnyConverterTest::convertPreprocessNotSupported, &AnyConverterTest::convertDebugInfoNotSupported, @@ -129,8 +133,6 @@ AnyConverterTest::AnyConverterTest() { addTests({&AnyConverterTest::detectConvertExplicitFormat}); - addTests({&AnyConverterTest::unknown}); - /* Load the plugin directly from the build tree. Otherwise it's static and already loaded. */ #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME @@ -159,6 +161,16 @@ void AnyConverterTest::validate() { std::make_pair(true, Utility::formatString("WARNING: {}:10: 'reserved__identifier' : identifiers containing consecutive underscores (\"__\") are reserved", filename))); } +void AnyConverterTest::validateUnknown() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_COMPARE(converter->validateFile({}, "dead.cg"), + std::make_pair(false, "")); + CORRADE_COMPARE(out.str(), "ShaderTools::AnyConverter::validateFile(): cannot determine the format of dead.cg\n"); +} + void AnyConverterTest::validateNotSupported() { CORRADE_SKIP("No plugin that would support just validation exists."); } @@ -310,6 +322,24 @@ void AnyConverterTest::convert() { "WARNING: {}:10: 'reserved__identifier' : identifiers containing consecutive underscores (\"__\") are reserved\n", inputFilename)); } +void AnyConverterTest::convertUnknownInput() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToFile({}, "dead.cg", "whatever.osl")); + CORRADE_COMPARE(out.str(), "ShaderTools::AnyConverter::convertFileToFile(): cannot determine the format of dead.cg\n"); +} + +void AnyConverterTest::convertUnknownOutput() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToFile({}, "file.spv", "whatever.osl")); + CORRADE_COMPARE(out.str(), "ShaderTools::AnyConverter::convertFileToFile(): cannot determine the format of whatever.osl\n"); +} + void AnyConverterTest::convertNotSupported() { CORRADE_SKIP("No plugin that would support just conversion exists."); } @@ -635,16 +665,6 @@ void AnyConverterTest::detectConvertExplicitFormat() { #endif } -void AnyConverterTest::unknown() { - std::ostringstream output; - Error redirectError{&output}; - - Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); - CORRADE_COMPARE(converter->validateFile({}, "dead.cg"), - std::make_pair(false, "")); - CORRADE_COMPARE(output.str(), "ShaderTools::AnyConverter::validateFile(): cannot determine the format of dead.cg\n"); -} - }}}} CORRADE_TEST_MAIN(Magnum::ShaderTools::Test::AnyConverterTest) From 31f2e18b586afff952c7e4932cff3130da8dace9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 12 Feb 2021 13:14:54 +0100 Subject: [PATCH 27/44] AnyShaderConverter: minor redundancy cleanup in the test. --- src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp index 0bc8e2975..e90f2ed3b 100644 --- a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp +++ b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp @@ -445,7 +445,7 @@ void AnyConverterTest::convertPropagateFlags() { std::ostringstream out; Debug redirectDebug{&out}; Error redirectError{&out}; - CORRADE_VERIFY(!converter->convertFileToFile(Stage::Fragment, Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl"), Utility::Directory::join(ANYSHADERCONVERTER_TEST_OUTPUT_DIR, "file.spv"))); + CORRADE_VERIFY(!converter->convertFileToFile(Stage::Fragment, filename, Utility::Directory::join(ANYSHADERCONVERTER_TEST_OUTPUT_DIR, "file.spv"))); CORRADE_COMPARE(out.str(), Utility::formatString( "ShaderTools::AnyConverter::convertFileToFile(): using GlslToSpirvShaderConverter (provided by GlslangShaderConverter)\n" "ShaderTools::GlslangConverter::convertDataToData(): compilation failed:\n" From 20dd2914c512d74eb83626e7de138d6449a612fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 12 Feb 2021 13:52:53 +0100 Subject: [PATCH 28/44] AnyShaderConverter: support validating and converting data as well. Because it's possible to specify the formats explicitly it doesn't make sense not to support that. Eventually contents-based detection could be implemented as well, but I don't want to do that just yet. --- .../AnyShaderConverter/AnyConverter.cpp | 215 +++- .../AnyShaderConverter/AnyConverter.h | 7 +- .../Test/AnyConverterTest.cpp | 1059 ++++++++++++++++- 3 files changed, 1213 insertions(+), 68 deletions(-) diff --git a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp index 226612626..ca7788d6e 100644 --- a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp +++ b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp @@ -54,7 +54,7 @@ AnyConverter::AnyConverter(PluginManager::AbstractManager& manager, const std::s AnyConverter::~AnyConverter() = default; ConverterFeatures AnyConverter::doFeatures() const { - return ConverterFeature::ValidateFile|ConverterFeature::ConvertFile|ConverterFeature::Preprocess|ConverterFeature::DebugInfo|ConverterFeature::Optimize; + return ConverterFeature::ValidateFile|ConverterFeature::ValidateData|ConverterFeature::ConvertFile|ConverterFeature::ConvertData|ConverterFeature::Preprocess|ConverterFeature::DebugInfo|ConverterFeature::Optimize; } void AnyConverter::doSetInputFormat(const Format format, const Containers::StringView version) { @@ -228,6 +228,59 @@ std::pair AnyConverter::doValidateFile(const Stage sta return converter->validateFile(stage, filename); } +std::pair AnyConverter::doValidateData(const Stage stage, const Containers::ArrayView data) { + CORRADE_INTERNAL_ASSERT(manager()); + + /* Decide on a plugin name based on the format */ + if(_state->inputFormat == Format::Unspecified) { + Error{} << "ShaderTools::AnyConverter::validateData(): no input format specified"; + return {}; + } + const std::string plugin = Utility::formatString("{}ShaderConverter", stringForFormat(_state->inputFormat)); + + /* Try to load the plugin */ + if(!(manager()->load(plugin) & PluginManager::LoadState::Loaded)) { + Error{} << "ShaderTools::AnyConverter::validateData(): cannot load the" << plugin << "plugin"; + return {}; + } + PluginManager::PluginMetadata* metadata = manager()->metadata(plugin); + if(flags() & ConverterFlag::Verbose) { + Debug d; + d << "ShaderTools::AnyConverter::validateData(): using" << plugin; + CORRADE_INTERNAL_ASSERT(metadata); + if(plugin != metadata->name()) + d << "(provided by" << metadata->name() << Debug::nospace << ")"; + } + + /* Instantiate the plugin */ + Containers::Pointer converter = static_cast*>(manager())->instantiate(plugin); + + /* Check that it can actually validate */ + if(!(converter->features() & ConverterFeature::ValidateData)) { + Error{} << "ShaderTools::AnyConverter::validateData():" << metadata->name() << "does not support validation"; + return {}; + } + + /* Check that it can preprocess, in case we were asked to preprocess */ + if((!_state->definitionViews.empty() || (flags() & ConverterFlag::PreprocessOnly)) && !(converter->features() & ConverterFeature::Preprocess)) { + Error{} << "ShaderTools::AnyConverter::validateData():" << metadata->name() << "does not support preprocessing"; + return {}; + } + + /* Propagate input/output version and flags */ + converter->setFlags(flags()); + converter->setInputFormat(_state->inputFormat, _state->inputVersion); + converter->setOutputFormat(_state->outputFormat, _state->outputVersion); + + /* Propagate definitions, if any */ + if(!_state->definitionViews.empty()) + converter->setDefinitions(_state->definitionViews); + + /* Try to validate the data (error output should be printed by the plugin + itself) */ + return converter->validateData(stage, data); +} + bool AnyConverter::doConvertFileToFile(const Stage stage, const Containers::StringView from, const Containers::StringView to) { CORRADE_INTERNAL_ASSERT(manager()); @@ -308,6 +361,166 @@ bool AnyConverter::doConvertFileToFile(const Stage stage, const Containers::Stri return converter->convertFileToFile(stage, from, to); } +Containers::Array AnyConverter::doConvertFileToData(const Stage stage, const Containers::StringView from) { + CORRADE_INTERNAL_ASSERT(manager()); + + /* Prefer the explicitly set input format. If not set, fall back to + detecting based on input and output extension. */ + const Containers::StringView formatFrom = stringForFormat( + _state->inputFormat != Format::Unspecified ? _state->inputFormat : formatForExtension("ShaderTools::AnyConverter::convertFileToData():", from) + ); + if(formatFrom.isEmpty()) return {}; + if(_state->outputFormat == Format::Unspecified) { + Error{} << "ShaderTools::AnyConverter::convertFileToData(): no output format specified"; + return {}; + } + const Containers::StringView formatTo = stringForFormat(_state->outputFormat); + + /* Decide on a plugin name based on the format. This might result in + invalid combinations such as SpirvToGlslShaderConverter which can't be + really handled yet but I think that's okay for now. */ + const std::string plugin = Utility::formatString( + formatFrom == formatTo ? "{}ShaderConverter" : "{}To{}ShaderConverter", + formatFrom, formatTo); + + /* Try to load the plugin */ + if(!(manager()->load(plugin) & PluginManager::LoadState::Loaded)) { + Error{} << "ShaderTools::AnyConverter::convertFileToData(): cannot load the" << plugin << "plugin"; + return {}; + } + PluginManager::PluginMetadata* metadata = manager()->metadata(plugin); + if(flags() & ConverterFlag::Verbose) { + Debug d; + d << "ShaderTools::AnyConverter::convertFileToData(): using" << plugin; + CORRADE_INTERNAL_ASSERT(metadata); + if(plugin != metadata->name()) + d << "(provided by" << metadata->name() << Debug::nospace << ")"; + } + + /* Instantiate the plugin */ + Containers::Pointer converter = static_cast*>(manager())->instantiate(plugin); + + /* Check that it can actually convert */ + if(!(converter->features() & ConverterFeature::ConvertData)) { + Error{} << "ShaderTools::AnyConverter::convertFileToData():" << metadata->name() << "does not support conversion"; + return {}; + } + + /* Check that it can preprocess, in case we were asked to preprocess */ + if((!_state->definitionViews.empty() || (flags() & ConverterFlag::PreprocessOnly)) && !(converter->features() & ConverterFeature::Preprocess)) { + Error{} << "ShaderTools::AnyConverter::convertFileToData():" << metadata->name() << "does not support preprocessing"; + return {}; + } + + /* Check that it can output debug info, in case we were asked to */ + if(!_state->debugInfoLevel.isEmpty() && !(converter->features() & ConverterFeature::DebugInfo)) { + Error{} << "ShaderTools::AnyConverter::convertFileToData():" << metadata->name() << "does not support controlling debug info output"; + return {}; + } + + /* Check that it can optimize, in case we were asked to */ + if(!_state->optimizationLevel.isEmpty() && !(converter->features() & ConverterFeature::Optimize)) { + Error{} << "ShaderTools::AnyConverter::convertFileToData():" << metadata->name() << "does not support optimization"; + return {}; + } + + /* Propagate input/output version and flags */ + converter->setFlags(flags()); + converter->setInputFormat(_state->inputFormat, _state->inputVersion); + converter->setOutputFormat(_state->outputFormat, _state->outputVersion); + + /* Propagate definitions and debug info, if any */ + if(!_state->definitionViews.empty()) + converter->setDefinitions(_state->definitionViews); + if(!_state->debugInfoLevel.isEmpty()) + converter->setDebugInfoLevel(_state->debugInfoLevel); + if(!_state->optimizationLevel.isEmpty()) + converter->setOptimizationLevel(_state->optimizationLevel); + + /* Try to convert the file (error output should be printed by the plugin + itself) */ + return converter->convertFileToData(stage, from); +} + +Containers::Array AnyConverter::doConvertDataToData(const Stage stage, const Containers::ArrayView from) { + CORRADE_INTERNAL_ASSERT(manager()); + + /* Decide on a plugin name based on the format. This might result in + invalid combinations such as SpirvToGlslShaderConverter which can't be + really handled yet but I think that's okay for now. */ + if(_state->inputFormat == Format::Unspecified) { + Error{} << "ShaderTools::AnyConverter::convertDataToData(): no input format specified"; + return {}; + } + if(_state->outputFormat == Format::Unspecified) { + Error{} << "ShaderTools::AnyConverter::convertDataToData(): no output format specified"; + return {}; + } + const Containers::StringView formatFrom = stringForFormat(_state->inputFormat); + const Containers::StringView formatTo = stringForFormat(_state->outputFormat); + const std::string plugin = Utility::formatString( + formatFrom == formatTo ? "{}ShaderConverter" : "{}To{}ShaderConverter", + formatFrom, formatTo); + + /* Try to load the plugin */ + if(!(manager()->load(plugin) & PluginManager::LoadState::Loaded)) { + Error{} << "ShaderTools::AnyConverter::convertDataToData(): cannot load the" << plugin << "plugin"; + return {}; + } + PluginManager::PluginMetadata* metadata = manager()->metadata(plugin); + if(flags() & ConverterFlag::Verbose) { + Debug d; + d << "ShaderTools::AnyConverter::convertDataToData(): using" << plugin; + CORRADE_INTERNAL_ASSERT(metadata); + if(plugin != metadata->name()) + d << "(provided by" << metadata->name() << Debug::nospace << ")"; + } + + /* Instantiate the plugin */ + Containers::Pointer converter = static_cast*>(manager())->instantiate(plugin); + + /* Check that it can actually convert */ + if(!(converter->features() & ConverterFeature::ConvertData)) { + Error{} << "ShaderTools::AnyConverter::convertDataToData():" << metadata->name() << "does not support conversion"; + return {}; + } + + /* Check that it can preprocess, in case we were asked to preprocess */ + if((!_state->definitionViews.empty() || (flags() & ConverterFlag::PreprocessOnly)) && !(converter->features() & ConverterFeature::Preprocess)) { + Error{} << "ShaderTools::AnyConverter::convertDataToData():" << metadata->name() << "does not support preprocessing"; + return {}; + } + + /* Check that it can output debug info, in case we were asked to */ + if(!_state->debugInfoLevel.isEmpty() && !(converter->features() & ConverterFeature::DebugInfo)) { + Error{} << "ShaderTools::AnyConverter::convertDataToData():" << metadata->name() << "does not support controlling debug info output"; + return {}; + } + + /* Check that it can optimize, in case we were asked to */ + if(!_state->optimizationLevel.isEmpty() && !(converter->features() & ConverterFeature::Optimize)) { + Error{} << "ShaderTools::AnyConverter::convertDataToData():" << metadata->name() << "does not support optimization"; + return {}; + } + + /* Propagate input/output version and flags */ + converter->setFlags(flags()); + converter->setInputFormat(_state->inputFormat, _state->inputVersion); + converter->setOutputFormat(_state->outputFormat, _state->outputVersion); + + /* Propagate definitions and debug info, if any */ + if(!_state->definitionViews.empty()) + converter->setDefinitions(_state->definitionViews); + if(!_state->debugInfoLevel.isEmpty()) + converter->setDebugInfoLevel(_state->debugInfoLevel); + if(!_state->optimizationLevel.isEmpty()) + converter->setOptimizationLevel(_state->optimizationLevel); + + /* Try to convert the file (error output should be printed by the plugin + itself) */ + return converter->convertDataToData(stage, from); +} + }} CORRADE_PLUGIN_REGISTER(AnyShaderConverter, Magnum::ShaderTools::AnyConverter, diff --git a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.h b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.h index 950e54b92..859f20445 100644 --- a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.h +++ b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.h @@ -88,7 +88,9 @@ Supported conversion paths: - SPIR-V Assembly to SPIR-V Assembly, converted with any plugin that provides `SpirvAssemblyShaderConverter` -Only validating and converting files is supported. +There's format detection based on file contents, so the plugin has to either +operate on files or @ref setInputFormat() / @ref setOutputFormat() has to be +explicitly set. @section ShaderTools-AnyConverter-usage Usage @@ -140,7 +142,10 @@ class MAGNUM_ANYSHADERCONVERTER_EXPORT AnyConverter: public AbstractConverter { MAGNUM_ANYSHADERCONVERTER_LOCAL void doSetOptimizationLevel(Containers::StringView level) override; MAGNUM_ANYSHADERCONVERTER_LOCAL std::pair doValidateFile(Stage stage, Containers::StringView filename) override; + MAGNUM_ANYSHADERCONVERTER_LOCAL std::pair doValidateData(Stage stage, Containers::ArrayView data) override; MAGNUM_ANYSHADERCONVERTER_LOCAL bool doConvertFileToFile(Stage stage, Containers::StringView from, Containers::StringView to) override; + MAGNUM_ANYSHADERCONVERTER_LOCAL Containers::Array doConvertFileToData(Magnum::ShaderTools::Stage stage, Containers::StringView from) override; + MAGNUM_ANYSHADERCONVERTER_LOCAL Containers::Array doConvertDataToData(Magnum::ShaderTools::Stage stage, Containers::ArrayView data) override; struct State; Containers::Pointer _state; diff --git a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp index e90f2ed3b..498ea94cc 100644 --- a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp +++ b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp @@ -24,7 +24,7 @@ */ #include -#include +#include #include #include #include @@ -43,28 +43,70 @@ namespace Magnum { namespace ShaderTools { namespace Test { namespace { struct AnyConverterTest: TestSuite::Tester { explicit AnyConverterTest(); - void validate(); - void validateUnknown(); - void validateNotSupported(); - void validatePreprocessNotSupported(); - void validatePropagateFlags(); - void validatePropagateInputVersion(); - void validatePropagateOutputVersion(); - void validatePropagatePreprocess(); - - void convert(); - void convertUnknownInput(); - void convertUnknownOutput(); - void convertNotSupported(); - void convertPreprocessNotSupported(); - void convertDebugInfoNotSupported(); - void convertOptimizationNotSupported(); - void convertPropagateFlags(); - void convertPropagateInputVersion(); - void convertPropagateOutputVersion(); - void convertPropagatePreprocess(); - void convertPropagateDebugInfo(); - void convertPropagateOptimization(); + void validateFile(); + void validateFilePluginLoadFailed(); + void validateFileUnknown(); + void validateFileNotSupported(); + void validateFilePreprocessNotSupported(); + void validateFilePropagateFlags(); + void validateFilePropagateInputVersion(); + void validateFilePropagateOutputVersion(); + void validateFilePropagatePreprocess(); + + void validateData(); + void validateDataPluginLoadFailed(); + void validateDataNoFormatSet(); + void validateDataNotSupported(); + void validateDataPreprocessNotSupported(); + void validateDataPropagateFlags(); + void validateDataPropagateInputVersion(); + void validateDataPropagateOutputVersion(); + void validateDataPropagatePreprocess(); + + void convertFileToFile(); + void convertFileToFilePluginLoadFailed(); + void convertFileToFileUnknownInput(); + void convertFileToFileUnknownOutput(); + void convertFileToFileNotSupported(); + void convertFileToFilePreprocessNotSupported(); + void convertFileToFileDebugInfoNotSupported(); + void convertFileToFileOptimizationNotSupported(); + void convertFileToFilePropagateFlags(); + void convertFileToFilePropagateInputVersion(); + void convertFileToFilePropagateOutputVersion(); + void convertFileToFilePropagatePreprocess(); + void convertFileToFilePropagateDebugInfo(); + void convertFileToFilePropagateOptimization(); + + void convertFileToData(); + void convertFileToDataPluginLoadFailed(); + void convertFileToDataUnknown(); + void convertFileToDataNoFormatSet(); + void convertFileToDataNotSupported(); + void convertFileToDataPreprocessNotSupported(); + void convertFileToDataDebugInfoNotSupported(); + void convertFileToDataOptimizationNotSupported(); + void convertFileToDataPropagateFlags(); + void convertFileToDataPropagateInputVersion(); + void convertFileToDataPropagateOutputVersion(); + void convertFileToDataPropagatePreprocess(); + void convertFileToDataPropagateDebugInfo(); + void convertFileToDataPropagateOptimization(); + + void convertDataToData(); + void convertDataToDataPluginLoadFailed(); + void convertDataToDataNoInputFormatSet(); + void convertDataToDataNoOutputFormatSet(); + void convertDataToDataNotSupported(); + void convertDataToDataPreprocessNotSupported(); + void convertDataToDataDebugInfoNotSupported(); + void convertDataToDataOptimizationNotSupported(); + void convertDataToDataPropagateFlags(); + void convertDataToDataPropagateInputVersion(); + void convertDataToDataPropagateOutputVersion(); + void convertDataToDataPropagatePreprocess(); + void convertDataToDataPropagateDebugInfo(); + void convertDataToDataPropagateOptimization(); void detectValidate(); void detectValidateExplicitFormat(); @@ -100,28 +142,70 @@ constexpr struct { }; AnyConverterTest::AnyConverterTest() { - addTests({&AnyConverterTest::validate, - &AnyConverterTest::validateUnknown, - &AnyConverterTest::validateNotSupported, - &AnyConverterTest::validatePreprocessNotSupported, - &AnyConverterTest::validatePropagateFlags, - &AnyConverterTest::validatePropagateInputVersion, - &AnyConverterTest::validatePropagateOutputVersion, - &AnyConverterTest::validatePropagatePreprocess, - - &AnyConverterTest::convert, - &AnyConverterTest::convertUnknownInput, - &AnyConverterTest::convertUnknownOutput, - &AnyConverterTest::convertNotSupported, - &AnyConverterTest::convertPreprocessNotSupported, - &AnyConverterTest::convertDebugInfoNotSupported, - &AnyConverterTest::convertOptimizationNotSupported, - &AnyConverterTest::convertPropagateFlags, - &AnyConverterTest::convertPropagateInputVersion, - &AnyConverterTest::convertPropagateOutputVersion, - &AnyConverterTest::convertPropagatePreprocess, - &AnyConverterTest::convertPropagateDebugInfo, - &AnyConverterTest::convertPropagateOptimization}); + addTests({&AnyConverterTest::validateFile, + &AnyConverterTest::validateFilePluginLoadFailed, + &AnyConverterTest::validateFileUnknown, + &AnyConverterTest::validateFileNotSupported, + &AnyConverterTest::validateFilePreprocessNotSupported, + &AnyConverterTest::validateFilePropagateFlags, + &AnyConverterTest::validateFilePropagateInputVersion, + &AnyConverterTest::validateFilePropagateOutputVersion, + &AnyConverterTest::validateFilePropagatePreprocess, + + &AnyConverterTest::validateData, + &AnyConverterTest::validateDataPluginLoadFailed, + &AnyConverterTest::validateDataNoFormatSet, + &AnyConverterTest::validateDataNotSupported, + &AnyConverterTest::validateDataPreprocessNotSupported, + &AnyConverterTest::validateDataPropagateFlags, + &AnyConverterTest::validateDataPropagateInputVersion, + &AnyConverterTest::validateDataPropagateOutputVersion, + &AnyConverterTest::validateDataPropagatePreprocess, + + &AnyConverterTest::convertFileToFile, + &AnyConverterTest::convertFileToFilePluginLoadFailed, + &AnyConverterTest::convertFileToFileUnknownInput, + &AnyConverterTest::convertFileToFileUnknownOutput, + &AnyConverterTest::convertFileToFileNotSupported, + &AnyConverterTest::convertFileToFilePreprocessNotSupported, + &AnyConverterTest::convertFileToFileDebugInfoNotSupported, + &AnyConverterTest::convertFileToFileOptimizationNotSupported, + &AnyConverterTest::convertFileToFilePropagateFlags, + &AnyConverterTest::convertFileToFilePropagateInputVersion, + &AnyConverterTest::convertFileToFilePropagateOutputVersion, + &AnyConverterTest::convertFileToFilePropagatePreprocess, + &AnyConverterTest::convertFileToFilePropagateDebugInfo, + &AnyConverterTest::convertFileToFilePropagateOptimization, + + &AnyConverterTest::convertFileToData, + &AnyConverterTest::convertFileToDataPluginLoadFailed, + &AnyConverterTest::convertFileToDataUnknown, + &AnyConverterTest::convertFileToDataNoFormatSet, + &AnyConverterTest::convertFileToDataNotSupported, + &AnyConverterTest::convertFileToDataPreprocessNotSupported, + &AnyConverterTest::convertFileToDataDebugInfoNotSupported, + &AnyConverterTest::convertFileToDataOptimizationNotSupported, + &AnyConverterTest::convertFileToDataPropagateFlags, + &AnyConverterTest::convertFileToDataPropagateInputVersion, + &AnyConverterTest::convertFileToDataPropagateOutputVersion, + &AnyConverterTest::convertFileToDataPropagatePreprocess, + &AnyConverterTest::convertFileToDataPropagateDebugInfo, + &AnyConverterTest::convertFileToDataPropagateOptimization, + + &AnyConverterTest::convertDataToData, + &AnyConverterTest::convertDataToDataPluginLoadFailed, + &AnyConverterTest::convertDataToDataNoInputFormatSet, + &AnyConverterTest::convertDataToDataNoOutputFormatSet, + &AnyConverterTest::convertDataToDataNotSupported, + &AnyConverterTest::convertDataToDataPreprocessNotSupported, + &AnyConverterTest::convertDataToDataDebugInfoNotSupported, + &AnyConverterTest::convertDataToDataOptimizationNotSupported, + &AnyConverterTest::convertDataToDataPropagateFlags, + &AnyConverterTest::convertDataToDataPropagateInputVersion, + &AnyConverterTest::convertDataToDataPropagateOutputVersion, + &AnyConverterTest::convertDataToDataPropagatePreprocess, + &AnyConverterTest::convertDataToDataPropagateDebugInfo, + &AnyConverterTest::convertDataToDataPropagateOptimization}); addInstancedTests({&AnyConverterTest::detectValidate}, Containers::arraySize(DetectValidateData)); @@ -143,7 +227,7 @@ AnyConverterTest::AnyConverterTest() { CORRADE_INTERNAL_ASSERT_OUTPUT(Utility::Directory::mkpath(ANYSHADERCONVERTER_TEST_OUTPUT_DIR)); } -void AnyConverterTest::validate() { +void AnyConverterTest::validateFile() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -161,7 +245,25 @@ void AnyConverterTest::validate() { std::make_pair(true, Utility::formatString("WARNING: {}:10: 'reserved__identifier' : identifiers containing consecutive underscores (\"__\") are reserved", filename))); } -void AnyConverterTest::validateUnknown() { +void AnyConverterTest::validateFilePluginLoadFailed() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_COMPARE(converter->validateFile({}, "file.glsl"), + std::make_pair(false, "")); + #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin GlslShaderConverter is not static and was not found in nonexistent\n" + "ShaderTools::AnyConverter::validateFile(): cannot load the GlslShaderConverter plugin\n"); + #else + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin GlslShaderConverter was not found\n" + "ShaderTools::AnyConverter::validateFile(): cannot load the GlslShaderConverter plugin\n"); + #endif +} + +void AnyConverterTest::validateFileUnknown() { Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); std::ostringstream out; @@ -171,11 +273,11 @@ void AnyConverterTest::validateUnknown() { CORRADE_COMPARE(out.str(), "ShaderTools::AnyConverter::validateFile(): cannot determine the format of dead.cg\n"); } -void AnyConverterTest::validateNotSupported() { +void AnyConverterTest::validateFileNotSupported() { CORRADE_SKIP("No plugin that would support just validation exists."); } -void AnyConverterTest::validatePreprocessNotSupported() { +void AnyConverterTest::validateFilePreprocessNotSupported() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -198,7 +300,7 @@ void AnyConverterTest::validatePreprocessNotSupported() { "ShaderTools::AnyConverter::validateFile(): SpirvToolsShaderConverter does not support preprocessing\n"); } -void AnyConverterTest::validatePropagateFlags() { +void AnyConverterTest::validateFilePropagateFlags() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -223,7 +325,7 @@ void AnyConverterTest::validatePropagateFlags() { "ShaderTools::AnyConverter::validateFile(): using GlslShaderConverter (provided by GlslangShaderConverter)\n"); } -void AnyConverterTest::validatePropagateInputVersion() { +void AnyConverterTest::validateFilePropagateInputVersion() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -247,7 +349,7 @@ void AnyConverterTest::validatePropagateInputVersion() { "ShaderTools::GlslangConverter::validateData(): input format version should be one of supported GLSL #version strings but got 100\n"); } -void AnyConverterTest::validatePropagateOutputVersion() { +void AnyConverterTest::validateFilePropagateOutputVersion() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -271,7 +373,7 @@ void AnyConverterTest::validatePropagateOutputVersion() { "ShaderTools::GlslangConverter::validateData(): output format should be Spirv or Unspecified but got ShaderTools::Format::Glsl\n"); } -void AnyConverterTest::validatePropagatePreprocess() { +void AnyConverterTest::validateFilePropagatePreprocess() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -296,7 +398,186 @@ void AnyConverterTest::validatePropagatePreprocess() { std::make_pair(true, Utility::formatString("WARNING: {}:10: 'different__but_also_wrong' : identifiers containing consecutive underscores (\"__\") are reserved", filename))); } -void AnyConverterTest::convert() { +void AnyConverterTest::validateData() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Glsl); + + /* Make it print a warning so we know it's doing something */ + CORRADE_COMPARE(converter->validateData(Stage::Fragment, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl"))), + std::make_pair(true, "WARNING: 0:10: 'reserved__identifier' : identifiers containing consecutive underscores (\"__\") are reserved")); +} + +void AnyConverterTest::validateDataPluginLoadFailed() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Glsl); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_COMPARE(converter->validateData({}, {}), + std::make_pair(false, "")); + #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin GlslShaderConverter is not static and was not found in nonexistent\n" + "ShaderTools::AnyConverter::validateData(): cannot load the GlslShaderConverter plugin\n"); + #else + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin GlslShaderConverter was not found\n" + "ShaderTools::AnyConverter::validateData(): cannot load the GlslShaderConverter plugin\n"); + #endif +} + +void AnyConverterTest::validateDataNoFormatSet() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_COMPARE(converter->validateData({}, "dead.cg"), + std::make_pair(false, "")); + CORRADE_COMPARE(out.str(), "ShaderTools::AnyConverter::validateData(): no input format specified\n"); +} + +void AnyConverterTest::validateDataNotSupported() { + CORRADE_SKIP("No plugin that would support just validation exists."); +} + +void AnyConverterTest::validateDataPreprocessNotSupported() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("SpirvToolsShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("SpirvToolsShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Spirv); + + converter->setDefinitions({ + {"DEFINE", "hahahahah"} + }); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_COMPARE(converter->validateData({}, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.spv"))), + std::make_pair(false, "")); + CORRADE_COMPARE(out.str(), + "ShaderTools::AnyConverter::validateData(): SpirvToolsShaderConverter does not support preprocessing\n"); +} + +void AnyConverterTest::validateDataPropagateFlags() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Glsl); + + /* With this, the warning should turn into an error. The converter should + also print the verbose info. */ + converter->setFlags(ConverterFlag::Verbose|ConverterFlag::WarningAsError); + + std::ostringstream out; + Debug redirectDebug{&out}; + CORRADE_COMPARE(converter->validateData(Stage::Fragment, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl"))), + std::make_pair(false, "WARNING: 0:10: 'reserved__identifier' : identifiers containing consecutive underscores (\"__\") are reserved")); + CORRADE_COMPARE(out.str(), + "ShaderTools::AnyConverter::validateData(): using GlslShaderConverter (provided by GlslangShaderConverter)\n"); +} + +void AnyConverterTest::validateDataPropagateInputVersion() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Glsl); + + /* This is an invalid version. We have to supply a valid file path because + the version gets checked in doValidateData(), called from + AbstractConverter::doValidateFile() with the file contents. */ + converter->setInputFormat(Format::Glsl, "100"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_COMPARE(converter->validateData(Stage::Fragment, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl"))), + std::make_pair(false, "")); + CORRADE_COMPARE(out.str(), + "ShaderTools::GlslangConverter::validateData(): input format version should be one of supported GLSL #version strings but got 100\n"); +} + +void AnyConverterTest::validateDataPropagateOutputVersion() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Glsl); + + /* This is an invalid version. We have to supply a valid file path because + the version gets checked in doValidateData(), called from + AbstractConverter::doValidateFile() with the file contents. */ + converter->setOutputFormat(Format::Glsl, "opengl4.0"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_COMPARE(converter->validateData(Stage::Fragment, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl"))), + std::make_pair(false, "")); + CORRADE_COMPARE(out.str(), + "ShaderTools::GlslangConverter::validateData(): output format should be Spirv or Unspecified but got ShaderTools::Format::Glsl\n"); +} + +void AnyConverterTest::validateDataPropagatePreprocess() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Glsl); + + /* Check that undefining works properly -- if it stays defined, the source + won't compile */ + converter->setDefinitions({ + {"SHOULD_BE_UNDEFINED", "really"}, + {"SHOULD_BE_UNDEFINED", nullptr}, + {"reserved__identifier", "different__but_also_wrong"} + }); + + CORRADE_COMPARE(converter->validateData(Stage::Fragment, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl"))), + std::make_pair(true, "WARNING: 0:10: 'different__but_also_wrong' : identifiers containing consecutive underscores (\"__\") are reserved")); +} + +void AnyConverterTest::convertFileToFile() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -322,7 +603,24 @@ void AnyConverterTest::convert() { "WARNING: {}:10: 'reserved__identifier' : identifiers containing consecutive underscores (\"__\") are reserved\n", inputFilename)); } -void AnyConverterTest::convertUnknownInput() { +void AnyConverterTest::convertFileToFilePluginLoadFailed() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToFile({}, "file.spv", "file.glsl")); + #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin SpirvToGlslShaderConverter is not static and was not found in nonexistent\n" + "ShaderTools::AnyConverter::convertFileToFile(): cannot load the SpirvToGlslShaderConverter plugin\n"); + #else + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin SpirvToGlslShaderConverter was not found\n" + "ShaderTools::AnyConverter::convertFileToFile(): cannot load the SpirvToGlslShaderConverter plugin\n"); + #endif +} + +void AnyConverterTest::convertFileToFileUnknownInput() { Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); std::ostringstream out; @@ -331,7 +629,7 @@ void AnyConverterTest::convertUnknownInput() { CORRADE_COMPARE(out.str(), "ShaderTools::AnyConverter::convertFileToFile(): cannot determine the format of dead.cg\n"); } -void AnyConverterTest::convertUnknownOutput() { +void AnyConverterTest::convertFileToFileUnknownOutput() { Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); std::ostringstream out; @@ -340,11 +638,11 @@ void AnyConverterTest::convertUnknownOutput() { CORRADE_COMPARE(out.str(), "ShaderTools::AnyConverter::convertFileToFile(): cannot determine the format of whatever.osl\n"); } -void AnyConverterTest::convertNotSupported() { +void AnyConverterTest::convertFileToFileNotSupported() { CORRADE_SKIP("No plugin that would support just conversion exists."); } -void AnyConverterTest::convertPreprocessNotSupported() { +void AnyConverterTest::convertFileToFilePreprocessNotSupported() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -376,7 +674,7 @@ void AnyConverterTest::convertPreprocessNotSupported() { "ShaderTools::AnyConverter::convertFileToFile(): SpirvToolsShaderConverter does not support preprocessing\n"); } -void AnyConverterTest::convertDebugInfoNotSupported() { +void AnyConverterTest::convertFileToFileDebugInfoNotSupported() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -399,7 +697,7 @@ void AnyConverterTest::convertDebugInfoNotSupported() { "ShaderTools::AnyConverter::convertFileToFile(): SpirvToolsShaderConverter does not support controlling debug info output\n"); } -void AnyConverterTest::convertOptimizationNotSupported() { +void AnyConverterTest::convertFileToFileOptimizationNotSupported() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -422,7 +720,7 @@ void AnyConverterTest::convertOptimizationNotSupported() { "ShaderTools::AnyConverter::convertFileToFile(): GlslangShaderConverter does not support optimization\n"); } -void AnyConverterTest::convertPropagateFlags() { +void AnyConverterTest::convertFileToFilePropagateFlags() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -452,7 +750,7 @@ void AnyConverterTest::convertPropagateFlags() { "WARNING: {}:10: 'reserved__identifier' : identifiers containing consecutive underscores (\"__\") are reserved\n", filename)); } -void AnyConverterTest::convertPropagateInputVersion() { +void AnyConverterTest::convertFileToFilePropagateInputVersion() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -476,7 +774,7 @@ void AnyConverterTest::convertPropagateInputVersion() { "ShaderTools::GlslangConverter::convertDataToData(): input format version should be one of supported GLSL #version strings but got 100\n"); } -void AnyConverterTest::convertPropagateOutputVersion() { +void AnyConverterTest::convertFileToFilePropagateOutputVersion() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -500,7 +798,7 @@ void AnyConverterTest::convertPropagateOutputVersion() { "ShaderTools::GlslangConverter::convertDataToData(): output format version target should be opengl4.5 or vulkanX.Y but got opengl4.0\n"); } -void AnyConverterTest::convertPropagatePreprocess() { +void AnyConverterTest::convertFileToFilePropagatePreprocess() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -534,7 +832,7 @@ void AnyConverterTest::convertPropagatePreprocess() { "WARNING: {}:10: 'different__but_also_wrong' : identifiers containing consecutive underscores (\"__\") are reserved\n", inputFilename)); } -void AnyConverterTest::convertPropagateDebugInfo() { +void AnyConverterTest::convertFileToFilePropagateDebugInfo() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -558,7 +856,7 @@ void AnyConverterTest::convertPropagateDebugInfo() { "ShaderTools::GlslangConverter::convertDataToData(): debug info level should be 0, 1 or empty but got 2\n"); } -void AnyConverterTest::convertPropagateOptimization() { +void AnyConverterTest::convertFileToFilePropagateOptimization() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -582,6 +880,635 @@ void AnyConverterTest::convertPropagateOptimization() { "ShaderTools::SpirvToolsConverter::convertDataToData(): optimization level should be 0, 1, s, legalizeHlsl, vulkanToWebGpu, webGpuToVulkan or empty but got 2\n"); } +void AnyConverterTest::convertFileToData() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setOutputFormat(Format::Spirv); + + const std::string inputFilename = Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl"); + + /* Make it print a warning so we know it's doing something */ + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter->convertFileToData(Stage::Fragment, inputFilename)); + CORRADE_COMPARE(out.str(), Utility::formatString( + "ShaderTools::GlslangConverter::convertDataToData(): compilation succeeded with the following message:\n" + "WARNING: {}:10: 'reserved__identifier' : identifiers containing consecutive underscores (\"__\") are reserved\n", inputFilename)); +} + +void AnyConverterTest::convertFileToDataPluginLoadFailed() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + converter->setOutputFormat(Format::Wgsl); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToData({}, "file.spv")); + #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin SpirvToWgslShaderConverter is not static and was not found in nonexistent\n" + "ShaderTools::AnyConverter::convertFileToData(): cannot load the SpirvToWgslShaderConverter plugin\n"); + #else + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin SpirvToWgslShaderConverter was not found\n" + "ShaderTools::AnyConverter::convertFileToData(): cannot load the SpirvToWgslShaderConverter plugin\n"); + #endif +} + +void AnyConverterTest::convertFileToDataUnknown() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToData({}, "dead.cg")); + CORRADE_COMPARE(out.str(), "ShaderTools::AnyConverter::convertFileToData(): cannot determine the format of dead.cg\n"); +} + +void AnyConverterTest::convertFileToDataNoFormatSet() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToData({}, "file.spv")); + CORRADE_COMPARE(out.str(), "ShaderTools::AnyConverter::convertFileToData(): no output format specified\n"); +} + +void AnyConverterTest::convertFileToDataNotSupported() { + CORRADE_SKIP("No plugin that would support just conversion exists."); +} + +void AnyConverterTest::convertFileToDataPreprocessNotSupported() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("SpirvToolsShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("SpirvToolsShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setOutputFormat(Format::SpirvAssembly); + + converter->setDefinitions({ + {"DEFINE", "hahahahah"} + }); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToData({}, Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.spv"))); + CORRADE_COMPARE(out.str(), + "ShaderTools::AnyConverter::convertFileToData(): SpirvToolsShaderConverter does not support preprocessing\n"); + + /* It should fail for the flag as well */ + out.str({}); + converter->setDefinitions({}); + converter->setFlags(ConverterFlag::PreprocessOnly); + CORRADE_VERIFY(!converter->convertFileToData({}, Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.spv"))); + CORRADE_COMPARE(out.str(), + "ShaderTools::AnyConverter::convertFileToData(): SpirvToolsShaderConverter does not support preprocessing\n"); +} + +void AnyConverterTest::convertFileToDataDebugInfoNotSupported() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("SpirvToolsShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("SpirvToolsShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setOutputFormat(Format::SpirvAssembly); + + converter->setDebugInfoLevel("1"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToData({}, Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.spv"))); + /** @todo it once may support that, in which case we need to find another + victim */ + CORRADE_COMPARE(out.str(), + "ShaderTools::AnyConverter::convertFileToData(): SpirvToolsShaderConverter does not support controlling debug info output\n"); +} + +void AnyConverterTest::convertFileToDataOptimizationNotSupported() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setOutputFormat(Format::Spirv); + + converter->setOptimizationLevel("1"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToData({}, Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl"))); + /** @todo it once may support that, in which case we need to find another + victim */ + CORRADE_COMPARE(out.str(), + "ShaderTools::AnyConverter::convertFileToData(): GlslangShaderConverter does not support optimization\n"); +} + +void AnyConverterTest::convertFileToDataPropagateFlags() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + const std::string filename = Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl"); + + converter->setOutputFormat(Format::Spirv); + + /* With this, the warning should turn into an error. The converter should + also print the verbose info. */ + converter->setFlags(ConverterFlag::Verbose|ConverterFlag::WarningAsError); + + /* We have to supply a valid file path because the version gets checked in + doConvertDataToData(), called from AbstractConverter::doConvertFileToFile() + with the file contents. */ + std::ostringstream out; + Debug redirectDebug{&out}; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToData(Stage::Fragment, filename)); + CORRADE_COMPARE(out.str(), Utility::formatString( + "ShaderTools::AnyConverter::convertFileToData(): using GlslToSpirvShaderConverter (provided by GlslangShaderConverter)\n" + "ShaderTools::GlslangConverter::convertDataToData(): compilation failed:\n" + "WARNING: {}:10: 'reserved__identifier' : identifiers containing consecutive underscores (\"__\") are reserved\n", filename)); +} + +void AnyConverterTest::convertFileToDataPropagateInputVersion() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + /* This is an invalid version */ + converter->setInputFormat(Format::Glsl, "100"); + + converter->setOutputFormat(Format::Spirv); + + /* We have to supply a valid file path because the version gets checked in + doConvertDataToData(), called from AbstractConverter::doConvertFileToFile() + with the file contents. */ + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToData(Stage::Fragment, Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl"))); + CORRADE_COMPARE(out.str(), + "ShaderTools::GlslangConverter::convertDataToData(): input format version should be one of supported GLSL #version strings but got 100\n"); +} + +void AnyConverterTest::convertFileToDataPropagateOutputVersion() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + /* This is an invalid version */ + converter->setOutputFormat(Format::Spirv, "opengl4.0"); + + /* We have to supply a valid file path because the version gets checked in + doConvertDataToData(), called from AbstractConverter::doConvertFileToFile() + with the file contents. */ + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToData(Stage::Fragment, Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl"))); + CORRADE_COMPARE(out.str(), + "ShaderTools::GlslangConverter::convertDataToData(): output format version target should be opengl4.5 or vulkanX.Y but got opengl4.0\n"); +} + +void AnyConverterTest::convertFileToDataPropagatePreprocess() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setOutputFormat(Format::Spirv); + + /* Check that undefining works properly -- if it stays defined, the source + won't compile */ + converter->setDefinitions({ + {"SHOULD_BE_UNDEFINED", "really"}, + {"SHOULD_BE_UNDEFINED", nullptr}, + {"reserved__identifier", "different__but_also_wrong"} + }); + + const std::string inputFilename = Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl"); + + /* Make it print a warning so we know it's doing something */ + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter->convertFileToData(Stage::Fragment, inputFilename)); + CORRADE_COMPARE(out.str(), Utility::formatString( + "ShaderTools::GlslangConverter::convertDataToData(): compilation succeeded with the following message:\n" + "WARNING: {}:10: 'different__but_also_wrong' : identifiers containing consecutive underscores (\"__\") are reserved\n", inputFilename)); +} + +void AnyConverterTest::convertFileToDataPropagateDebugInfo() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setOutputFormat(Format::Spirv); + + /* This is an invalid level */ + converter->setDebugInfoLevel("2"); + + /* We have to supply a valid file path because the version gets checked in + doConvertDataToData(), called from AbstractConverter::doConvertFileToFile() + with the file contents. */ + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToData(Stage::Fragment, Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl"))); + CORRADE_COMPARE(out.str(), + "ShaderTools::GlslangConverter::convertDataToData(): debug info level should be 0, 1 or empty but got 2\n"); +} + +void AnyConverterTest::convertFileToDataPropagateOptimization() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("SpirvToolsShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("SpirvToolsShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setOutputFormat(Format::Spirv); + + /* This is an invalid level */ + converter->setOptimizationLevel("2"); + + /* We have to supply a valid file path because the version gets checked in + doConvertDataToData(), called from AbstractConverter::doConvertFileToFile() + with the file contents. */ + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToData(Stage::Fragment, Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.spv"))); + CORRADE_COMPARE(out.str(), + "ShaderTools::SpirvToolsConverter::convertDataToData(): optimization level should be 0, 1, s, legalizeHlsl, vulkanToWebGpu, webGpuToVulkan or empty but got 2\n"); +} + +void AnyConverterTest::convertDataToData() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Glsl); + converter->setOutputFormat(Format::Spirv); + + /* Make it print a warning so we know it's doing something */ + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter->convertDataToData(Stage::Fragment, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl")))); + CORRADE_COMPARE(out.str(), + "ShaderTools::GlslangConverter::convertDataToData(): compilation succeeded with the following message:\n" + "WARNING: 0:10: 'reserved__identifier' : identifiers containing consecutive underscores (\"__\") are reserved\n"); +} + +void AnyConverterTest::convertDataToDataPluginLoadFailed() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Hlsl); + converter->setOutputFormat(Format::Wgsl); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertDataToData({}, {})); + #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin HlslToWgslShaderConverter is not static and was not found in nonexistent\n" + "ShaderTools::AnyConverter::convertDataToData(): cannot load the HlslToWgslShaderConverter plugin\n"); + #else + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin HlslToWgslShaderConverter was not found\n" + "ShaderTools::AnyConverter::convertDataToData(): cannot load the HlslToWgslShaderConverter plugin\n"); + #endif +} + +void AnyConverterTest::convertDataToDataNoInputFormatSet() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertDataToData({}, {})); + CORRADE_COMPARE(out.str(), "ShaderTools::AnyConverter::convertDataToData(): no input format specified\n"); +} + +void AnyConverterTest::convertDataToDataNoOutputFormatSet() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Spirv); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertDataToData({}, {})); + CORRADE_COMPARE(out.str(), "ShaderTools::AnyConverter::convertDataToData(): no output format specified\n"); +} + +void AnyConverterTest::convertDataToDataNotSupported() { + CORRADE_SKIP("No plugin that would support just conversion exists."); +} + +void AnyConverterTest::convertDataToDataPreprocessNotSupported() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("SpirvToolsShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("SpirvToolsShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Spirv); + converter->setOutputFormat(Format::SpirvAssembly); + + converter->setDefinitions({ + {"DEFINE", "hahahahah"} + }); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertDataToData({}, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.spv")))); + CORRADE_COMPARE(out.str(), + "ShaderTools::AnyConverter::convertDataToData(): SpirvToolsShaderConverter does not support preprocessing\n"); + + /* It should fail for the flag as well */ + out.str({}); + converter->setDefinitions({}); + converter->setFlags(ConverterFlag::PreprocessOnly); + CORRADE_VERIFY(!converter->convertDataToData({}, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.spv")))); + CORRADE_COMPARE(out.str(), + "ShaderTools::AnyConverter::convertDataToData(): SpirvToolsShaderConverter does not support preprocessing\n"); +} + +void AnyConverterTest::convertDataToDataDebugInfoNotSupported() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("SpirvToolsShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("SpirvToolsShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Spirv); + converter->setOutputFormat(Format::SpirvAssembly); + + converter->setDebugInfoLevel("1"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertDataToData({}, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.spv")))); + /** @todo it once may support that, in which case we need to find another + victim */ + CORRADE_COMPARE(out.str(), + "ShaderTools::AnyConverter::convertDataToData(): SpirvToolsShaderConverter does not support controlling debug info output\n"); +} + +void AnyConverterTest::convertDataToDataOptimizationNotSupported() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Glsl); + converter->setOutputFormat(Format::Spirv); + + converter->setOptimizationLevel("1"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertDataToData({}, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl")))); + /** @todo it once may support that, in which case we need to find another + victim */ + CORRADE_COMPARE(out.str(), + "ShaderTools::AnyConverter::convertDataToData(): GlslangShaderConverter does not support optimization\n"); +} + +void AnyConverterTest::convertDataToDataPropagateFlags() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Glsl); + converter->setOutputFormat(Format::Spirv); + + /* With this, the warning should turn into an error. The converter should + also print the verbose info. */ + converter->setFlags(ConverterFlag::Verbose|ConverterFlag::WarningAsError); + + /* We have to supply a valid file path because the version gets checked in + doConvertDataToData(), called from AbstractConverter::doConvertFileToFile() + with the file contents. */ + std::ostringstream out; + Debug redirectDebug{&out}; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertDataToData(Stage::Fragment, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl")))); + CORRADE_COMPARE(out.str(), + "ShaderTools::AnyConverter::convertDataToData(): using GlslToSpirvShaderConverter (provided by GlslangShaderConverter)\n" + "ShaderTools::GlslangConverter::convertDataToData(): compilation failed:\n" + "WARNING: 0:10: 'reserved__identifier' : identifiers containing consecutive underscores (\"__\") are reserved\n"); +} + +void AnyConverterTest::convertDataToDataPropagateInputVersion() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + /* This is an invalid version */ + converter->setInputFormat(Format::Glsl, "100"); + + converter->setOutputFormat(Format::Spirv); + + /* We have to supply a valid file path because the version gets checked in + doConvertDataToData(), called from AbstractConverter::doConvertFileToFile() + with the file contents. */ + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertDataToData(Stage::Fragment, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl")))); + CORRADE_COMPARE(out.str(), + "ShaderTools::GlslangConverter::convertDataToData(): input format version should be one of supported GLSL #version strings but got 100\n"); +} + +void AnyConverterTest::convertDataToDataPropagateOutputVersion() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Glsl); + + /* This is an invalid version */ + converter->setOutputFormat(Format::Spirv, "opengl4.0"); + + /* We have to supply a valid file path because the version gets checked in + doConvertDataToData(), called from AbstractConverter::doConvertFileToFile() + with the file contents. */ + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertDataToData(Stage::Fragment, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl")))); + CORRADE_COMPARE(out.str(), + "ShaderTools::GlslangConverter::convertDataToData(): output format version target should be opengl4.5 or vulkanX.Y but got opengl4.0\n"); +} + +void AnyConverterTest::convertDataToDataPropagatePreprocess() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Glsl); + converter->setOutputFormat(Format::Spirv); + + /* Check that undefining works properly -- if it stays defined, the source + won't compile */ + converter->setDefinitions({ + {"SHOULD_BE_UNDEFINED", "really"}, + {"SHOULD_BE_UNDEFINED", nullptr}, + {"reserved__identifier", "different__but_also_wrong"} + }); + + /* Make it print a warning so we know it's doing something */ + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter->convertDataToData(Stage::Fragment, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl")))); + CORRADE_COMPARE(out.str(), + "ShaderTools::GlslangConverter::convertDataToData(): compilation succeeded with the following message:\n" + "WARNING: 0:10: 'different__but_also_wrong' : identifiers containing consecutive underscores (\"__\") are reserved\n"); +} + +void AnyConverterTest::convertDataToDataPropagateDebugInfo() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Glsl); + converter->setOutputFormat(Format::Spirv); + + /* This is an invalid level */ + converter->setDebugInfoLevel("2"); + + /* We have to supply a valid file path because the version gets checked in + doConvertDataToData(), called from AbstractConverter::doConvertFileToFile() + with the file contents. */ + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertDataToData(Stage::Fragment, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl")))); + CORRADE_COMPARE(out.str(), + "ShaderTools::GlslangConverter::convertDataToData(): debug info level should be 0, 1 or empty but got 2\n"); +} + +void AnyConverterTest::convertDataToDataPropagateOptimization() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("SpirvToolsShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("SpirvToolsShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Spirv); + converter->setOutputFormat(Format::Spirv); + + /* This is an invalid level */ + converter->setOptimizationLevel("2"); + + /* We have to supply a valid file path because the version gets checked in + doConvertDataToData(), called from AbstractConverter::doConvertFileToFile() + with the file contents. */ + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertDataToData(Stage::Fragment, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.spv")))); + CORRADE_COMPARE(out.str(), + "ShaderTools::SpirvToolsConverter::convertDataToData(): optimization level should be 0, 1, s, legalizeHlsl, vulkanToWebGpu, webGpuToVulkan or empty but got 2\n"); +} + void AnyConverterTest::detectValidate() { auto&& data = DetectValidateData[testCaseInstanceId()]; setTestCaseDescription(data.name); From c4778363968e51514863541a44d84ac40ccdada1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 12 Feb 2021 14:21:42 +0100 Subject: [PATCH 29/44] magnum-shaderconverter: use the less error-prone >= for flag checks. Otherwise if some feature flags are combinations, & alone won't fail if only a subset is set. --- src/Magnum/ShaderTools/shaderconverter.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Magnum/ShaderTools/shaderconverter.cpp b/src/Magnum/ShaderTools/shaderconverter.cpp index 543c08925..8cb3fd41c 100644 --- a/src/Magnum/ShaderTools/shaderconverter.cpp +++ b/src/Magnum/ShaderTools/shaderconverter.cpp @@ -335,7 +335,7 @@ see documentation of a particular converter for more information.)") Containers::Array> linkInputs; if(i == 0) { if((args.isSet("preprocess-only") || args.arrayValueCount("define") || args.arrayValueCount("undefine"))) { - if(!(converter->features() & ShaderTools::ConverterFeature::Preprocess)) { + if(!(converter->features() >= ShaderTools::ConverterFeature::Preprocess)) { Error{} << "The -E / -D / -U options are set, but" << converterName << "doesn't support preprocessing"; return 10; } @@ -360,7 +360,7 @@ see documentation of a particular converter for more information.)") } if(!args.value("optimize").isEmpty()) { - if(!(converter->features() & ShaderTools::ConverterFeature::Optimize)) { + if(!(converter->features() >= ShaderTools::ConverterFeature::Optimize)) { Error{} << "The -O option is set, but" << converterName << "doesn't support optimization"; return 11; } @@ -369,7 +369,7 @@ see documentation of a particular converter for more information.)") } if(!args.value("debug-info").isEmpty()) { - if(!(converter->features() & ShaderTools::ConverterFeature::DebugInfo)) { + if(!(converter->features() >= ShaderTools::ConverterFeature::DebugInfo)) { Error{} << "The -g option is set, but" << converterName << "doesn't support debug info"; return 12; } @@ -394,7 +394,7 @@ see documentation of a particular converter for more information.)") re-entered again */ CORRADE_INTERNAL_ASSERT(i == 0); - if(!(converter->features() & ShaderTools::ConverterFeature::ValidateFile)) { + if(!(converter->features() >= ShaderTools::ConverterFeature::ValidateFile)) { Error{} << converterName << "doesn't support file validation"; return 13; } @@ -417,7 +417,7 @@ see documentation of a particular converter for more information.)") /* This is the first *and* last --converter, go from a file to a file */ if(i == 0 && converterCount <= 1) { - if(!(converter->features() & ShaderTools::ConverterFeature::ConvertFile)) { + if(!(converter->features() >= ShaderTools::ConverterFeature::ConvertFile)) { Error{} << converterName << "doesn't support file conversion"; return 15; } @@ -441,7 +441,7 @@ see documentation of a particular converter for more information.)") /* Otherwise we need to go through data */ } else { - if(!(converter->features() & ShaderTools::ConverterFeature::ConvertData)) { + if(!(converter->features() >= ShaderTools::ConverterFeature::ConvertData)) { Error{} << converterName << "doesn't support data conversion"; return 18; } From c879f91f5a17c33c6c1bd265100b763751d42daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 12 Feb 2021 14:32:03 +0100 Subject: [PATCH 30/44] shaderconverter: add a very basic --info option. --- src/Magnum/ShaderTools/shaderconverter.cpp | 133 +++++++++++++++++++-- 1 file changed, 122 insertions(+), 11 deletions(-) diff --git a/src/Magnum/ShaderTools/shaderconverter.cpp b/src/Magnum/ShaderTools/shaderconverter.cpp index 8cb3fd41c..ea4098ef9 100644 --- a/src/Magnum/ShaderTools/shaderconverter.cpp +++ b/src/Magnum/ShaderTools/shaderconverter.cpp @@ -34,6 +34,7 @@ #include "Magnum/Math/Functions.h" #include "Magnum/ShaderTools/AbstractConverter.h" #include "Magnum/ShaderTools/Stage.h" +#include "Magnum/ShaderTools/Implementation/spirv.h" namespace Magnum { @@ -63,10 +64,10 @@ information. @code{.sh} magnum-shaderconverter [-h|--help] [--validate] [--link] [-C|--converter NAME]... [--plugin-dir DIR] - [-c|--converter-options key=val,key2=val2,…]... [-q|--quiet] [-v|--verbose] - [--warning-as-error] [-E|--preprocess-only] [-D|--define name=value]... - [-U|--undefine name]... [-O|--optimize LEVEL] [-g|--debug-info LEVEL] - [--input-format glsl|spv|spvasm|hlsl|metal]... + [-c|--converter-options key=val,key2=val2,…]... [--info] [-q|--quiet] + [-v|--verbose] [--warning-as-error] [-E|--preprocess-only] + [-D|--define name=value]... [-U|--undefine name]... [-O|--optimize LEVEL] + [-g|--debug-info LEVEL] [--input-format glsl|spv|spvasm|hlsl|metal]... [--output-format glsl|spv|spvasm|hlsl|metal]... [--input-version VERSION]... [--output-version VERSION]... [--] input... output @@ -75,9 +76,10 @@ magnum-shaderconverter [-h|--help] [--validate] [--link] Arguments: - `input` --- input file(s) -- `output` --- output file, disallowed for `--validate`. If neither - `--validate` nor `--link` is present, corresponds to the - @ref ShaderTools::AbstractConverter::convertFileToFile() function. +- `output` --- output file; ignored if `--info` is present, disallowed for + `--validate`. If neither `--info`, `--validate` nor `--link` is present, + corresponds to the @ref ShaderTools::AbstractConverter::convertFileToFile() + function. - `-h`, `--help` --- display this help message and exit - `--validate` --- validate input. Corresponds to the @ref ShaderTools::AbstractConverter::validateFile() function. @@ -87,6 +89,7 @@ Arguments: - `--plugin-dir DIR` --- override base plugin dir - `-c`, `--converter-options key=val,key2=val2,…` --- configuration options to pass to the converter(s) +- `--info` --- print SPIR-V module info and exit - `-q`, `--quiet` --- quiet output from converter plugin(s). Corresponds to the @ref ShaderTools::ConverterFlag::Quiet flag. - `-v`, `--verbose` --- verbose output from converter plugin(s). Corresponds @@ -170,15 +173,71 @@ magnum-shaderconverter phong.frag -DDIFFUSE_TEXTURE -DNORMAL_TEXTURE --input-ver using namespace Corrade::Containers::Literals; using namespace Magnum; +namespace { + +ShaderTools::Stage spvExecutionModelToStage(const SpvExecutionModel model) { + switch(model) { + #define _c(model, stage) case SpvExecutionModel ## model: return ShaderTools::Stage::stage; + _c(Vertex, Vertex) + _c(Fragment, Fragment) + _c(Geometry, Geometry) + _c(TessellationControl, TessellationControl) + _c(TessellationEvaluation, TessellationEvaluation) + _c(GLCompute, Compute) + _c(RayGenerationKHR, RayGeneration) + _c(AnyHitKHR, RayAnyHit) + _c(ClosestHitKHR, RayClosestHit) + _c(MissKHR, RayMiss) + _c(IntersectionKHR, RayIntersection) + _c(CallableKHR, RayCallable) + _c(TaskNV, MeshTask) + _c(MeshNV, Mesh) + _c(Kernel, Kernel) + #undef _c + + case SpvExecutionModelMax: break; + } + + /* Encode unknown stages with the highest bit set. SpvExecutionModelMax is + 0x7fffffff, so this shouldn't lead to any data loss. */ + return ShaderTools::Stage((1u << 31)|model); +} + +void printSpirvInfo(Containers::ArrayView data) { + while(Containers::Optional entrypoint = ShaderTools::Implementation::spirvNextEntrypoint(data)) { + Debug d; + d << "Entrypoint" << entrypoint->name << "(" << Debug::nospace << spvExecutionModelToStage(entrypoint->executionModel) << Debug::nospace << ")" << Debug::newline; + + Containers::Array interface{Containers::ValueInit, entrypoint->interfaces.size()}; + ShaderTools::Implementation::spirvEntrypointInterface(data, *entrypoint, interface); + for(const ShaderTools::Implementation::SpirvEntrypointInterface& i: interface) { + d << " "; + if(!i.storageClass) d << "(unknown)"; + else if(*i.storageClass == SpvStorageClassInput) + d << "in"; + else if(*i.storageClass == SpvStorageClassOutput) + d << "out"; + else d << "SpvStorageClass(" << Debug::nospace << *i.storageClass << Debug::nospace << ")"; + + if(i.location) d << "(location=" << Debug::nospace << *i.location << Debug::nospace << ")"; + + d << Debug::newline; + } + } +} + +} + int main(int argc, char** argv) { Utility::Arguments args; args.addArrayArgument("input").setHelp("input", "input file(s)") - .addArgument("output").setHelp("output", "output file, disallowed for --validate") + .addArgument("output").setHelp("output", "output file; ignored if --info is present, disallowed for --validate") .addBooleanOption("validate").setHelp("validate", "validate input") .addBooleanOption("link").setHelp("link", "link multiple input files together") .addArrayOption('C', "converter").setHelp("converter", "shader converter plugin(s)") .addOption("plugin-dir").setHelp("plugin-dir", "override base plugin dir", "DIR") .addArrayOption('c', "converter-options").setHelp("converter-options", "configuration options to pass to the converter(s)", "key=val,key2=val2,…") + .addBooleanOption("info").setHelp("info", "print SPIR-V module info and exit") .addBooleanOption('q', "quiet").setHelp("quiet", "quiet output from converter plugin(s)") .addBooleanOption('v', "verbose").setHelp("verbose", "verbose output from converter plugin(s)") .addBooleanOption("warning-as-error").setHelp("warning-as-error", "treat warnings as errors") @@ -193,15 +252,21 @@ int main(int argc, char** argv) { .addArrayOption("input-version").setHelp("input-version", "input format version for each converter", "VERSION") .addArrayOption("output-version").setHelp("output-version", "output format version for each converter", "VERSION") .setParseErrorCallback([](const Utility::Arguments& args, Utility::Arguments::ParseError error, const std::string& key) { - /* If --validate is passed, we don't need the output argument */ + /* If --info / --validate is passed, we don't need the output + argument */ if(error == Utility::Arguments::ParseError::MissingArgument && - key == "output" && args.isSet("validate")) return true; + key == "output" && (args.isSet("info") || args.isSet("validate"))) + return true; /* Handle all other errors as usual */ return false; }) .setGlobalHelp(R"(Converts, compiles, optimizes and links shaders of different formats. +If --info is given and the input looks like a SPIR-V binary, the utility prints +information about its entrypoints using builtin SPIR-V reflection capabilities. +If it's not a SPIR-V binary, it's converted to it first. + If --validate is given, the utility will validate the input file using passed --converter (or AnyShaderConverter if none is specified), print the validation log on output and exit with a non-zero code if the validation fails. If --link @@ -237,6 +302,12 @@ see documentation of a particular converter for more information.)") Error{} << "Output file shouldn't be set for --validate:" << args.value("output"); return 1; } + + /* Not an error in this case, it should be possible to just append + --info to existing command line without having to remove anything. + But print a warning at least, it could also be a mistyped option. */ + if(args.isSet("info")) + Warning{} << "Ignoring output file for --info:" << args.value("output"); } if(!args.isSet("link")) { if(args.arrayValueCount("input") != 1) { @@ -264,6 +335,17 @@ see documentation of a particular converter for more information.)") return 6; } + /* If we want just SPIR-V info and the input looks like a SPIR-V binary, + do that right away without going through any plugin. If it doesn't, we + try again after using a converter.s */ + if(args.isSet("info")) { + const Containers::Array data = Utility::Directory::read(args.arrayValue("input", 0)); + if(Containers::ArrayView spirv = ShaderTools::Implementation::spirvData(data, data.size())) { + printSpirvInfo(spirv); + return 0; + } + } + /* Set up a converter manager */ PluginManager::Manager converterManager{ args.value("plugin-dir").empty() ? std::string{} : @@ -286,8 +368,11 @@ see documentation of a particular converter for more information.)") if(i < args.arrayValueCount("converter-options")) Implementation::setOptions(*converter, args.arrayValue("converter-options", i)); - /* Parse format, if passed */ + /* Parse format, if passed. If --info is desired, implicitly set the + output format to SPIR-V */ ShaderTools::Format inputFormat{}, outputFormat{}; + if(args.isSet("info")) + outputFormat = ShaderTools::Format::Spirv; auto parseFormat = [](Containers::StringView format) -> Containers::Optional { if(format == ""_s) return ShaderTools::Format::Unspecified; if(format == "glsl"_s) return ShaderTools::Format::Glsl; @@ -387,6 +472,32 @@ see documentation of a particular converter for more information.)") converter->setFlags(flags); + /* If we want just SPIR-V info, convert to a SPIR-V and exit */ + if(args.isSet("info")) { + /* The info exits right after, so this branch shouldn't get + re-entered again */ + CORRADE_INTERNAL_ASSERT(i == 0); + + if(!(converter->features() >= ShaderTools::ConverterFeature::ConvertData)) { + Error{} << converterName << "doesn't support data conversion"; + return 18; /* same code as the same message below */ + } + + if(!(data = converter->convertFileToData(ShaderTools::Stage::Unspecified, args.arrayValue("input", 0)))) { + Error{} << "Cannot convert" << args.arrayValue("input", 0); + return 20; /* same code as the same message below */ + } + + Containers::ArrayView spirv = ShaderTools::Implementation::spirvData(data, data.size()); + if(!spirv) { + Error{} << "The output is not a SPIR-V binary, can't print info"; + return 23; + } + + printSpirvInfo(spirv); + return 0; + } + /* If validating, do it just with the first passed converter and then exit */ if(args.isSet("validate")) { From d9a13e6d080c9ce6fdc53d32a0fd42b6dbc512b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 10 Feb 2021 01:25:00 +0100 Subject: [PATCH 31/44] Vk: patch SPIR-V binaries on load on SwiftShader as a workaround. The things I get to do here. Wow. --- src/Magnum/Vk/Implementation/DeviceState.cpp | 8 ++ src/Magnum/Vk/Implementation/DeviceState.h | 2 + .../Vk/Implementation/DriverWorkaround.cpp | 16 +++ src/Magnum/Vk/Implementation/spirvPatching.h | 116 ++++++++++++++++++ src/Magnum/Vk/Shader.cpp | 39 +++++- src/Magnum/Vk/Shader.h | 7 ++ src/Magnum/Vk/Test/CMakeLists.txt | 3 + src/Magnum/Vk/Test/DeviceVkTest.cpp | 5 +- src/Magnum/Vk/Test/ShaderTest.cpp | 115 ++++++++++++++++- src/Magnum/Vk/Test/ShaderTestFiles/convert.sh | 5 + .../Vk/Test/ShaderTestFiles/vert-frag.spv | Bin 0 -> 676 bytes .../Vk/Test/ShaderTestFiles/vert-frag.spvasm | 53 ++++++++ 12 files changed, 364 insertions(+), 5 deletions(-) create mode 100644 src/Magnum/Vk/Implementation/spirvPatching.h create mode 100755 src/Magnum/Vk/Test/ShaderTestFiles/convert.sh create mode 100644 src/Magnum/Vk/Test/ShaderTestFiles/vert-frag.spv create mode 100644 src/Magnum/Vk/Test/ShaderTestFiles/vert-frag.spvasm diff --git a/src/Magnum/Vk/Implementation/DeviceState.cpp b/src/Magnum/Vk/Implementation/DeviceState.cpp index 93c01d05f..5b848d190 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.cpp +++ b/src/Magnum/Vk/Implementation/DeviceState.cpp @@ -32,6 +32,7 @@ #include "Magnum/Vk/Extensions.h" #include "Magnum/Vk/Image.h" #include "Magnum/Vk/RenderPass.h" +#include "Magnum/Vk/Shader.h" #include "Magnum/Vk/Version.h" #include "Magnum/Vk/Implementation/DriverWorkaround.h" @@ -113,6 +114,13 @@ DeviceState::DeviceState(Device& device, Containers::Array + + 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. +*/ + +#include + +#include "Magnum/Math/Functions.h" + +/* All code in this file should be self-contained, with no link-time dependency + on ShaderTools */ +#include "Magnum/ShaderTools/Implementation/spirv.h" + +namespace Magnum { namespace Vk { namespace Implementation { namespace { + +/* This kinda throws const-correctness through the window -- the the SPIR-V + utils work on const data, but this function actually does mutate them */ +bool spirvPatchSwiftShaderConflictingMultiEntrypointLocations(Containers::ArrayView data) { + /* Find vertex/fragment entrypoints and count how many is there in total */ + UnsignedInt entrypointCount = 0; + Containers::Optional vertexEntryPoint, fragmentEntryPoint; + while(Containers::Optional entrypoint = ShaderTools::Implementation::spirvNextEntrypoint(data)) { + ++entrypointCount; + if(entrypoint->executionModel == SpvExecutionModelVertex) + vertexEntryPoint = *entrypoint; + else if(entrypoint->executionModel == SpvExecutionModelFragment) + fragmentEntryPoint = *entrypoint; + } + + /* If there aren't both entrypoints, this bug doesn't affect the shader. If + there are more, we won't attempt anything -- right now SwiftShader + doesn't support geom/tess shaders, so the only possibility is that the + module is a library of multiple different vertex / fragment + implementations and that's too frightening as any patching would most + likely break things *really bad*. */ + if(entrypointCount > 2 || !vertexEntryPoint || !fragmentEntryPoint) + return false; + + /* Get locations and storage classes for all entrypoint interfaces */ + Containers::ArrayView vertexInterface, fragmentInterface; + Containers::ArrayTuple interfaceData{ + {Containers::ValueInit, vertexEntryPoint->interfaces.size(), vertexInterface}, + {Containers::ValueInit, fragmentEntryPoint->interfaces.size(), fragmentInterface} + }; + spirvEntrypointInterface(data, *vertexEntryPoint, vertexInterface); + spirvEntrypointInterface(data, *fragmentEntryPoint, fragmentInterface); + + /* Calculate the max location so we know what to change to */ + UnsignedInt maxLocation = 0; + for(const ShaderTools::Implementation::SpirvEntrypointInterface& i: vertexInterface) + if(i.location) maxLocation = Math::max(maxLocation, *i.location); + for(const ShaderTools::Implementation::SpirvEntrypointInterface& i: fragmentInterface) + if(i.location) maxLocation = Math::max(maxLocation, *i.location); + + /* For all vertex outputs check if there are fragment outputs with the same + locations */ + for(const ShaderTools::Implementation::SpirvEntrypointInterface& vertexOutput: vertexInterface) { + /* Ignore what's not an output or what doesn't have a location (for + example a builtin) */ + if(!vertexOutput.storageClass || *vertexOutput.storageClass != SpvStorageClassOutput || !vertexOutput.location) + continue; + + for(const ShaderTools::Implementation::SpirvEntrypointInterface& fragmentOutput: fragmentInterface) { + /* Ignore what's not an output or what doesn't have a location (for + example a builtin) */ + if(!fragmentOutput.storageClass || *fragmentOutput.storageClass != SpvStorageClassOutput || !fragmentOutput.location) continue; + + /* The same location used, we need to remap. Use the next highest + unused location and change also the corresponding fragment + input, if there's any. */ + if(*vertexOutput.location == *fragmentOutput.location) { + const UnsignedInt newLocation = ++maxLocation; + for(const ShaderTools::Implementation::SpirvEntrypointInterface& fragmentInput: fragmentInterface) { + if(*fragmentInput.storageClass != SpvStorageClassInput) + continue; + if(*fragmentInput.location == *vertexOutput.location) { + /** @todo ugly! */ + *const_cast(fragmentInput.location) = newLocation; + break; + } + } + /** @todo ugly! */ + *const_cast(vertexOutput.location) = newLocation; + break; + } + } + } + + return true; +} + +}}}} + +#endif diff --git a/src/Magnum/Vk/Shader.cpp b/src/Magnum/Vk/Shader.cpp index 55126c988..f5b4557ac 100644 --- a/src/Magnum/Vk/Shader.cpp +++ b/src/Magnum/Vk/Shader.cpp @@ -26,11 +26,14 @@ #include "Shader.h" #include "ShaderCreateInfo.h" -#include +#include +#include #include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Implementation/DeviceState.h" +#include "Magnum/Vk/Implementation/spirvPatching.h" namespace Magnum { namespace Vk { @@ -87,7 +90,7 @@ Shader Shader::wrap(Device& device, const VkShaderModule handle, const HandleFla } Shader::Shader(Device& device, const ShaderCreateInfo& info): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { - MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateShaderModule(device, info, nullptr, &_handle)); + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device.state().createShaderImplementation(device, *info, nullptr, _handle)); } Shader::Shader(NoCreateT): _device{}, _handle{} {} @@ -115,4 +118,36 @@ VkShaderModule Shader::release() { return handle; } +VkResult Shader::createImplementationDefault(Device& device, const VkShaderModuleCreateInfo& info, const VkAllocationCallbacks* callbacks, VkShaderModule& handle) { + return device->CreateShaderModule(device, &info, callbacks, &handle); +} + +VkResult Shader::createImplementationSwiftShaderMultiEntryPointPatching(Device& device, const VkShaderModuleCreateInfo& info, const VkAllocationCallbacks* callbacks, VkShaderModule& handle) { + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + VkShaderModuleCreateInfo patchedInfo(info); + + /** @todo there's too many casts and it's all slightly weird, figure out a + better API (the problem is that spirvData() skips the header and so we + can't use its output to copy anything anywhere, but even if we could + we'd need to call it again on the copied mutable data and that's even + worse than what's there now) */ + + /* Even though our ShaderCreateInfo *might* have the code owned and we thus + might not need to copy it, the owned code may also be read-only for + whatever reason (memory-mapped location etc). Thus, to prevent issues, + we go the safe route and copy always. */ + Containers::Array mutableCode{Containers::NoInit, info.codeSize}; + Utility::copy(Containers::arrayView(reinterpret_cast(info.pCode), info.codeSize), mutableCode); + + /* If the code looks like SPIR-V, patch it. If not, supply the original and + let SwiftShader deal with it. */ + if(Containers::ArrayView spirv = ShaderTools::Implementation::spirvData(mutableCode, mutableCode.size())) { + Implementation::spirvPatchSwiftShaderConflictingMultiEntrypointLocations(spirv); + patchedInfo.pCode = reinterpret_cast(mutableCode.data()); + } + + return createImplementationDefault(device, patchedInfo, callbacks, handle); +} + }} diff --git a/src/Magnum/Vk/Shader.h b/src/Magnum/Vk/Shader.h index ad0a2a83f..a436eba6a 100644 --- a/src/Magnum/Vk/Shader.h +++ b/src/Magnum/Vk/Shader.h @@ -39,6 +39,8 @@ namespace Magnum { namespace Vk { +namespace Implementation { struct DeviceState; } + /** @brief Shader stage @m_since_latest @@ -203,6 +205,11 @@ class MAGNUM_VK_EXPORT Shader { VkShaderModule release(); private: + friend Implementation::DeviceState; + + MAGNUM_VK_LOCAL static VkResult createImplementationDefault(Device& device, const VkShaderModuleCreateInfo& info, const VkAllocationCallbacks* callbacks, VkShaderModule& handle); + MAGNUM_VK_LOCAL static VkResult createImplementationSwiftShaderMultiEntryPointPatching(Device& device, const VkShaderModuleCreateInfo& info, const VkAllocationCallbacks* callbacks, VkShaderModule& handle); + /* Can't be a reference because of the NoCreate constructor */ Device* _device; diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 3effaf840..7cce44722 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -50,7 +50,10 @@ corrade_add_test(VkPixelFormatTest PixelFormatTest.cpp LIBRARIES MagnumVkTestLib corrade_add_test(VkQueueTest QueueTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkRenderPassTest RenderPassTest.cpp LIBRARIES MagnumVkTestLib) + corrade_add_test(VkShaderTest ShaderTest.cpp LIBRARIES MagnumVk) +target_include_directories(VkShaderTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) + corrade_add_test(VkShaderSetTest ShaderSetTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkVertexFormatTest VertexFormatTest.cpp LIBRARIES MagnumVkTestLib) diff --git a/src/Magnum/Vk/Test/DeviceVkTest.cpp b/src/Magnum/Vk/Test/DeviceVkTest.cpp index ca179edf9..a1a67fbf7 100644 --- a/src/Magnum/Vk/Test/DeviceVkTest.cpp +++ b/src/Magnum/Vk/Test/DeviceVkTest.cpp @@ -163,7 +163,8 @@ struct { "Device: {}\n" "Device version: Vulkan {}.{}{}\n" "Using device driver workarounds:\n" - " swiftshader-image-copy-extent-instead-of-layers\n"}, + " swiftshader-image-copy-extent-instead-of-layers\n" + " swiftshader-spirv-multi-entrypoint-conflicting-locations\n"}, /* Shouldn't print anything if quiet output is enabled */ {"quiet", true, Containers::array({"", @@ -171,7 +172,7 @@ struct { ""}, {"disabled workarounds", true, Containers::array({"", - "--magnum-disable-workarounds", "swiftshader-image-copy-extent-instead-of-layers"}), + "--magnum-disable-workarounds", "swiftshader-image-copy-extent-instead-of-layers swiftshader-spirv-multi-entrypoint-conflicting-locations"}), "Device: {}\n" "Device version: Vulkan {}.{}{}\n"} }; diff --git a/src/Magnum/Vk/Test/ShaderTest.cpp b/src/Magnum/Vk/Test/ShaderTest.cpp index a209d08e0..6b236649e 100644 --- a/src/Magnum/Vk/Test/ShaderTest.cpp +++ b/src/Magnum/Vk/Test/ShaderTest.cpp @@ -23,16 +23,27 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include +#include +#include "Magnum/ShaderTools/Implementation/spirv.h" #include "Magnum/Vk/ShaderCreateInfo.h" +#include "Magnum/Vk/Implementation/spirvPatching.h" + +#include "configure.h" namespace Magnum { namespace Vk { namespace Test { namespace { struct ShaderTest: TestSuite::Tester { explicit ShaderTest(); + void spirvPatchSwiftShaderConflictingMultiEntrypointLocations(); + void spirvPatchSwiftShaderConflictingMultiEntrypointLocationsTooManyEntrypoints(); + void spirvPatchSwiftShaderConflictingMultiEntrypointLocationsOnlyOneEntrypoint(); + void spirvPatchSwiftShaderConflictingMultiEntrypointLocationsNoInterfaces(); + void createInfoConstruct(); void createInfoConstructTransferOwnership(); void createInfoConstructNoInit(); @@ -45,7 +56,12 @@ struct ShaderTest: TestSuite::Tester { }; ShaderTest::ShaderTest() { - addTests({&ShaderTest::createInfoConstruct, + addTests({&ShaderTest::spirvPatchSwiftShaderConflictingMultiEntrypointLocations, + &ShaderTest::spirvPatchSwiftShaderConflictingMultiEntrypointLocationsTooManyEntrypoints, + &ShaderTest::spirvPatchSwiftShaderConflictingMultiEntrypointLocationsOnlyOneEntrypoint, + &ShaderTest::spirvPatchSwiftShaderConflictingMultiEntrypointLocationsNoInterfaces, + + &ShaderTest::createInfoConstruct, &ShaderTest::createInfoConstructTransferOwnership, &ShaderTest::createInfoConstructNoInit, &ShaderTest::createInfoConstructFromVk, @@ -56,6 +72,103 @@ ShaderTest::ShaderTest() { &ShaderTest::constructCopy}); } +void ShaderTest::spirvPatchSwiftShaderConflictingMultiEntrypointLocations() { + Containers::Array data = Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "ShaderTestFiles/vert-frag.spv")); + + /* The file is a full SPIR-V, strip the header first */ + const Containers::ArrayView spirv = ShaderTools::Implementation::spirvData(data, data.size()); + CORRADE_VERIFY(spirv); + + Containers::ArrayView view = spirv; + Containers::Optional vert = ShaderTools::Implementation::spirvNextEntrypoint(view); + CORRADE_VERIFY(vert); + CORRADE_COMPARE(vert->name, "ver"); + CORRADE_COMPARE(vert->interfaces.size(), 7); + + Containers::Optional frag = ShaderTools::Implementation::spirvNextEntrypoint(view); + CORRADE_VERIFY(frag); + CORRADE_COMPARE(frag->name, "fra"); + CORRADE_COMPARE(frag->interfaces.size(), 5); + + ShaderTools::Implementation::SpirvEntrypointInterface vertInterfaces[7]{}; + ShaderTools::Implementation::spirvEntrypointInterface(view, *vert, vertInterfaces); + CORRADE_VERIFY(vertInterfaces[0].location); /* position */ + CORRADE_COMPARE(*vertInterfaces[0].location, 0); + CORRADE_VERIFY(vertInterfaces[1].location); /* color */ + CORRADE_COMPARE(*vertInterfaces[1].location, 1); + CORRADE_VERIFY(vertInterfaces[3].location); /* interpolatedColorOut */ + CORRADE_COMPARE(*vertInterfaces[3].location, 0); + CORRADE_VERIFY(vertInterfaces[4].location); /* interpolatedTexCoordsOut */ + CORRADE_COMPARE(*vertInterfaces[4].location, 1); + CORRADE_VERIFY(vertInterfaces[5].location); /* interpolatedNormalOut */ + CORRADE_COMPARE(*vertInterfaces[5].location, 2); + CORRADE_VERIFY(vertInterfaces[6].location); /* unused */ + CORRADE_COMPARE(*vertInterfaces[6].location, 3); + + ShaderTools::Implementation::SpirvEntrypointInterface fragInterfaces[5]{}; + ShaderTools::Implementation::spirvEntrypointInterface(view, *frag, fragInterfaces); + CORRADE_VERIFY(fragInterfaces[0].location); /* interpolatedColorIn */ + CORRADE_COMPARE(*fragInterfaces[0].location, 0); + CORRADE_VERIFY(fragInterfaces[1].location); /* interpolatedTexCoordsIn */ + CORRADE_COMPARE(*fragInterfaces[1].location, 1); + CORRADE_VERIFY(fragInterfaces[2].location); /* interpolatedNormalIn */ + CORRADE_COMPARE(*fragInterfaces[2].location, 2); + CORRADE_VERIFY(fragInterfaces[3].location); /* fragmentColor */ + CORRADE_COMPARE(*fragInterfaces[3].location, 0); + CORRADE_VERIFY(fragInterfaces[4].location); /* weight */ + CORRADE_COMPARE(*fragInterfaces[4].location, 1); + + CORRADE_VERIFY(Implementation::spirvPatchSwiftShaderConflictingMultiEntrypointLocations(spirv)); + CORRADE_COMPARE(*vertInterfaces[0].location, 0); + CORRADE_COMPARE(*vertInterfaces[1].location, 1); + CORRADE_COMPARE(*vertInterfaces[3].location, 4); /* changed */ + CORRADE_COMPARE(*vertInterfaces[4].location, 5); /* changed */ + CORRADE_COMPARE(*vertInterfaces[5].location, 2); /* kept, no conflict */ + CORRADE_COMPARE(*vertInterfaces[6].location, 3); /* kept, no conflict */ + + CORRADE_COMPARE(*fragInterfaces[0].location, 4); /* changed */ + CORRADE_COMPARE(*fragInterfaces[1].location, 5); /* changed */ + CORRADE_COMPARE(*fragInterfaces[2].location, 2); /* kept, no conflict */ + CORRADE_COMPARE(*fragInterfaces[3].location, 0); + CORRADE_COMPARE(*fragInterfaces[4].location, 1); +} + +UnsignedInt op(UnsignedInt length, SpvOp op) { + return length << 16 | op; +} + +void ShaderTest::spirvPatchSwiftShaderConflictingMultiEntrypointLocationsTooManyEntrypoints() { + UnsignedInt data[] { + op(6, SpvOpEntryPoint), SpvExecutionModelVertex, 1, '\0', 4, 5, + op(4, SpvOpEntryPoint), SpvExecutionModelFragment, 2, '\0', + op(6, SpvOpEntryPoint), SpvExecutionModelFragment, 3, '\0', 7, 8, + }; + + /* There's three entrypoints, skip to avoid breaking something we don't + understand */ + CORRADE_VERIFY(!Implementation::spirvPatchSwiftShaderConflictingMultiEntrypointLocations(data)); +} + +void ShaderTest::spirvPatchSwiftShaderConflictingMultiEntrypointLocationsOnlyOneEntrypoint() { + UnsignedInt data[] { + op(6, SpvOpEntryPoint), SpvExecutionModelVertex, 1, '\0', 4, 5, + }; + + /* There's just one entrypoint, the bug doesn't affect this case */ + CORRADE_VERIFY(!Implementation::spirvPatchSwiftShaderConflictingMultiEntrypointLocations(data)); +} + +void ShaderTest::spirvPatchSwiftShaderConflictingMultiEntrypointLocationsNoInterfaces() { + UnsignedInt data[] { + op(4, SpvOpEntryPoint), SpvExecutionModelVertex, 1, '\0', + op(4, SpvOpEntryPoint), SpvExecutionModelFragment, 2, '\0' + }; + + /* There's no interfaces and thus nothing to do, but the function should + succeed and not crash */ + CORRADE_VERIFY(Implementation::spirvPatchSwiftShaderConflictingMultiEntrypointLocations(data)); +} + void ShaderTest::createInfoConstruct() { const UnsignedInt data[] { 0xdead, 0xbee5, 0xbaba }; diff --git a/src/Magnum/Vk/Test/ShaderTestFiles/convert.sh b/src/Magnum/Vk/Test/ShaderTestFiles/convert.sh new file mode 100755 index 000000000..0e65958ac --- /dev/null +++ b/src/Magnum/Vk/Test/ShaderTestFiles/convert.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +for i in $(ls *.spvasm); do + magnum-shaderconverter $i ${i%asm} +done diff --git a/src/Magnum/Vk/Test/ShaderTestFiles/vert-frag.spv b/src/Magnum/Vk/Test/ShaderTestFiles/vert-frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..4614c82cffefa0d48181e2382e734ca6526aa543 GIT binary patch literal 676 zcmYk(TT%i+429tcD&mC!FQ7hHRJjZnz&d=81@PedJ>M{G^;Bijf1f0sbkAp>i>iyN zTGV^~zD>=>omI_8rVe%YzyAF>C89?4GX5%d9ZR9Qi4Cua0;5WkP@61$Ga za`9$jvG{RfGw;Js@iaMK6Pw2#{W;#;>e8`(?a^Q2#rC@STK+a`vHFmY&C~LCS&Qv; q^SAuhDOTU|vDxG|7TdG?ZuQ@?rnh|TeeB7{?xJV!+t~fMZ~O;f9}so` literal 0 HcmV?d00001 diff --git a/src/Magnum/Vk/Test/ShaderTestFiles/vert-frag.spvasm b/src/Magnum/Vk/Test/ShaderTestFiles/vert-frag.spvasm new file mode 100644 index 000000000..22baddfa1 --- /dev/null +++ b/src/Magnum/Vk/Test/ShaderTestFiles/vert-frag.spvasm @@ -0,0 +1,53 @@ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %ver "ver" %position %color %gl_Position %interpolatedColorOut %interpolatedTexCoordsOut %interpolatedNormalOut %unused + OpEntryPoint Fragment %fra "fra" %interpolatedColorIn %interpolatedTexCoordsIn %interpolatedNormalIn %fragmentColor %weight + + OpDecorate %gl_Position BuiltIn Position + OpDecorate %position Location 0 + OpDecorate %color Location 1 + OpDecorate %fragmentColor Location 0 + OpDecorate %weight Location 1 + + OpDecorate %interpolatedColorOut Location 0 + OpDecorate %interpolatedColorIn Location 0 + + OpDecorate %interpolatedTexCoordsOut Location 1 + OpDecorate %interpolatedTexCoordsIn Location 1 + + OpDecorate %interpolatedNormalOut Location 2 + OpDecorate %interpolatedNormalIn Location 2 + + OpDecorate %unused Location 3 + + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %v4float = OpTypeVector %float 4 + +%_ptr_Output_float = OpTypePointer Output %float + %weight = OpVariable %_ptr_Output_float Output + +%_ptr_Input_v2float = OpTypePointer Input %v2float +%interpolatedTexCoordsIn = OpVariable %_ptr_Input_v2float Input + +%_ptr_Output_v2float = OpTypePointer Output %v2float +%interpolatedTexCoordsOut = OpVariable %_ptr_Input_v2float Output + +%_ptr_Input_v3float = OpTypePointer Input %v3float +%interpolatedNormalIn = OpVariable %_ptr_Input_v3float Input + +%_ptr_Output_v3float = OpTypePointer Output %v3float +%interpolatedNormalOut = OpVariable %_ptr_Input_v3float Output + +%_ptr_Input_v4float = OpTypePointer Input %v4float + %position = OpVariable %_ptr_Input_v4float Input + %color = OpVariable %_ptr_Input_v4float Input +%interpolatedColorIn = OpVariable %_ptr_Input_v4float Input + +%_ptr_Output_v4float = OpTypePointer Output %v4float +%interpolatedColorOut = OpVariable %_ptr_Output_v4float Output +%gl_Position = OpVariable %_ptr_Output_v4float Output +%fragmentColor = OpVariable %_ptr_Output_v4float Output + + %unused = OpVariable %_ptr_Output_float Output From d591c8d003ec8f3590e92819c459ad18f3023a58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 12 Feb 2021 17:04:16 +0100 Subject: [PATCH 32/44] ShaderTools: GCC 4.8, come on, it's Friday. --- src/Magnum/ShaderTools/Test/SpirvTest.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Magnum/ShaderTools/Test/SpirvTest.cpp b/src/Magnum/ShaderTools/Test/SpirvTest.cpp index d2eae7a2d..fb5fe774f 100644 --- a/src/Magnum/ShaderTools/Test/SpirvTest.cpp +++ b/src/Magnum/ShaderTools/Test/SpirvTest.cpp @@ -70,8 +70,9 @@ const struct { Containers::ArrayView data; } InvalidData[] { {"empty", {}}, - {"just the header", JustHeader}, - {"invalid magic", InvalidMagic}, + /* GCC 4.8 needs the ArrayView conversion explicit */ + {"just the header", Containers::arrayView(JustHeader)}, + {"invalid magic", Containers::arrayView(InvalidMagic)}, {"size not divisible by four", Containers::arrayCast(Data).except(1)} }; From 08893165f2607687c873b8fc7113bd2c3125d7a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 12 Feb 2021 22:26:23 +0100 Subject: [PATCH 33/44] GL: TextureFormat::SR[G]8 aren't supposed to be available on ES2. --- doc/changelog.dox | 4 ++++ src/Magnum/GL/AbstractTexture.cpp | 8 +++++--- src/Magnum/GL/TextureFormat.cpp | 4 +++- src/Magnum/GL/TextureFormat.h | 10 +++++++--- src/MagnumExternal/OpenGL/GLES2/extensions.txt | 2 -- src/MagnumExternal/OpenGL/GLES2/flextGL.h | 8 -------- .../OpenGL/GLES2/flextGLWindowsDesktop.h | 8 -------- 7 files changed, 19 insertions(+), 25 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index b5249d819..c345876d0 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -435,6 +435,10 @@ See also: @ref Corrade::Containers::ArrayView are now removed. This should have a significant positive effect on compile times of code using the @ref GL, @ref Audio, @ref Trade and @ref Text libraries +- @ref GL::TextureFormat::SR8 and @ref GL::TextureFormat::SRG8 were present + on ES2 builds by mistake --- the @gl_extension{EXT,texture_sRGB_R8} and + @gl_extension{EXT,texture_sRGB_RG8} extensions require OpenGL ES 3.0 at + least - @ref SceneGraph::Object::addChild() no longer requires the type constructor to have the last parameter a parent object object pointer, as that was quite limiting. Instead it's calling @ref SceneGraph::Object::setParent() diff --git a/src/Magnum/GL/AbstractTexture.cpp b/src/Magnum/GL/AbstractTexture.cpp index 606424ae3..275035e57 100644 --- a/src/Magnum/GL/AbstractTexture.cpp +++ b/src/Magnum/GL/AbstractTexture.cpp @@ -561,7 +561,7 @@ PixelFormat pixelFormatForInternalFormat(const TextureFormat internalFormat) { #ifndef MAGNUM_TARGET_GLES2 case TextureFormat::R8Snorm: #endif - #ifndef MAGNUM_TARGET_WEBGL + #if !defined(MAGNUM_TARGET_WEBGL) && !defined(MAGNUM_TARGET_GLES2) case TextureFormat::SR8: #endif #ifndef MAGNUM_TARGET_GLES2 @@ -611,7 +611,7 @@ PixelFormat pixelFormatForInternalFormat(const TextureFormat internalFormat) { #ifndef MAGNUM_TARGET_GLES2 case TextureFormat::RG8Snorm: #endif - #ifdef MAGNUM_TARGET_GLES + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_GLES2) case TextureFormat::SRG8: #endif #ifndef MAGNUM_TARGET_GLES2 @@ -929,8 +929,10 @@ PixelType pixelTypeForInternalFormat(const TextureFormat internalFormat) { case TextureFormat::LuminanceAlpha: #endif #ifndef MAGNUM_TARGET_WEBGL + #ifndef MAGNUM_TARGET_GLES2 case TextureFormat::SR8: - #ifdef MAGNUM_TARGET_GLES + #endif + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_GLES2) case TextureFormat::SRG8: #endif #endif diff --git a/src/Magnum/GL/TextureFormat.cpp b/src/Magnum/GL/TextureFormat.cpp index d2f7ab08e..6d43e02e4 100644 --- a/src/Magnum/GL/TextureFormat.cpp +++ b/src/Magnum/GL/TextureFormat.cpp @@ -64,8 +64,10 @@ Debug& operator<<(Debug& debug, const TextureFormat value) { _c(RGBA8) #endif #ifndef MAGNUM_TARGET_WEBGL + #ifndef MAGNUM_TARGET_GLES2 _c(SR8) - #ifdef MAGNUM_TARGET_GLES + #endif + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_GLES2) _c(SRG8) #endif #endif diff --git a/src/Magnum/GL/TextureFormat.h b/src/Magnum/GL/TextureFormat.h index d38bc333c..a9638cf0a 100644 --- a/src/Magnum/GL/TextureFormat.h +++ b/src/Magnum/GL/TextureFormat.h @@ -196,21 +196,25 @@ enum class TextureFormat: GLenum { #endif #ifndef MAGNUM_TARGET_WEBGL + #ifndef MAGNUM_TARGET_GLES2 /** * sRGB-encoded red component, normalized unsigned byte. * @requires_extension Extension @gl_extension{EXT,texture_sRGB_R8} - * @requires_es_extension Extension @gl_extension{EXT,texture_sRGB_R8} + * @requires_es_extension OpenGL ES 3.0 and extension + * @gl_extension{EXT,texture_sRGB_R8} * @requires_gles One- and two-component sRGB texture formats are not * available in WebGL, use @ref TextureFormat::SRGB8 or * @ref TextureFormat::SRGB8Alpha8 * @m_since{2019,10} */ SR8 = GL_SR8_EXT, + #endif - #if defined(MAGNUM_TARGET_GLES) || defined(DOXYGEN_GENERATING_OUTPUT) + #if (defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_GLES2)) || defined(DOXYGEN_GENERATING_OUTPUT) /** * sRGB-encoded red and green component, normalized unsigned byte. - * @requires_es_extension Extension @gl_extension{EXT,texture_sRGB_RG8} + * @requires_es_extension OpenGL ES 3.0 and extension + * @gl_extension{EXT,texture_sRGB_RG8} * @requires_gles One- and two-component sRGB texture formats are not * available in WebGL, use @ref TextureFormat::SRGB8 or * @ref TextureFormat::SRGB8Alpha8 instead. Only diff --git a/src/MagnumExternal/OpenGL/GLES2/extensions.txt b/src/MagnumExternal/OpenGL/GLES2/extensions.txt index a54bd2612..8f8a361d0 100644 --- a/src/MagnumExternal/OpenGL/GLES2/extensions.txt +++ b/src/MagnumExternal/OpenGL/GLES2/extensions.txt @@ -87,8 +87,6 @@ extension EXT_texture_sRGB_decode optional extension EXT_sRGB_write_control optional extension EXT_texture_compression_s3tc optional extension EXT_pvrtc_sRGB optional -extension EXT_texture_sRGB_R8 optional -extension EXT_texture_sRGB_RG8 optional extension EXT_polygon_offset_clamp optional extension EXT_texture_compression_s3tc_srgb optional extension IMG_texture_compression_pvrtc optional diff --git a/src/MagnumExternal/OpenGL/GLES2/flextGL.h b/src/MagnumExternal/OpenGL/GLES2/flextGL.h index 12380c2e8..9e51a4eae 100644 --- a/src/MagnumExternal/OpenGL/GLES2/flextGL.h +++ b/src/MagnumExternal/OpenGL/GLES2/flextGL.h @@ -988,14 +988,6 @@ typedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLen #define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG 0x93F0 #define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG 0x93F1 -/* GL_EXT_texture_sRGB_R8 */ - -#define GL_SR8_EXT 0x8FBD - -/* GL_EXT_texture_sRGB_RG8 */ - -#define GL_SRG8_EXT 0x8FBE - /* GL_EXT_polygon_offset_clamp */ #define GL_POLYGON_OFFSET_CLAMP_EXT 0x8E1B diff --git a/src/MagnumExternal/OpenGL/GLES2/flextGLWindowsDesktop.h b/src/MagnumExternal/OpenGL/GLES2/flextGLWindowsDesktop.h index a58fd626d..2ab6ff922 100644 --- a/src/MagnumExternal/OpenGL/GLES2/flextGLWindowsDesktop.h +++ b/src/MagnumExternal/OpenGL/GLES2/flextGLWindowsDesktop.h @@ -983,14 +983,6 @@ typedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLen #define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG 0x93F0 #define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG 0x93F1 -/* GL_EXT_texture_sRGB_R8 */ - -#define GL_SR8_EXT 0x8FBD - -/* GL_EXT_texture_sRGB_RG8 */ - -#define GL_SRG8_EXT 0x8FBE - /* GL_EXT_polygon_offset_clamp */ #define GL_POLYGON_OFFSET_CLAMP_EXT 0x8E1B From 5fa2696583e8ff4265aefea3575b37bf24354d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 12 Feb 2021 22:27:17 +0100 Subject: [PATCH 34/44] external: and this ES3 extension is in the wrong group. --- .../OpenGL/GLES3/extensions.txt | 2 +- src/MagnumExternal/OpenGL/GLES3/flextGL.h | 22 +++++++++---------- .../OpenGL/GLES3/flextGLWindowsDesktop.h | 22 +++++++++---------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/MagnumExternal/OpenGL/GLES3/extensions.txt b/src/MagnumExternal/OpenGL/GLES3/extensions.txt index 8185431be..99c57a7c3 100644 --- a/src/MagnumExternal/OpenGL/GLES3/extensions.txt +++ b/src/MagnumExternal/OpenGL/GLES3/extensions.txt @@ -17,7 +17,6 @@ extension EXT_texture_border_clamp optional extension EXT_texture_buffer optional extension EXT_texture_cube_map_array optional extension EXT_primitive_bounding_box optional -extension EXT_texture_norm16 optional # extension KHR_texture_compression_astc_ldr optional extension KHR_debug optional extension KHR_blend_equation_advanced optional @@ -57,6 +56,7 @@ extension EXT_sRGB_write_control optional extension EXT_texture_compression_s3tc optional extension EXT_pvrtc_sRGB optional extension EXT_shader_integer_mix optional +extension EXT_texture_norm16 optional extension EXT_texture_sRGB_R8 optional extension EXT_texture_sRGB_RG8 optional extension EXT_polygon_offset_clamp optional diff --git a/src/MagnumExternal/OpenGL/GLES3/flextGL.h b/src/MagnumExternal/OpenGL/GLES3/flextGL.h index 68349f21d..f1a6a212a 100644 --- a/src/MagnumExternal/OpenGL/GLES3/flextGL.h +++ b/src/MagnumExternal/OpenGL/GLES3/flextGL.h @@ -1305,17 +1305,6 @@ typedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLen #define GL_PRIMITIVE_BOUNDING_BOX_EXT 0x92BE -/* GL_EXT_texture_norm16 */ - -#define GL_R16_EXT 0x822A -#define GL_RG16_EXT 0x822C -#define GL_RGBA16_EXT 0x805B -#define GL_RGB16_EXT 0x8054 -#define GL_R16_SNORM_EXT 0x8F98 -#define GL_RG16_SNORM_EXT 0x8F99 -#define GL_RGB16_SNORM_EXT 0x8F9A -#define GL_RGBA16_SNORM_EXT 0x8F9B - /* GL_KHR_debug */ #define GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR 0x8242 @@ -1567,6 +1556,17 @@ typedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLen #define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG 0x93F0 #define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG 0x93F1 +/* GL_EXT_texture_norm16 */ + +#define GL_R16_EXT 0x822A +#define GL_RG16_EXT 0x822C +#define GL_RGBA16_EXT 0x805B +#define GL_RGB16_EXT 0x8054 +#define GL_R16_SNORM_EXT 0x8F98 +#define GL_RG16_SNORM_EXT 0x8F99 +#define GL_RGB16_SNORM_EXT 0x8F9A +#define GL_RGBA16_SNORM_EXT 0x8F9B + /* GL_EXT_texture_sRGB_R8 */ #define GL_SR8_EXT 0x8FBD diff --git a/src/MagnumExternal/OpenGL/GLES3/flextGLWindowsDesktop.h b/src/MagnumExternal/OpenGL/GLES3/flextGLWindowsDesktop.h index e6ec050ee..9ea6b5537 100644 --- a/src/MagnumExternal/OpenGL/GLES3/flextGLWindowsDesktop.h +++ b/src/MagnumExternal/OpenGL/GLES3/flextGLWindowsDesktop.h @@ -1297,17 +1297,6 @@ typedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLen #define GL_PRIMITIVE_BOUNDING_BOX_EXT 0x92BE -/* GL_EXT_texture_norm16 */ - -#define GL_R16_EXT 0x822A -#define GL_RG16_EXT 0x822C -#define GL_RGBA16_EXT 0x805B -#define GL_RGB16_EXT 0x8054 -#define GL_R16_SNORM_EXT 0x8F98 -#define GL_RG16_SNORM_EXT 0x8F99 -#define GL_RGB16_SNORM_EXT 0x8F9A -#define GL_RGBA16_SNORM_EXT 0x8F9B - /* GL_KHR_debug */ #define GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR 0x8242 @@ -1559,6 +1548,17 @@ typedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLen #define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG 0x93F0 #define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG 0x93F1 +/* GL_EXT_texture_norm16 */ + +#define GL_R16_EXT 0x822A +#define GL_RG16_EXT 0x822C +#define GL_RGBA16_EXT 0x805B +#define GL_RGB16_EXT 0x8054 +#define GL_R16_SNORM_EXT 0x8F98 +#define GL_RG16_SNORM_EXT 0x8F99 +#define GL_RGB16_SNORM_EXT 0x8F9A +#define GL_RGBA16_SNORM_EXT 0x8F9B + /* GL_EXT_texture_sRGB_R8 */ #define GL_SR8_EXT 0x8FBD From b0bc3663347513748fdb0902dbd71ef8fe6aa5ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 12 Feb 2021 22:30:50 +0100 Subject: [PATCH 35/44] GL: recognize and load ANGLE multi-draw extensions. Needed a change in flextGL to allow merging in 3rd party gl.xml additions because Chrome is apparently a center of the universe and thus doesn't need to bother upstreaming its extensions, ffs. --- doc/changelog.dox | 13 +++-- doc/opengl-support.dox | 4 ++ src/Magnum/GL/Context.cpp | 6 +++ src/Magnum/GL/Extensions.h | 50 ++++++++++------- .../OpenGL/GLES2/Emscripten/extensions.txt | 7 +++ .../OpenGL/GLES2/extensions.txt | 13 +++++ src/MagnumExternal/OpenGL/GLES2/flextGL.h | 32 +++++++++++ .../OpenGL/GLES2/flextGLEmscripten.h | 14 +++++ .../OpenGL/GLES2/flextGLPlatform.cpp | 13 +++++ .../OpenGL/GLES2/flextGLPlatformIOS.cpp | 26 +++++++++ .../GLES2/flextGLPlatformWindowsDesktop.cpp | 13 +++++ .../OpenGL/GLES2/flextGLWindowsDesktop.h | 32 +++++++++++ .../OpenGL/GLES3/Emscripten/extensions.txt | 8 ++- .../OpenGL/GLES3/extensions.txt | 8 +++ src/MagnumExternal/OpenGL/GLES3/flextGL.h | 54 +++++++++++++++++++ .../OpenGL/GLES3/flextGLEmscripten.h | 14 +++++ .../OpenGL/GLES3/flextGLPlatform.cpp | 23 ++++++++ .../OpenGL/GLES3/flextGLPlatformIOS.cpp | 46 ++++++++++++++++ .../GLES3/flextGLPlatformWindowsDesktop.cpp | 23 ++++++++ .../OpenGL/GLES3/flextGLWindowsDesktop.h | 54 +++++++++++++++++++ 20 files changed, 428 insertions(+), 25 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index c345876d0..52d89fe16 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -62,11 +62,14 @@ See also: @webgl_extension{WEBGL,debug_renderer_info} and @webgl_extension{WEBGL,debug_shaders} extensions, no implementation done yet -- Recognizing @webgl_extension{WEBGL,multi_draw}, - @webgl_extension{WEBGL,draw_instanced_base_vertex_base_instance} and - @webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance} - extensions, however there's no Emscripten entrypoints for those yet which - means these can'be implemented right now. +- Recognizing ANGLE GLES and WebGL multi-draw extensions: + - @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + - @m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_base_vertex_base_instance.txt) + - @gl_extension{EXT,draw_elements_base_vertex} + - @gl_extension{OES,draw_elements_base_vertex} + - @webgl_extension{WEBGL,multi_draw} + - @webgl_extension{WEBGL,draw_instanced_base_vertex_base_instance} + - @webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance} - Added a @ref GL::AbstractTexture::target() getter to simplify interaction with raw GL code - Exposed @gl_extension{ARB,buffer_storage} as diff --git a/doc/opengl-support.dox b/doc/opengl-support.dox index 9a30dac23..eaee12a1b 100644 --- a/doc/opengl-support.dox +++ b/doc/opengl-support.dox @@ -449,6 +449,8 @@ Extension | Status @gl_extension2{ANGLE,texture_compression_dxt1,ANGLE_texture_compression_dxt} | done @gl_extension2{ANGLE,texture_compression_dxt3,ANGLE_texture_compression_dxt} | done @gl_extension2{ANGLE,texture_compression_dxt5,ANGLE_texture_compression_dxt} | done +@m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted) | | +@m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_base_vertex_base_instance.txt) (unlisted) | | @gl_extension{APPLE,texture_format_BGRA8888} | done @gl_extension{APPLE,clip_distance} | done @gl_extension{ARM,shader_framebuffer_fetch} | missing renderer setup and limit query @@ -470,6 +472,7 @@ Extension | Status @gl_extension{EXT,texture_compression_s3tc} | done @gl_extension{EXT,pvrtc_sRGB} | done @gl_extension{EXT,shader_integer_mix} | done (shading language only) +@gl_extension{EXT,draw_elements_base_vertex} | | @gl_extension{EXT,texture_norm16} | done @gl_extension{EXT,texture_sRGB_R8} | done @gl_extension{EXT,texture_sRGB_RG8} | done @@ -497,6 +500,7 @@ Extension | Status @gl_extension{OES,stencil1} | done @gl_extension{OES,stencil4} | done @gl_extension{OES,texture_float_linear} | done +@gl_extension{OES,draw_elements_base_vertex} | | @gl_extension{OVR,multiview} | | @gl_extension{OVR,multiview2} | | diff --git a/src/Magnum/GL/Context.cpp b/src/Magnum/GL/Context.cpp index b9fb43676..0086d1238 100644 --- a/src/Magnum/GL/Context.cpp +++ b/src/Magnum/GL/Context.cpp @@ -327,6 +327,10 @@ constexpr Extension ExtensionList[]{ #ifndef MAGNUM_TARGET_GLES2 _extension(ANDROID,extension_pack_es31a), #endif + #ifndef MAGNUM_TARGET_GLES2 + _extension(ANGLE,base_vertex_base_instance), + #endif + _extension(ANGLE,multi_draw), _extension(ANGLE,texture_compression_dxt1), _extension(ANGLE,texture_compression_dxt3), _extension(ANGLE,texture_compression_dxt5), @@ -340,6 +344,7 @@ constexpr Extension ExtensionList[]{ _extension(EXT,debug_label), _extension(EXT,debug_marker), _extension(EXT,disjoint_timer_query), + _extension(EXT,draw_elements_base_vertex), _extension(EXT,multi_draw_arrays), _extension(EXT,multisampled_render_to_texture), _extension(EXT,polygon_offset_clamp), @@ -391,6 +396,7 @@ constexpr Extension ExtensionList[]{ #endif _extension(NV,texture_border_clamp), _extension(OES,depth32), + _extension(OES,draw_elements_base_vertex), _extension(OES,mapbuffer), _extension(OES,stencil1), _extension(OES,stencil4), diff --git a/src/Magnum/GL/Extensions.h b/src/Magnum/GL/Extensions.h index bcbc47247..b03b8e944 100644 --- a/src/Magnum/GL/Extensions.h +++ b/src/Magnum/GL/Extensions.h @@ -368,21 +368,29 @@ namespace ANDROID { #ifdef MAGNUM_TARGET_GLES2 _extension( 8,ANGLE,depth_texture, GLES200, GLES300) // #138 #endif + /* Unlike the WEBGL variants, these don't have a number assigned because + Google just doesn't give a shit. These are also not in the official + gl.xml but had to be fetched from an extra file inside ANGLE's repo + (which doesn't even follow the XML schema, so it had to be fixed). */ + _extension( 9,ANGLE,multi_draw, GLES200, None) // #??? + #ifndef MAGNUM_TARGET_GLES2 + _extension( 10,ANGLE,base_vertex_base_instance, GLES310, None) // #??? + #endif } namespace APPLE { #ifdef MAGNUM_TARGET_GLES2 - _extension( 9,APPLE,framebuffer_multisample, GLES200, GLES300) // #78 + _extension( 11,APPLE,framebuffer_multisample, GLES200, GLES300) // #78 #endif - _extension( 10,APPLE,texture_format_BGRA8888, GLES200, None) // #79 + _extension( 12,APPLE,texture_format_BGRA8888, GLES200, None) // #79 #ifdef MAGNUM_TARGET_GLES2 - _extension( 11,APPLE,texture_max_level, GLES200, GLES300) // #80 + _extension( 13,APPLE,texture_max_level, GLES200, GLES300) // #80 #endif - _extension( 12,APPLE,clip_distance, GLES200, None) // #193 + _extension( 14,APPLE,clip_distance, GLES200, None) // #193 } namespace ARM { #ifdef MAGNUM_TARGET_GLES2 - _extension( 13,ARM,rgba8, GLES200, GLES300) // #82 + _extension( 15,ARM,rgba8, GLES200, GLES300) // #82 #endif - _extension( 14,ARM,shader_framebuffer_fetch, GLES200, None) // #165 - _extension( 15,ARM,shader_framebuffer_fetch_depth_stencil, GLES200, None) // #166 + _extension( 16,ARM,shader_framebuffer_fetch, GLES200, None) // #165 + _extension( 17,ARM,shader_framebuffer_fetch_depth_stencil, GLES200, None) // #166 } namespace EXT { _extension( 19,EXT,texture_filter_anisotropic, GLES200, None) // #41 #ifdef MAGNUM_TARGET_GLES2 @@ -450,19 +458,22 @@ namespace ANDROID { _extension( 59,EXT,texture_buffer, GLES310, GLES320) // #183 _extension( 60,EXT,texture_cube_map_array, GLES310, GLES320) // #184 _extension( 61,EXT,primitive_bounding_box, GLES310, GLES320) // #186 - _extension( 62,EXT,texture_norm16, GLES310, None) // #207 - _extension( 63,EXT,texture_sRGB_R8, GLES300, None) // #221 - _extension( 64,EXT,texture_sRGB_RG8, GLES300, None) // #223 #endif - _extension( 65,EXT,polygon_offset_clamp, GLES200, None) // #252 + _extension( 62,EXT,draw_elements_base_vertex, GLES200, None) // #204 + #ifndef MAGNUM_TARGET_GLES2 + _extension( 63,EXT,texture_norm16, GLES310, None) // #207 + _extension( 64,EXT,texture_sRGB_R8, GLES300, None) // #221 + _extension( 65,EXT,texture_sRGB_RG8, GLES300, None) // #223 + #endif + _extension( 66,EXT,polygon_offset_clamp, GLES200, None) // #252 #ifndef MAGNUM_TARGET_GLES2 - _extension( 66,EXT,clip_cull_distance, GLES300, None) // #257 - _extension( 67,EXT,texture_compression_rgtc, GLES300, None) // #286 - _extension( 68,EXT,texture_compression_bptc, GLES300, None) // #287 + _extension( 67,EXT,clip_cull_distance, GLES300, None) // #257 + _extension( 68,EXT,texture_compression_rgtc, GLES300, None) // #286 + _extension( 69,EXT,texture_compression_bptc, GLES300, None) // #287 #endif - _extension( 69,EXT,texture_compression_s3tc_srgb, GLES200, None) // #289 + _extension( 70,EXT,texture_compression_s3tc_srgb, GLES200, None) // #289 } namespace IMG { - _extension( 70,IMG,texture_compression_pvrtc, GLES200, None) // #54 + _extension( 71,IMG,texture_compression_pvrtc, GLES200, None) // #54 } namespace KHR { _extension( 80,KHR,texture_compression_astc_ldr,GLES200, GLES320) // #117 _extension( 81,KHR,texture_compression_astc_hdr,GLES200, None) // #117 @@ -545,14 +556,15 @@ namespace ANDROID { #ifndef MAGNUM_TARGET_GLES2 _extension(147,OES,texture_storage_multisample_2d_array, GLES310, GLES320) // #174 #endif + _extension(148,OES,draw_elements_base_vertex, GLES200, None) // #219 } namespace OVR { #ifndef MAGNUM_TARGET_GLES2 - _extension(148,OVR,multiview, GLES300, None) // #241 - _extension(149,OVR,multiview2, GLES300, None) // #242 + _extension(149,OVR,multiview, GLES300, None) // #241 + _extension(150,OVR,multiview2, GLES300, None) // #242 #endif } namespace MAGNUM { #ifndef MAGNUM_TARGET_GLES2 - _extension(150,MAGNUM,shader_vertex_id, GLES300, GLES300) + _extension(151,MAGNUM,shader_vertex_id, GLES300, GLES300) #endif } #endif diff --git a/src/MagnumExternal/OpenGL/GLES2/Emscripten/extensions.txt b/src/MagnumExternal/OpenGL/GLES2/Emscripten/extensions.txt index 3a3a378f2..a95e95b8c 100644 --- a/src/MagnumExternal/OpenGL/GLES2/Emscripten/extensions.txt +++ b/src/MagnumExternal/OpenGL/GLES2/Emscripten/extensions.txt @@ -4,6 +4,8 @@ version 2.0 es +extraspec https://raw.githubusercontent.com/google/angle/master/scripts/gl_angle_ext.xml + extension ANGLE_instanced_arrays optional extension EXT_color_buffer_half_float optional extension EXT_sRGB optional @@ -37,6 +39,11 @@ extension KHR_texture_compression_astc_ldr optional # barrier extension KHR_blend_equation_advanced optional +# From the gl_angle_ext file, base for WEBGL_multi_draw +extension ANGLE_multi_draw optional +# Base for WEBGL_{multi_,}draw_instanced_base_vertex_base_instance +extension ANGLE_base_vertex_base_instance optional + begin functions blacklist # Not present in WEBGL_blend_equation_advanced_coherent BlendBarrierKHR diff --git a/src/MagnumExternal/OpenGL/GLES2/extensions.txt b/src/MagnumExternal/OpenGL/GLES2/extensions.txt index 8f8a361d0..8dc4dae15 100644 --- a/src/MagnumExternal/OpenGL/GLES2/extensions.txt +++ b/src/MagnumExternal/OpenGL/GLES2/extensions.txt @@ -4,6 +4,8 @@ version 2.0 es +extraspec https://raw.githubusercontent.com/google/angle/master/scripts/gl_angle_ext.xml + extension ANGLE_framebuffer_blit optional extension ANGLE_framebuffer_multisample optional extension ANGLE_instanced_arrays optional @@ -87,6 +89,7 @@ extension EXT_texture_sRGB_decode optional extension EXT_sRGB_write_control optional extension EXT_texture_compression_s3tc optional extension EXT_pvrtc_sRGB optional +extension EXT_draw_elements_base_vertex optional extension EXT_polygon_offset_clamp optional extension EXT_texture_compression_s3tc_srgb optional extension IMG_texture_compression_pvrtc optional @@ -106,6 +109,10 @@ extension OES_depth32 optional extension OES_mapbuffer optional extension OES_stencil1 optional extension OES_stencil4 optional +extension OES_draw_elements_base_vertex optional + +# From the gl_angle_ext file +extension ANGLE_multi_draw optional begin functions blacklist # These are listed in EXT_separate_shader_objects with only the comment @@ -141,6 +148,12 @@ begin functions blacklist TextureStorage1DEXT TextureStorage2DEXT TextureStorage3DEXT + + # EXT/OES_draw_elements_base_vertex lists those are only present in ES3 + DrawRangeElementsBaseVertexEXT + DrawRangeElementsBaseVertexOES + DrawElementsInstancedBaseVertexEXT + DrawElementsInstancedBaseVertexOES end functions blacklist # kate: hl python diff --git a/src/MagnumExternal/OpenGL/GLES2/flextGL.h b/src/MagnumExternal/OpenGL/GLES2/flextGL.h index 9e51a4eae..2bce54cde 100644 --- a/src/MagnumExternal/OpenGL/GLES2/flextGL.h +++ b/src/MagnumExternal/OpenGL/GLES2/flextGL.h @@ -1230,6 +1230,13 @@ struct FlextGL { void(APIENTRY *DrawElementsInstancedANGLE)(GLenum, GLsizei, GLenum, const void *, GLsizei); void(APIENTRY *VertexAttribDivisorANGLE)(GLuint, GLuint); + /* GL_ANGLE_multi_draw */ + + void(APIENTRY *MultiDrawArraysANGLE)(GLenum, const GLint *, const GLsizei *, GLsizei); + void(APIENTRY *MultiDrawArraysInstancedANGLE)(GLenum, const GLint *, const GLsizei *, const GLsizei *, GLsizei); + void(APIENTRY *MultiDrawElementsANGLE)(GLenum, const GLsizei *, GLenum, const GLvoid *const*, GLsizei); + void(APIENTRY *MultiDrawElementsInstancedANGLE)(GLenum, const GLsizei *, GLenum, const GLvoid *const*, const GLsizei*, GLsizei); + /* GL_APPLE_framebuffer_multisample */ void(APIENTRY *RenderbufferStorageMultisampleAPPLE)(GLenum, GLsizei, GLenum, GLsizei, GLsizei); @@ -1273,6 +1280,11 @@ struct FlextGL { void(APIENTRY *EnableiEXT)(GLenum, GLuint); GLboolean(APIENTRY *IsEnablediEXT)(GLenum, GLuint); + /* GL_EXT_draw_elements_base_vertex */ + + void(APIENTRY *DrawElementsBaseVertexEXT)(GLenum, GLsizei, GLenum, const void *, GLint); + void(APIENTRY *MultiDrawElementsBaseVertexEXT)(GLenum, const GLsizei *, GLenum, const void *const*, GLsizei, const GLint *); + /* GL_EXT_instanced_arrays */ void(APIENTRY *DrawArraysInstancedEXT)(GLenum, GLint, GLsizei, GLsizei); @@ -1425,6 +1437,10 @@ struct FlextGL { void(APIENTRY *NamedFramebufferSampleLocationsfvNV)(GLuint, GLuint, GLsizei, const GLfloat *); void(APIENTRY *ResolveDepthValuesNV)(void); + /* GL_OES_draw_elements_base_vertex */ + + void(APIENTRY *DrawElementsBaseVertexOES)(GLenum, GLsizei, GLenum, const void *, GLint); + /* GL_OES_mapbuffer */ void(APIENTRY *GetBufferPointervOES)(GLenum, GLenum, void **); @@ -1464,6 +1480,13 @@ extern FLEXTGL_EXPORT FlextGL flextGL; #define glDrawElementsInstancedANGLE flextGL.DrawElementsInstancedANGLE #define glVertexAttribDivisorANGLE flextGL.VertexAttribDivisorANGLE +/* GL_ANGLE_multi_draw */ + +#define glMultiDrawArraysANGLE flextGL.MultiDrawArraysANGLE +#define glMultiDrawArraysInstancedANGLE flextGL.MultiDrawArraysInstancedANGLE +#define glMultiDrawElementsANGLE flextGL.MultiDrawElementsANGLE +#define glMultiDrawElementsInstancedANGLE flextGL.MultiDrawElementsInstancedANGLE + /* GL_APPLE_framebuffer_multisample */ #define glRenderbufferStorageMultisampleAPPLE flextGL.RenderbufferStorageMultisampleAPPLE @@ -1507,6 +1530,11 @@ extern FLEXTGL_EXPORT FlextGL flextGL; #define glEnableiEXT flextGL.EnableiEXT #define glIsEnablediEXT flextGL.IsEnablediEXT +/* GL_EXT_draw_elements_base_vertex */ + +#define glDrawElementsBaseVertexEXT flextGL.DrawElementsBaseVertexEXT +#define glMultiDrawElementsBaseVertexEXT flextGL.MultiDrawElementsBaseVertexEXT + /* GL_EXT_instanced_arrays */ #define glDrawArraysInstancedEXT flextGL.DrawArraysInstancedEXT @@ -1659,6 +1687,10 @@ extern FLEXTGL_EXPORT FlextGL flextGL; #define glNamedFramebufferSampleLocationsfvNV flextGL.NamedFramebufferSampleLocationsfvNV #define glResolveDepthValuesNV flextGL.ResolveDepthValuesNV +/* GL_OES_draw_elements_base_vertex */ + +#define glDrawElementsBaseVertexOES flextGL.DrawElementsBaseVertexOES + /* GL_OES_mapbuffer */ #define glGetBufferPointervOES flextGL.GetBufferPointervOES diff --git a/src/MagnumExternal/OpenGL/GLES2/flextGLEmscripten.h b/src/MagnumExternal/OpenGL/GLES2/flextGLEmscripten.h index 3be9d7239..c357e5a57 100644 --- a/src/MagnumExternal/OpenGL/GLES2/flextGLEmscripten.h +++ b/src/MagnumExternal/OpenGL/GLES2/flextGLEmscripten.h @@ -589,12 +589,26 @@ typedef khronos_uint64_t GLuint64; /* Function prototypes */ +/* GL_ANGLE_base_vertex_base_instance */ + +GLAPI void glDrawArraysInstancedBaseInstanceANGLE(GLenum, GLint, GLsizei, GLsizei, GLuint); +GLAPI void glDrawElementsInstancedBaseVertexBaseInstanceANGLE(GLenum, GLsizei, GLenum, const GLvoid *, GLsizei, GLint, GLuint); +GLAPI void glMultiDrawArraysInstancedBaseInstanceANGLE(GLenum, const GLint *, const GLsizei *, const GLsizei *, const GLuint *, GLsizei); +GLAPI void glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE(GLenum, const GLsizei *, GLenum, const GLvoid *const*, const GLsizei *, const GLint *, const GLuint *, GLsizei); + /* GL_ANGLE_instanced_arrays */ GLAPI void glDrawArraysInstancedANGLE(GLenum, GLint, GLsizei, GLsizei); GLAPI void glDrawElementsInstancedANGLE(GLenum, GLsizei, GLenum, const void *, GLsizei); GLAPI void glVertexAttribDivisorANGLE(GLuint, GLuint); +/* GL_ANGLE_multi_draw */ + +GLAPI void glMultiDrawArraysANGLE(GLenum, const GLint *, const GLsizei *, GLsizei); +GLAPI void glMultiDrawArraysInstancedANGLE(GLenum, const GLint *, const GLsizei *, const GLsizei *, GLsizei); +GLAPI void glMultiDrawElementsANGLE(GLenum, const GLsizei *, GLenum, const GLvoid *const*, GLsizei); +GLAPI void glMultiDrawElementsInstancedANGLE(GLenum, const GLsizei *, GLenum, const GLvoid *const*, const GLsizei*, GLsizei); + /* GL_ES_VERSION_2_0 */ GLAPI void glActiveTexture(GLenum); diff --git a/src/MagnumExternal/OpenGL/GLES2/flextGLPlatform.cpp b/src/MagnumExternal/OpenGL/GLES2/flextGLPlatform.cpp index ea270afe8..47357d686 100644 --- a/src/MagnumExternal/OpenGL/GLES2/flextGLPlatform.cpp +++ b/src/MagnumExternal/OpenGL/GLES2/flextGLPlatform.cpp @@ -44,6 +44,12 @@ void flextGLInit(Magnum::GL::Context&) { flextGL.DrawElementsInstancedANGLE = reinterpret_cast(loader.load("glDrawElementsInstancedANGLE")); flextGL.VertexAttribDivisorANGLE = reinterpret_cast(loader.load("glVertexAttribDivisorANGLE")); + /* GL_ANGLE_multi_draw */ + flextGL.MultiDrawArraysANGLE = reinterpret_cast(loader.load("glMultiDrawArraysANGLE")); + flextGL.MultiDrawArraysInstancedANGLE = reinterpret_cast(loader.load("glMultiDrawArraysInstancedANGLE")); + flextGL.MultiDrawElementsANGLE = reinterpret_cast(loader.load("glMultiDrawElementsANGLE")); + flextGL.MultiDrawElementsInstancedANGLE = reinterpret_cast(loader.load("glMultiDrawElementsInstancedANGLE")); + /* GL_APPLE_framebuffer_multisample */ flextGL.RenderbufferStorageMultisampleAPPLE = reinterpret_cast(loader.load("glRenderbufferStorageMultisampleAPPLE")); flextGL.ResolveMultisampleFramebufferAPPLE = reinterpret_cast(loader.load("glResolveMultisampleFramebufferAPPLE")); @@ -80,6 +86,10 @@ void flextGLInit(Magnum::GL::Context&) { flextGL.EnableiEXT = reinterpret_cast(loader.load("glEnableiEXT")); flextGL.IsEnablediEXT = reinterpret_cast(loader.load("glIsEnablediEXT")); + /* GL_EXT_draw_elements_base_vertex */ + flextGL.DrawElementsBaseVertexEXT = reinterpret_cast(loader.load("glDrawElementsBaseVertexEXT")); + flextGL.MultiDrawElementsBaseVertexEXT = reinterpret_cast(loader.load("glMultiDrawElementsBaseVertexEXT")); + /* GL_EXT_instanced_arrays */ flextGL.DrawArraysInstancedEXT = reinterpret_cast(loader.load("glDrawArraysInstancedEXT")); flextGL.DrawElementsInstancedEXT = reinterpret_cast(loader.load("glDrawElementsInstancedEXT")); @@ -211,6 +221,9 @@ void flextGLInit(Magnum::GL::Context&) { flextGL.NamedFramebufferSampleLocationsfvNV = reinterpret_cast(loader.load("glNamedFramebufferSampleLocationsfvNV")); flextGL.ResolveDepthValuesNV = reinterpret_cast(loader.load("glResolveDepthValuesNV")); + /* GL_OES_draw_elements_base_vertex */ + flextGL.DrawElementsBaseVertexOES = reinterpret_cast(loader.load("glDrawElementsBaseVertexOES")); + /* GL_OES_mapbuffer */ flextGL.GetBufferPointervOES = reinterpret_cast(loader.load("glGetBufferPointervOES")); flextGL.MapBufferOES = reinterpret_cast(loader.load("glMapBufferOES")); diff --git a/src/MagnumExternal/OpenGL/GLES2/flextGLPlatformIOS.cpp b/src/MagnumExternal/OpenGL/GLES2/flextGLPlatformIOS.cpp index c31963cfc..34f55bcfd 100644 --- a/src/MagnumExternal/OpenGL/GLES2/flextGLPlatformIOS.cpp +++ b/src/MagnumExternal/OpenGL/GLES2/flextGLPlatformIOS.cpp @@ -30,6 +30,10 @@ #undef glDrawArraysInstancedANGLE #undef glDrawElementsInstancedANGLE #undef glVertexAttribDivisorANGLE +#undef glMultiDrawArraysANGLE +#undef glMultiDrawArraysInstancedANGLE +#undef glMultiDrawElementsANGLE +#undef glMultiDrawElementsInstancedANGLE #undef glRenderbufferStorageMultisampleAPPLE #undef glResolveMultisampleFramebufferAPPLE #undef glGetObjectLabelEXT @@ -52,6 +56,8 @@ #undef glDisableiEXT #undef glEnableiEXT #undef glIsEnablediEXT +#undef glDrawElementsBaseVertexEXT +#undef glMultiDrawElementsBaseVertexEXT #undef glDrawArraysInstancedEXT #undef glDrawElementsInstancedEXT #undef glVertexAttribDivisorEXT @@ -141,6 +147,7 @@ #undef glFramebufferSampleLocationsfvNV #undef glNamedFramebufferSampleLocationsfvNV #undef glResolveDepthValuesNV +#undef glDrawElementsBaseVertexOES #undef glGetBufferPointervOES #undef glMapBufferOES #undef glUnmapBufferOES @@ -179,6 +186,14 @@ void flextGLInit(Magnum::GL::Context&) { flextGL.VertexAttribDivisorANGLE = reinterpret_cast(glVertexAttribDivisorANGLE); #endif + /* GL_ANGLE_multi_draw */ + #if GL_ANGLE_multi_draw + flextGL.MultiDrawArraysANGLE = reinterpret_cast(glMultiDrawArraysANGLE); + flextGL.MultiDrawArraysInstancedANGLE = reinterpret_cast(glMultiDrawArraysInstancedANGLE); + flextGL.MultiDrawElementsANGLE = reinterpret_cast(glMultiDrawElementsANGLE); + flextGL.MultiDrawElementsInstancedANGLE = reinterpret_cast(glMultiDrawElementsInstancedANGLE); + #endif + /* GL_APPLE_framebuffer_multisample */ #if GL_APPLE_framebuffer_multisample flextGL.RenderbufferStorageMultisampleAPPLE = reinterpret_cast(glRenderbufferStorageMultisampleAPPLE); @@ -229,6 +244,12 @@ void flextGLInit(Magnum::GL::Context&) { flextGL.IsEnablediEXT = reinterpret_cast(glIsEnablediEXT); #endif + /* GL_EXT_draw_elements_base_vertex */ + #if GL_EXT_draw_elements_base_vertex + flextGL.DrawElementsBaseVertexEXT = reinterpret_cast(glDrawElementsBaseVertexEXT); + flextGL.MultiDrawElementsBaseVertexEXT = reinterpret_cast(glMultiDrawElementsBaseVertexEXT); + #endif + /* GL_EXT_instanced_arrays */ #if GL_EXT_instanced_arrays flextGL.DrawArraysInstancedEXT = reinterpret_cast(glDrawArraysInstancedEXT); @@ -402,6 +423,11 @@ void flextGLInit(Magnum::GL::Context&) { flextGL.ResolveDepthValuesNV = reinterpret_cast(glResolveDepthValuesNV); #endif + /* GL_OES_draw_elements_base_vertex */ + #if GL_OES_draw_elements_base_vertex + flextGL.DrawElementsBaseVertexOES = reinterpret_cast(glDrawElementsBaseVertexOES); + #endif + /* GL_OES_mapbuffer */ #if GL_OES_mapbuffer flextGL.GetBufferPointervOES = reinterpret_cast(glGetBufferPointervOES); diff --git a/src/MagnumExternal/OpenGL/GLES2/flextGLPlatformWindowsDesktop.cpp b/src/MagnumExternal/OpenGL/GLES2/flextGLPlatformWindowsDesktop.cpp index ab63b09d4..9f73452de 100644 --- a/src/MagnumExternal/OpenGL/GLES2/flextGLPlatformWindowsDesktop.cpp +++ b/src/MagnumExternal/OpenGL/GLES2/flextGLPlatformWindowsDesktop.cpp @@ -44,6 +44,12 @@ void flextGLInit(Magnum::GL::Context&) { flextGL.DrawElementsInstancedANGLE = reinterpret_cast(loader.load("glDrawElementsInstancedANGLE")); flextGL.VertexAttribDivisorANGLE = reinterpret_cast(loader.load("glVertexAttribDivisorANGLE")); + /* GL_ANGLE_multi_draw */ + flextGL.MultiDrawArraysANGLE = reinterpret_cast(loader.load("glMultiDrawArraysANGLE")); + flextGL.MultiDrawArraysInstancedANGLE = reinterpret_cast(loader.load("glMultiDrawArraysInstancedANGLE")); + flextGL.MultiDrawElementsANGLE = reinterpret_cast(loader.load("glMultiDrawElementsANGLE")); + flextGL.MultiDrawElementsInstancedANGLE = reinterpret_cast(loader.load("glMultiDrawElementsInstancedANGLE")); + /* GL_APPLE_framebuffer_multisample */ flextGL.RenderbufferStorageMultisampleAPPLE = reinterpret_cast(loader.load("glRenderbufferStorageMultisampleAPPLE")); flextGL.ResolveMultisampleFramebufferAPPLE = reinterpret_cast(loader.load("glResolveMultisampleFramebufferAPPLE")); @@ -179,6 +185,10 @@ void flextGLInit(Magnum::GL::Context&) { flextGL.EnableiEXT = reinterpret_cast(loader.load("glEnableiEXT")); flextGL.IsEnablediEXT = reinterpret_cast(loader.load("glIsEnablediEXT")); + /* GL_EXT_draw_elements_base_vertex */ + flextGL.DrawElementsBaseVertexEXT = reinterpret_cast(loader.load("glDrawElementsBaseVertexEXT")); + flextGL.MultiDrawElementsBaseVertexEXT = reinterpret_cast(loader.load("glMultiDrawElementsBaseVertexEXT")); + /* GL_EXT_instanced_arrays */ flextGL.DrawArraysInstancedEXT = reinterpret_cast(loader.load("glDrawArraysInstancedEXT")); flextGL.DrawElementsInstancedEXT = reinterpret_cast(loader.load("glDrawElementsInstancedEXT")); @@ -310,6 +320,9 @@ void flextGLInit(Magnum::GL::Context&) { flextGL.NamedFramebufferSampleLocationsfvNV = reinterpret_cast(loader.load("glNamedFramebufferSampleLocationsfvNV")); flextGL.ResolveDepthValuesNV = reinterpret_cast(loader.load("glResolveDepthValuesNV")); + /* GL_OES_draw_elements_base_vertex */ + flextGL.DrawElementsBaseVertexOES = reinterpret_cast(loader.load("glDrawElementsBaseVertexOES")); + /* GL_OES_mapbuffer */ flextGL.GetBufferPointervOES = reinterpret_cast(loader.load("glGetBufferPointervOES")); flextGL.MapBufferOES = reinterpret_cast(loader.load("glMapBufferOES")); diff --git a/src/MagnumExternal/OpenGL/GLES2/flextGLWindowsDesktop.h b/src/MagnumExternal/OpenGL/GLES2/flextGLWindowsDesktop.h index 2ab6ff922..3eda78f32 100644 --- a/src/MagnumExternal/OpenGL/GLES2/flextGLWindowsDesktop.h +++ b/src/MagnumExternal/OpenGL/GLES2/flextGLWindowsDesktop.h @@ -1129,6 +1129,13 @@ struct FlextGL { void(APIENTRY *DrawElementsInstancedANGLE)(GLenum, GLsizei, GLenum, const void *, GLsizei); void(APIENTRY *VertexAttribDivisorANGLE)(GLuint, GLuint); + /* GL_ANGLE_multi_draw */ + + void(APIENTRY *MultiDrawArraysANGLE)(GLenum, const GLint *, const GLsizei *, GLsizei); + void(APIENTRY *MultiDrawArraysInstancedANGLE)(GLenum, const GLint *, const GLsizei *, const GLsizei *, GLsizei); + void(APIENTRY *MultiDrawElementsANGLE)(GLenum, const GLsizei *, GLenum, const GLvoid *const*, GLsizei); + void(APIENTRY *MultiDrawElementsInstancedANGLE)(GLenum, const GLsizei *, GLenum, const GLvoid *const*, const GLsizei*, GLsizei); + /* GL_APPLE_framebuffer_multisample */ void(APIENTRY *RenderbufferStorageMultisampleAPPLE)(GLenum, GLsizei, GLenum, GLsizei, GLsizei); @@ -1272,6 +1279,11 @@ struct FlextGL { void(APIENTRY *EnableiEXT)(GLenum, GLuint); GLboolean(APIENTRY *IsEnablediEXT)(GLenum, GLuint); + /* GL_EXT_draw_elements_base_vertex */ + + void(APIENTRY *DrawElementsBaseVertexEXT)(GLenum, GLsizei, GLenum, const void *, GLint); + void(APIENTRY *MultiDrawElementsBaseVertexEXT)(GLenum, const GLsizei *, GLenum, const void *const*, GLsizei, const GLint *); + /* GL_EXT_instanced_arrays */ void(APIENTRY *DrawArraysInstancedEXT)(GLenum, GLint, GLsizei, GLsizei); @@ -1424,6 +1436,10 @@ struct FlextGL { void(APIENTRY *NamedFramebufferSampleLocationsfvNV)(GLuint, GLuint, GLsizei, const GLfloat *); void(APIENTRY *ResolveDepthValuesNV)(void); + /* GL_OES_draw_elements_base_vertex */ + + void(APIENTRY *DrawElementsBaseVertexOES)(GLenum, GLsizei, GLenum, const void *, GLint); + /* GL_OES_mapbuffer */ void(APIENTRY *GetBufferPointervOES)(GLenum, GLenum, void **); @@ -1463,6 +1479,13 @@ extern FLEXTGL_EXPORT FlextGL flextGL; #define glDrawElementsInstancedANGLE flextGL.DrawElementsInstancedANGLE #define glVertexAttribDivisorANGLE flextGL.VertexAttribDivisorANGLE +/* GL_ANGLE_multi_draw */ + +#define glMultiDrawArraysANGLE flextGL.MultiDrawArraysANGLE +#define glMultiDrawArraysInstancedANGLE flextGL.MultiDrawArraysInstancedANGLE +#define glMultiDrawElementsANGLE flextGL.MultiDrawElementsANGLE +#define glMultiDrawElementsInstancedANGLE flextGL.MultiDrawElementsInstancedANGLE + /* GL_APPLE_framebuffer_multisample */ #define glRenderbufferStorageMultisampleAPPLE flextGL.RenderbufferStorageMultisampleAPPLE @@ -1606,6 +1629,11 @@ extern FLEXTGL_EXPORT FlextGL flextGL; #define glEnableiEXT flextGL.EnableiEXT #define glIsEnablediEXT flextGL.IsEnablediEXT +/* GL_EXT_draw_elements_base_vertex */ + +#define glDrawElementsBaseVertexEXT flextGL.DrawElementsBaseVertexEXT +#define glMultiDrawElementsBaseVertexEXT flextGL.MultiDrawElementsBaseVertexEXT + /* GL_EXT_instanced_arrays */ #define glDrawArraysInstancedEXT flextGL.DrawArraysInstancedEXT @@ -1758,6 +1786,10 @@ extern FLEXTGL_EXPORT FlextGL flextGL; #define glNamedFramebufferSampleLocationsfvNV flextGL.NamedFramebufferSampleLocationsfvNV #define glResolveDepthValuesNV flextGL.ResolveDepthValuesNV +/* GL_OES_draw_elements_base_vertex */ + +#define glDrawElementsBaseVertexOES flextGL.DrawElementsBaseVertexOES + /* GL_OES_mapbuffer */ #define glGetBufferPointervOES flextGL.GetBufferPointervOES diff --git a/src/MagnumExternal/OpenGL/GLES3/Emscripten/extensions.txt b/src/MagnumExternal/OpenGL/GLES3/Emscripten/extensions.txt index 864728091..b51f38b25 100644 --- a/src/MagnumExternal/OpenGL/GLES3/Emscripten/extensions.txt +++ b/src/MagnumExternal/OpenGL/GLES3/Emscripten/extensions.txt @@ -4,6 +4,8 @@ version 3.0 es +extraspec https://raw.githubusercontent.com/google/angle/master/scripts/gl_angle_ext.xml + extension EXT_texture_filter_anisotropic optional # It's actually EXT_disjoint_timer_query_webgl2, but that's not known to gl.xml extension EXT_disjoint_timer_query optional @@ -24,7 +26,11 @@ extension KHR_texture_compression_astc_ldr optional # WEBGL_blend_equation_advanced_coherent includes just the enums but not the # barrier -extension KHR_blend_equation_advanced optional +extension KHR_blend_equation_advanced optional + +# From the gl_angle_ext file, base for WEBGL_multi_draw etc +extension ANGLE_multi_draw optional +extension ANGLE_base_vertex_base_instance optional begin functions blacklist # Not present in WEBGL_blend_equation_advanced_coherent diff --git a/src/MagnumExternal/OpenGL/GLES3/extensions.txt b/src/MagnumExternal/OpenGL/GLES3/extensions.txt index 99c57a7c3..9183fe213 100644 --- a/src/MagnumExternal/OpenGL/GLES3/extensions.txt +++ b/src/MagnumExternal/OpenGL/GLES3/extensions.txt @@ -5,6 +5,8 @@ version 3.2 es +extraspec https://raw.githubusercontent.com/google/angle/master/scripts/gl_angle_ext.xml + extension EXT_color_buffer_half_float optional extension EXT_color_buffer_float optional extension EXT_copy_image optional @@ -56,6 +58,7 @@ extension EXT_sRGB_write_control optional extension EXT_texture_compression_s3tc optional extension EXT_pvrtc_sRGB optional extension EXT_shader_integer_mix optional +extension EXT_draw_elements_base_vertex optional extension EXT_texture_norm16 optional extension EXT_texture_sRGB_R8 optional extension EXT_texture_sRGB_RG8 optional @@ -83,7 +86,12 @@ extension OES_depth32 optional extension OES_mapbuffer optional extension OES_stencil1 optional extension OES_stencil4 optional +extension OES_draw_elements_base_vertex optional extension OVR_multiview optional extension OVR_multiview2 optional +# From the gl_angle_ext file +extension ANGLE_multi_draw optional +extension ANGLE_base_vertex_base_instance optional + # kate: hl python diff --git a/src/MagnumExternal/OpenGL/GLES3/flextGL.h b/src/MagnumExternal/OpenGL/GLES3/flextGL.h index f1a6a212a..86428b761 100644 --- a/src/MagnumExternal/OpenGL/GLES3/flextGL.h +++ b/src/MagnumExternal/OpenGL/GLES3/flextGL.h @@ -1945,6 +1945,20 @@ GLAPI FLEXTGL_EXPORT void APIENTRY glWaitSync(GLsync, GLbitfield, GLuint64); less symbols is exported, saving on binary size. */ struct FlextGL { + /* GL_ANGLE_base_vertex_base_instance */ + + void(APIENTRY *DrawArraysInstancedBaseInstanceANGLE)(GLenum, GLint, GLsizei, GLsizei, GLuint); + void(APIENTRY *DrawElementsInstancedBaseVertexBaseInstanceANGLE)(GLenum, GLsizei, GLenum, const GLvoid *, GLsizei, GLint, GLuint); + void(APIENTRY *MultiDrawArraysInstancedBaseInstanceANGLE)(GLenum, const GLint *, const GLsizei *, const GLsizei *, const GLuint *, GLsizei); + void(APIENTRY *MultiDrawElementsInstancedBaseVertexBaseInstanceANGLE)(GLenum, const GLsizei *, GLenum, const GLvoid *const*, const GLsizei *, const GLint *, const GLuint *, GLsizei); + + /* GL_ANGLE_multi_draw */ + + void(APIENTRY *MultiDrawArraysANGLE)(GLenum, const GLint *, const GLsizei *, GLsizei); + void(APIENTRY *MultiDrawArraysInstancedANGLE)(GLenum, const GLint *, const GLsizei *, const GLsizei *, GLsizei); + void(APIENTRY *MultiDrawElementsANGLE)(GLenum, const GLsizei *, GLenum, const GLvoid *const*, GLsizei); + void(APIENTRY *MultiDrawElementsInstancedANGLE)(GLenum, const GLsizei *, GLenum, const GLvoid *const*, const GLsizei*, GLsizei); + /* GL_ES_VERSION_3_1 */ void(APIENTRY *ActiveShaderProgram)(GLuint, GLuint); @@ -2104,6 +2118,13 @@ struct FlextGL { void(APIENTRY *EnableiEXT)(GLenum, GLuint); GLboolean(APIENTRY *IsEnablediEXT)(GLenum, GLuint); + /* GL_EXT_draw_elements_base_vertex */ + + void(APIENTRY *DrawElementsBaseVertexEXT)(GLenum, GLsizei, GLenum, const void *, GLint); + void(APIENTRY *DrawElementsInstancedBaseVertexEXT)(GLenum, GLsizei, GLenum, const void *, GLsizei, GLint); + void(APIENTRY *DrawRangeElementsBaseVertexEXT)(GLenum, GLuint, GLuint, GLsizei, GLenum, const void *, GLint); + void(APIENTRY *MultiDrawElementsBaseVertexEXT)(GLenum, const GLsizei *, GLenum, const void *const*, GLsizei, const GLint *); + /* GL_EXT_geometry_shader */ void(APIENTRY *FramebufferTextureEXT)(GLenum, GLenum, GLuint, GLint); @@ -2236,6 +2257,12 @@ struct FlextGL { void(APIENTRY *NamedFramebufferSampleLocationsfvNV)(GLuint, GLuint, GLsizei, const GLfloat *); void(APIENTRY *ResolveDepthValuesNV)(void); + /* GL_OES_draw_elements_base_vertex */ + + void(APIENTRY *DrawElementsBaseVertexOES)(GLenum, GLsizei, GLenum, const void *, GLint); + void(APIENTRY *DrawElementsInstancedBaseVertexOES)(GLenum, GLsizei, GLenum, const void *, GLsizei, GLint); + void(APIENTRY *DrawRangeElementsBaseVertexOES)(GLenum, GLuint, GLuint, GLsizei, GLenum, const void *, GLint); + /* GL_OES_mapbuffer */ void(APIENTRY *GetBufferPointervOES)(GLenum, GLenum, void **); @@ -2257,6 +2284,20 @@ struct FlextGL { extern FLEXTGL_EXPORT FlextGL flextGL; +/* GL_ANGLE_base_vertex_base_instance */ + +#define glDrawArraysInstancedBaseInstanceANGLE flextGL.DrawArraysInstancedBaseInstanceANGLE +#define glDrawElementsInstancedBaseVertexBaseInstanceANGLE flextGL.DrawElementsInstancedBaseVertexBaseInstanceANGLE +#define glMultiDrawArraysInstancedBaseInstanceANGLE flextGL.MultiDrawArraysInstancedBaseInstanceANGLE +#define glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE flextGL.MultiDrawElementsInstancedBaseVertexBaseInstanceANGLE + +/* GL_ANGLE_multi_draw */ + +#define glMultiDrawArraysANGLE flextGL.MultiDrawArraysANGLE +#define glMultiDrawArraysInstancedANGLE flextGL.MultiDrawArraysInstancedANGLE +#define glMultiDrawElementsANGLE flextGL.MultiDrawElementsANGLE +#define glMultiDrawElementsInstancedANGLE flextGL.MultiDrawElementsInstancedANGLE + /* GL_ES_VERSION_3_1 */ #define glActiveShaderProgram flextGL.ActiveShaderProgram @@ -2416,6 +2457,13 @@ extern FLEXTGL_EXPORT FlextGL flextGL; #define glEnableiEXT flextGL.EnableiEXT #define glIsEnablediEXT flextGL.IsEnablediEXT +/* GL_EXT_draw_elements_base_vertex */ + +#define glDrawElementsBaseVertexEXT flextGL.DrawElementsBaseVertexEXT +#define glDrawElementsInstancedBaseVertexEXT flextGL.DrawElementsInstancedBaseVertexEXT +#define glDrawRangeElementsBaseVertexEXT flextGL.DrawRangeElementsBaseVertexEXT +#define glMultiDrawElementsBaseVertexEXT flextGL.MultiDrawElementsBaseVertexEXT + /* GL_EXT_geometry_shader */ #define glFramebufferTextureEXT flextGL.FramebufferTextureEXT @@ -2548,6 +2596,12 @@ extern FLEXTGL_EXPORT FlextGL flextGL; #define glNamedFramebufferSampleLocationsfvNV flextGL.NamedFramebufferSampleLocationsfvNV #define glResolveDepthValuesNV flextGL.ResolveDepthValuesNV +/* GL_OES_draw_elements_base_vertex */ + +#define glDrawElementsBaseVertexOES flextGL.DrawElementsBaseVertexOES +#define glDrawElementsInstancedBaseVertexOES flextGL.DrawElementsInstancedBaseVertexOES +#define glDrawRangeElementsBaseVertexOES flextGL.DrawRangeElementsBaseVertexOES + /* GL_OES_mapbuffer */ #define glGetBufferPointervOES flextGL.GetBufferPointervOES diff --git a/src/MagnumExternal/OpenGL/GLES3/flextGLEmscripten.h b/src/MagnumExternal/OpenGL/GLES3/flextGLEmscripten.h index 56222ee9b..b8f0b7d5f 100644 --- a/src/MagnumExternal/OpenGL/GLES3/flextGLEmscripten.h +++ b/src/MagnumExternal/OpenGL/GLES3/flextGLEmscripten.h @@ -881,6 +881,20 @@ typedef struct __GLsync *GLsync; /* Function prototypes */ +/* GL_ANGLE_base_vertex_base_instance */ + +GLAPI void glDrawArraysInstancedBaseInstanceANGLE(GLenum, GLint, GLsizei, GLsizei, GLuint); +GLAPI void glDrawElementsInstancedBaseVertexBaseInstanceANGLE(GLenum, GLsizei, GLenum, const GLvoid *, GLsizei, GLint, GLuint); +GLAPI void glMultiDrawArraysInstancedBaseInstanceANGLE(GLenum, const GLint *, const GLsizei *, const GLsizei *, const GLuint *, GLsizei); +GLAPI void glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE(GLenum, const GLsizei *, GLenum, const GLvoid *const*, const GLsizei *, const GLint *, const GLuint *, GLsizei); + +/* GL_ANGLE_multi_draw */ + +GLAPI void glMultiDrawArraysANGLE(GLenum, const GLint *, const GLsizei *, GLsizei); +GLAPI void glMultiDrawArraysInstancedANGLE(GLenum, const GLint *, const GLsizei *, const GLsizei *, GLsizei); +GLAPI void glMultiDrawElementsANGLE(GLenum, const GLsizei *, GLenum, const GLvoid *const*, GLsizei); +GLAPI void glMultiDrawElementsInstancedANGLE(GLenum, const GLsizei *, GLenum, const GLvoid *const*, const GLsizei*, GLsizei); + /* GL_ES_VERSION_2_0 */ GLAPI void glActiveTexture(GLenum); diff --git a/src/MagnumExternal/OpenGL/GLES3/flextGLPlatform.cpp b/src/MagnumExternal/OpenGL/GLES3/flextGLPlatform.cpp index 19f46a724..2bf5ad8b1 100644 --- a/src/MagnumExternal/OpenGL/GLES3/flextGLPlatform.cpp +++ b/src/MagnumExternal/OpenGL/GLES3/flextGLPlatform.cpp @@ -33,6 +33,18 @@ void flextGLInit(Magnum::GL::Context&) { Magnum::Platform::Implementation::OpenGLFunctionLoader loader; + /* GL_ANGLE_base_vertex_base_instance */ + flextGL.DrawArraysInstancedBaseInstanceANGLE = reinterpret_cast(loader.load("glDrawArraysInstancedBaseInstanceANGLE")); + flextGL.DrawElementsInstancedBaseVertexBaseInstanceANGLE = reinterpret_cast(loader.load("glDrawElementsInstancedBaseVertexBaseInstanceANGLE")); + flextGL.MultiDrawArraysInstancedBaseInstanceANGLE = reinterpret_cast(loader.load("glMultiDrawArraysInstancedBaseInstanceANGLE")); + flextGL.MultiDrawElementsInstancedBaseVertexBaseInstanceANGLE = reinterpret_cast(loader.load("glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE")); + + /* GL_ANGLE_multi_draw */ + flextGL.MultiDrawArraysANGLE = reinterpret_cast(loader.load("glMultiDrawArraysANGLE")); + flextGL.MultiDrawArraysInstancedANGLE = reinterpret_cast(loader.load("glMultiDrawArraysInstancedANGLE")); + flextGL.MultiDrawElementsANGLE = reinterpret_cast(loader.load("glMultiDrawElementsANGLE")); + flextGL.MultiDrawElementsInstancedANGLE = reinterpret_cast(loader.load("glMultiDrawElementsInstancedANGLE")); + /* GL_ES_VERSION_3_1 */ flextGL.ActiveShaderProgram = reinterpret_cast(loader.load("glActiveShaderProgram")); flextGL.BindImageTexture = reinterpret_cast(loader.load("glBindImageTexture")); @@ -185,6 +197,12 @@ void flextGLInit(Magnum::GL::Context&) { flextGL.EnableiEXT = reinterpret_cast(loader.load("glEnableiEXT")); flextGL.IsEnablediEXT = reinterpret_cast(loader.load("glIsEnablediEXT")); + /* GL_EXT_draw_elements_base_vertex */ + flextGL.DrawElementsBaseVertexEXT = reinterpret_cast(loader.load("glDrawElementsBaseVertexEXT")); + flextGL.DrawElementsInstancedBaseVertexEXT = reinterpret_cast(loader.load("glDrawElementsInstancedBaseVertexEXT")); + flextGL.DrawRangeElementsBaseVertexEXT = reinterpret_cast(loader.load("glDrawRangeElementsBaseVertexEXT")); + flextGL.MultiDrawElementsBaseVertexEXT = reinterpret_cast(loader.load("glMultiDrawElementsBaseVertexEXT")); + /* GL_EXT_geometry_shader */ flextGL.FramebufferTextureEXT = reinterpret_cast(loader.load("glFramebufferTextureEXT")); @@ -302,6 +320,11 @@ void flextGLInit(Magnum::GL::Context&) { flextGL.NamedFramebufferSampleLocationsfvNV = reinterpret_cast(loader.load("glNamedFramebufferSampleLocationsfvNV")); flextGL.ResolveDepthValuesNV = reinterpret_cast(loader.load("glResolveDepthValuesNV")); + /* GL_OES_draw_elements_base_vertex */ + flextGL.DrawElementsBaseVertexOES = reinterpret_cast(loader.load("glDrawElementsBaseVertexOES")); + flextGL.DrawElementsInstancedBaseVertexOES = reinterpret_cast(loader.load("glDrawElementsInstancedBaseVertexOES")); + flextGL.DrawRangeElementsBaseVertexOES = reinterpret_cast(loader.load("glDrawRangeElementsBaseVertexOES")); + /* GL_OES_mapbuffer */ flextGL.GetBufferPointervOES = reinterpret_cast(loader.load("glGetBufferPointervOES")); flextGL.MapBufferOES = reinterpret_cast(loader.load("glMapBufferOES")); diff --git a/src/MagnumExternal/OpenGL/GLES3/flextGLPlatformIOS.cpp b/src/MagnumExternal/OpenGL/GLES3/flextGLPlatformIOS.cpp index bfcfdc176..5c2408406 100644 --- a/src/MagnumExternal/OpenGL/GLES3/flextGLPlatformIOS.cpp +++ b/src/MagnumExternal/OpenGL/GLES3/flextGLPlatformIOS.cpp @@ -25,6 +25,14 @@ #include "flextGL.h" +#undef glDrawArraysInstancedBaseInstanceANGLE +#undef glDrawElementsInstancedBaseVertexBaseInstanceANGLE +#undef glMultiDrawArraysInstancedBaseInstanceANGLE +#undef glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE +#undef glMultiDrawArraysANGLE +#undef glMultiDrawArraysInstancedANGLE +#undef glMultiDrawElementsANGLE +#undef glMultiDrawElementsInstancedANGLE #undef glCopyImageSubDataEXT #undef glGetObjectLabelEXT #undef glLabelObjectEXT @@ -51,6 +59,10 @@ #undef glDisableiEXT #undef glEnableiEXT #undef glIsEnablediEXT +#undef glDrawElementsBaseVertexEXT +#undef glDrawElementsInstancedBaseVertexEXT +#undef glDrawRangeElementsBaseVertexEXT +#undef glMultiDrawElementsBaseVertexEXT #undef glFramebufferTextureEXT #undef glMultiDrawArraysEXT #undef glMultiDrawElementsEXT @@ -138,6 +150,9 @@ #undef glFramebufferSampleLocationsfvNV #undef glNamedFramebufferSampleLocationsfvNV #undef glResolveDepthValuesNV +#undef glDrawElementsBaseVertexOES +#undef glDrawElementsInstancedBaseVertexOES +#undef glDrawRangeElementsBaseVertexOES #undef glGetBufferPointervOES #undef glMapBufferOES #undef glUnmapBufferOES @@ -149,6 +164,22 @@ void flextGLInit(Magnum::GL::Context&) { + /* GL_ANGLE_base_vertex_base_instance */ + #if GL_ANGLE_base_vertex_base_instance + flextGL.DrawArraysInstancedBaseInstanceANGLE = reinterpret_cast(glDrawArraysInstancedBaseInstanceANGLE); + flextGL.DrawElementsInstancedBaseVertexBaseInstanceANGLE = reinterpret_cast(glDrawElementsInstancedBaseVertexBaseInstanceANGLE); + flextGL.MultiDrawArraysInstancedBaseInstanceANGLE = reinterpret_cast(glMultiDrawArraysInstancedBaseInstanceANGLE); + flextGL.MultiDrawElementsInstancedBaseVertexBaseInstanceANGLE = reinterpret_cast(glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE); + #endif + + /* GL_ANGLE_multi_draw */ + #if GL_ANGLE_multi_draw + flextGL.MultiDrawArraysANGLE = reinterpret_cast(glMultiDrawArraysANGLE); + flextGL.MultiDrawArraysInstancedANGLE = reinterpret_cast(glMultiDrawArraysInstancedANGLE); + flextGL.MultiDrawElementsANGLE = reinterpret_cast(glMultiDrawElementsANGLE); + flextGL.MultiDrawElementsInstancedANGLE = reinterpret_cast(glMultiDrawElementsInstancedANGLE); + #endif + /* GL_EXT_copy_image */ #if GL_EXT_copy_image flextGL.CopyImageSubDataEXT = reinterpret_cast(glCopyImageSubDataEXT); @@ -195,6 +226,14 @@ void flextGLInit(Magnum::GL::Context&) { flextGL.IsEnablediEXT = reinterpret_cast(glIsEnablediEXT); #endif + /* GL_EXT_draw_elements_base_vertex */ + #if GL_EXT_draw_elements_base_vertex + flextGL.DrawElementsBaseVertexEXT = reinterpret_cast(glDrawElementsBaseVertexEXT); + flextGL.DrawElementsInstancedBaseVertexEXT = reinterpret_cast(glDrawElementsInstancedBaseVertexEXT); + flextGL.DrawRangeElementsBaseVertexEXT = reinterpret_cast(glDrawRangeElementsBaseVertexEXT); + flextGL.MultiDrawElementsBaseVertexEXT = reinterpret_cast(glMultiDrawElementsBaseVertexEXT); + #endif + /* GL_EXT_geometry_shader */ #if GL_EXT_geometry_shader flextGL.FramebufferTextureEXT = reinterpret_cast(glFramebufferTextureEXT); @@ -342,6 +381,13 @@ void flextGLInit(Magnum::GL::Context&) { flextGL.ResolveDepthValuesNV = reinterpret_cast(glResolveDepthValuesNV); #endif + /* GL_OES_draw_elements_base_vertex */ + #if GL_OES_draw_elements_base_vertex + flextGL.DrawElementsBaseVertexOES = reinterpret_cast(glDrawElementsBaseVertexOES); + flextGL.DrawElementsInstancedBaseVertexOES = reinterpret_cast(glDrawElementsInstancedBaseVertexOES); + flextGL.DrawRangeElementsBaseVertexOES = reinterpret_cast(glDrawRangeElementsBaseVertexOES); + #endif + /* GL_OES_mapbuffer */ #if GL_OES_mapbuffer flextGL.GetBufferPointervOES = reinterpret_cast(glGetBufferPointervOES); diff --git a/src/MagnumExternal/OpenGL/GLES3/flextGLPlatformWindowsDesktop.cpp b/src/MagnumExternal/OpenGL/GLES3/flextGLPlatformWindowsDesktop.cpp index 66981e4d9..44a01cf9e 100644 --- a/src/MagnumExternal/OpenGL/GLES3/flextGLPlatformWindowsDesktop.cpp +++ b/src/MagnumExternal/OpenGL/GLES3/flextGLPlatformWindowsDesktop.cpp @@ -33,6 +33,18 @@ void flextGLInit(Magnum::GL::Context&) { Magnum::Platform::Implementation::OpenGLFunctionLoader loader; + /* GL_ANGLE_base_vertex_base_instance */ + flextGL.DrawArraysInstancedBaseInstanceANGLE = reinterpret_cast(loader.load("glDrawArraysInstancedBaseInstanceANGLE")); + flextGL.DrawElementsInstancedBaseVertexBaseInstanceANGLE = reinterpret_cast(loader.load("glDrawElementsInstancedBaseVertexBaseInstanceANGLE")); + flextGL.MultiDrawArraysInstancedBaseInstanceANGLE = reinterpret_cast(loader.load("glMultiDrawArraysInstancedBaseInstanceANGLE")); + flextGL.MultiDrawElementsInstancedBaseVertexBaseInstanceANGLE = reinterpret_cast(loader.load("glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE")); + + /* GL_ANGLE_multi_draw */ + flextGL.MultiDrawArraysANGLE = reinterpret_cast(loader.load("glMultiDrawArraysANGLE")); + flextGL.MultiDrawArraysInstancedANGLE = reinterpret_cast(loader.load("glMultiDrawArraysInstancedANGLE")); + flextGL.MultiDrawElementsANGLE = reinterpret_cast(loader.load("glMultiDrawElementsANGLE")); + flextGL.MultiDrawElementsInstancedANGLE = reinterpret_cast(loader.load("glMultiDrawElementsInstancedANGLE")); + /* GL_ES_VERSION_2_0 */ flextGL.ActiveTexture = reinterpret_cast(loader.load("glActiveTexture")); flextGL.AttachShader = reinterpret_cast(loader.load("glAttachShader")); @@ -387,6 +399,12 @@ void flextGLInit(Magnum::GL::Context&) { flextGL.EnableiEXT = reinterpret_cast(loader.load("glEnableiEXT")); flextGL.IsEnablediEXT = reinterpret_cast(loader.load("glIsEnablediEXT")); + /* GL_EXT_draw_elements_base_vertex */ + flextGL.DrawElementsBaseVertexEXT = reinterpret_cast(loader.load("glDrawElementsBaseVertexEXT")); + flextGL.DrawElementsInstancedBaseVertexEXT = reinterpret_cast(loader.load("glDrawElementsInstancedBaseVertexEXT")); + flextGL.DrawRangeElementsBaseVertexEXT = reinterpret_cast(loader.load("glDrawRangeElementsBaseVertexEXT")); + flextGL.MultiDrawElementsBaseVertexEXT = reinterpret_cast(loader.load("glMultiDrawElementsBaseVertexEXT")); + /* GL_EXT_geometry_shader */ flextGL.FramebufferTextureEXT = reinterpret_cast(loader.load("glFramebufferTextureEXT")); @@ -504,6 +522,11 @@ void flextGLInit(Magnum::GL::Context&) { flextGL.NamedFramebufferSampleLocationsfvNV = reinterpret_cast(loader.load("glNamedFramebufferSampleLocationsfvNV")); flextGL.ResolveDepthValuesNV = reinterpret_cast(loader.load("glResolveDepthValuesNV")); + /* GL_OES_draw_elements_base_vertex */ + flextGL.DrawElementsBaseVertexOES = reinterpret_cast(loader.load("glDrawElementsBaseVertexOES")); + flextGL.DrawElementsInstancedBaseVertexOES = reinterpret_cast(loader.load("glDrawElementsInstancedBaseVertexOES")); + flextGL.DrawRangeElementsBaseVertexOES = reinterpret_cast(loader.load("glDrawRangeElementsBaseVertexOES")); + /* GL_OES_mapbuffer */ flextGL.GetBufferPointervOES = reinterpret_cast(loader.load("glGetBufferPointervOES")); flextGL.MapBufferOES = reinterpret_cast(loader.load("glMapBufferOES")); diff --git a/src/MagnumExternal/OpenGL/GLES3/flextGLWindowsDesktop.h b/src/MagnumExternal/OpenGL/GLES3/flextGLWindowsDesktop.h index 9ea6b5537..7557e4c77 100644 --- a/src/MagnumExternal/OpenGL/GLES3/flextGLWindowsDesktop.h +++ b/src/MagnumExternal/OpenGL/GLES3/flextGLWindowsDesktop.h @@ -1744,6 +1744,20 @@ GLAPI FLEXTGL_EXPORT void APIENTRY glGetTexLevelParameteriv(GLenum, GLint, GLenu less symbols is exported, saving on binary size. */ struct FlextGL { + /* GL_ANGLE_base_vertex_base_instance */ + + void(APIENTRY *DrawArraysInstancedBaseInstanceANGLE)(GLenum, GLint, GLsizei, GLsizei, GLuint); + void(APIENTRY *DrawElementsInstancedBaseVertexBaseInstanceANGLE)(GLenum, GLsizei, GLenum, const GLvoid *, GLsizei, GLint, GLuint); + void(APIENTRY *MultiDrawArraysInstancedBaseInstanceANGLE)(GLenum, const GLint *, const GLsizei *, const GLsizei *, const GLuint *, GLsizei); + void(APIENTRY *MultiDrawElementsInstancedBaseVertexBaseInstanceANGLE)(GLenum, const GLsizei *, GLenum, const GLvoid *const*, const GLsizei *, const GLint *, const GLuint *, GLsizei); + + /* GL_ANGLE_multi_draw */ + + void(APIENTRY *MultiDrawArraysANGLE)(GLenum, const GLint *, const GLsizei *, GLsizei); + void(APIENTRY *MultiDrawArraysInstancedANGLE)(GLenum, const GLint *, const GLsizei *, const GLsizei *, GLsizei); + void(APIENTRY *MultiDrawElementsANGLE)(GLenum, const GLsizei *, GLenum, const GLvoid *const*, GLsizei); + void(APIENTRY *MultiDrawElementsInstancedANGLE)(GLenum, const GLsizei *, GLenum, const GLvoid *const*, const GLsizei*, GLsizei); + /* GL_ES_VERSION_2_0 */ void(APIENTRY *ActiveTexture)(GLenum); @@ -2107,6 +2121,13 @@ struct FlextGL { void(APIENTRY *EnableiEXT)(GLenum, GLuint); GLboolean(APIENTRY *IsEnablediEXT)(GLenum, GLuint); + /* GL_EXT_draw_elements_base_vertex */ + + void(APIENTRY *DrawElementsBaseVertexEXT)(GLenum, GLsizei, GLenum, const void *, GLint); + void(APIENTRY *DrawElementsInstancedBaseVertexEXT)(GLenum, GLsizei, GLenum, const void *, GLsizei, GLint); + void(APIENTRY *DrawRangeElementsBaseVertexEXT)(GLenum, GLuint, GLuint, GLsizei, GLenum, const void *, GLint); + void(APIENTRY *MultiDrawElementsBaseVertexEXT)(GLenum, const GLsizei *, GLenum, const void *const*, GLsizei, const GLint *); + /* GL_EXT_geometry_shader */ void(APIENTRY *FramebufferTextureEXT)(GLenum, GLenum, GLuint, GLint); @@ -2239,6 +2260,12 @@ struct FlextGL { void(APIENTRY *NamedFramebufferSampleLocationsfvNV)(GLuint, GLuint, GLsizei, const GLfloat *); void(APIENTRY *ResolveDepthValuesNV)(void); + /* GL_OES_draw_elements_base_vertex */ + + void(APIENTRY *DrawElementsBaseVertexOES)(GLenum, GLsizei, GLenum, const void *, GLint); + void(APIENTRY *DrawElementsInstancedBaseVertexOES)(GLenum, GLsizei, GLenum, const void *, GLsizei, GLint); + void(APIENTRY *DrawRangeElementsBaseVertexOES)(GLenum, GLuint, GLuint, GLsizei, GLenum, const void *, GLint); + /* GL_OES_mapbuffer */ void(APIENTRY *GetBufferPointervOES)(GLenum, GLenum, void **); @@ -2260,6 +2287,20 @@ struct FlextGL { extern FLEXTGL_EXPORT FlextGL flextGL; +/* GL_ANGLE_base_vertex_base_instance */ + +#define glDrawArraysInstancedBaseInstanceANGLE flextGL.DrawArraysInstancedBaseInstanceANGLE +#define glDrawElementsInstancedBaseVertexBaseInstanceANGLE flextGL.DrawElementsInstancedBaseVertexBaseInstanceANGLE +#define glMultiDrawArraysInstancedBaseInstanceANGLE flextGL.MultiDrawArraysInstancedBaseInstanceANGLE +#define glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE flextGL.MultiDrawElementsInstancedBaseVertexBaseInstanceANGLE + +/* GL_ANGLE_multi_draw */ + +#define glMultiDrawArraysANGLE flextGL.MultiDrawArraysANGLE +#define glMultiDrawArraysInstancedANGLE flextGL.MultiDrawArraysInstancedANGLE +#define glMultiDrawElementsANGLE flextGL.MultiDrawElementsANGLE +#define glMultiDrawElementsInstancedANGLE flextGL.MultiDrawElementsInstancedANGLE + /* GL_ES_VERSION_2_0 */ #define glActiveTexture flextGL.ActiveTexture @@ -2623,6 +2664,13 @@ extern FLEXTGL_EXPORT FlextGL flextGL; #define glEnableiEXT flextGL.EnableiEXT #define glIsEnablediEXT flextGL.IsEnablediEXT +/* GL_EXT_draw_elements_base_vertex */ + +#define glDrawElementsBaseVertexEXT flextGL.DrawElementsBaseVertexEXT +#define glDrawElementsInstancedBaseVertexEXT flextGL.DrawElementsInstancedBaseVertexEXT +#define glDrawRangeElementsBaseVertexEXT flextGL.DrawRangeElementsBaseVertexEXT +#define glMultiDrawElementsBaseVertexEXT flextGL.MultiDrawElementsBaseVertexEXT + /* GL_EXT_geometry_shader */ #define glFramebufferTextureEXT flextGL.FramebufferTextureEXT @@ -2755,6 +2803,12 @@ extern FLEXTGL_EXPORT FlextGL flextGL; #define glNamedFramebufferSampleLocationsfvNV flextGL.NamedFramebufferSampleLocationsfvNV #define glResolveDepthValuesNV flextGL.ResolveDepthValuesNV +/* GL_OES_draw_elements_base_vertex */ + +#define glDrawElementsBaseVertexOES flextGL.DrawElementsBaseVertexOES +#define glDrawElementsInstancedBaseVertexOES flextGL.DrawElementsInstancedBaseVertexOES +#define glDrawRangeElementsBaseVertexOES flextGL.DrawRangeElementsBaseVertexOES + /* GL_OES_mapbuffer */ #define glGetBufferPointervOES flextGL.GetBufferPointervOES From f5313a480decece0ff25348090f05bbaf2a502ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 12 Feb 2021 22:36:36 +0100 Subject: [PATCH 36/44] GL: this should be here only on ES. The definition is already like that. --- src/Magnum/GL/MeshView.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Magnum/GL/MeshView.h b/src/Magnum/GL/MeshView.h index c79a513cb..1cf344120 100644 --- a/src/Magnum/GL/MeshView.h +++ b/src/Magnum/GL/MeshView.h @@ -260,7 +260,9 @@ class MAGNUM_GL_EXPORT MeshView { #ifndef MAGNUM_TARGET_WEBGL static MAGNUM_GL_LOCAL void multiDrawImplementationDefault(Containers::ArrayView> meshes); #endif + #ifdef MAGNUM_TARGET_GLES static MAGNUM_GL_LOCAL void multiDrawImplementationFallback(Containers::ArrayView> meshes); + #endif Containers::Reference _original; From 63f55a37b1dba0fc568a67721093b10f325a0a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 13 Feb 2021 14:10:23 +0100 Subject: [PATCH 37/44] GL: implement ANGLE_multi_draw and WEBGL_multi_draw support. --- doc/changelog.dox | 6 ++-- doc/opengl-support.dox | 8 ++--- src/Magnum/GL/AbstractShaderProgram.h | 6 ++-- src/Magnum/GL/Implementation/MeshState.cpp | 39 ++++++++++++++++++---- src/Magnum/GL/Implementation/MeshState.h | 2 ++ src/Magnum/GL/MeshView.cpp | 12 +++---- src/Magnum/GL/MeshView.h | 2 -- src/Magnum/GL/Test/MeshGLTest.cpp | 24 +++++++++---- 8 files changed, 71 insertions(+), 28 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 52d89fe16..5b9765be5 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -62,12 +62,14 @@ See also: @webgl_extension{WEBGL,debug_renderer_info} and @webgl_extension{WEBGL,debug_shaders} extensions, no implementation done yet -- Recognizing ANGLE GLES and WebGL multi-draw extensions: +- Recognizing ANGLE GLES and WebGL multi-draw extensions and using them in + @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>): - @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + - @webgl_extension{WEBGL,multi_draw} +- The following extensions are recognized but not used yet: - @m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_base_vertex_base_instance.txt) - @gl_extension{EXT,draw_elements_base_vertex} - @gl_extension{OES,draw_elements_base_vertex} - - @webgl_extension{WEBGL,multi_draw} - @webgl_extension{WEBGL,draw_instanced_base_vertex_base_instance} - @webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance} - Added a @ref GL::AbstractTexture::target() getter to simplify interaction diff --git a/doc/opengl-support.dox b/doc/opengl-support.dox index eaee12a1b..050e80605 100644 --- a/doc/opengl-support.dox +++ b/doc/opengl-support.dox @@ -449,7 +449,7 @@ Extension | Status @gl_extension2{ANGLE,texture_compression_dxt1,ANGLE_texture_compression_dxt} | done @gl_extension2{ANGLE,texture_compression_dxt3,ANGLE_texture_compression_dxt} | done @gl_extension2{ANGLE,texture_compression_dxt5,ANGLE_texture_compression_dxt} | done -@m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted) | | +@m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted) | done @m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_base_vertex_base_instance.txt) (unlisted) | | @gl_extension{APPLE,texture_format_BGRA8888} | done @gl_extension{APPLE,clip_distance} | done @@ -571,10 +571,10 @@ Extension | Status @webgl_extension{WEBGL,compressed_texture_pvrtc} | done @webgl_extension{WEBGL,compressed_texture_astc} | done @webgl_extension{WEBGL,compressed_texture_s3tc_srgb} | done -@webgl_extension{WEBGL,multi_draw} | missing support in Emscripten +@webgl_extension{WEBGL,multi_draw} | done @webgl_extension{WEBGL,blend_equation_advanced_coherent} | done -@webgl_extension{WEBGL,draw_instanced_base_vertex_base_instance} | missing support in Emscripten -@webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance} | missing support in Emscripten +@webgl_extension{WEBGL,draw_instanced_base_vertex_base_instance} | | +@webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance} | | @section opengl-unsupported Unsupported OpenGL features diff --git a/src/Magnum/GL/AbstractShaderProgram.h b/src/Magnum/GL/AbstractShaderProgram.h index 804ddef34..7b3a24161 100644 --- a/src/Magnum/GL/AbstractShaderProgram.h +++ b/src/Magnum/GL/AbstractShaderProgram.h @@ -840,8 +840,10 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * @brief Draw multiple meshes at once * @m_since{2020,06} * - * In OpenGL ES, if @gl_extension{EXT,multi_draw_arrays} is not - * present, the functionality is emulated using a sequence of + * On OpenGL ES, if neither @gl_extension{EXT,multi_draw_arrays} nor + * @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + * is present, and on WebGL if @webgl_extension{WEBGL,multi_draw} is + * not present, the functionality is emulated using a sequence of * @ref draw(MeshView&) calls. * * If @gl_extension{ARB,vertex_array_object} (part of OpenGL 3.0), diff --git a/src/Magnum/GL/Implementation/MeshState.cpp b/src/Magnum/GL/Implementation/MeshState.cpp index 955697e52..7901a6a8e 100644 --- a/src/Magnum/GL/Implementation/MeshState.cpp +++ b/src/Magnum/GL/Implementation/MeshState.cpp @@ -108,16 +108,43 @@ MeshState::MeshState(Context& context, ContextState& contextState, std::vector()) { - extensions.push_back(Extensions::EXT::multi_draw_arrays::string()); + if(context.isExtensionSupported() || + context.isExtensionSupported()) + #else + if(context.isExtensionSupported()) + #endif + { + #ifndef MAGNUM_TARGET_WEBGL + if(context.isExtensionSupported()) { + extensions.push_back(Extensions::EXT::multi_draw_arrays::string()); + + multiDrawArraysImplementation = glMultiDrawArraysEXT; + multiDrawElementsImplementation = glMultiDrawElementsEXT; + } else if(context.isExtensionSupported()) { + extensions.push_back(Extensions::ANGLE::multi_draw::string()); + + multiDrawArraysImplementation = glMultiDrawArraysANGLE; + multiDrawElementsImplementation = glMultiDrawElementsANGLE; + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + #else + { + extensions.push_back(Extensions::WEBGL::multi_draw::string()); + + /* The WEBGL extension uses the same entrypoints as the ANGLE + extension it was based on */ + multiDrawArraysImplementation = glMultiDrawArraysANGLE; + multiDrawElementsImplementation = glMultiDrawElementsANGLE; + } + #endif multiDrawImplementation = &MeshView::multiDrawImplementationDefault; + } else multiDrawImplementation = &MeshView::multiDrawImplementationFallback; - #else - multiDrawImplementation = &MeshView::multiDrawImplementationFallback; - #endif #endif #ifdef MAGNUM_TARGET_GLES2 diff --git a/src/Magnum/GL/Implementation/MeshState.h b/src/Magnum/GL/Implementation/MeshState.h index 62544edc9..f512cc5bc 100644 --- a/src/Magnum/GL/Implementation/MeshState.h +++ b/src/Magnum/GL/Implementation/MeshState.h @@ -60,6 +60,8 @@ struct MeshState { #ifdef MAGNUM_TARGET_GLES void(*multiDrawImplementation)(Containers::ArrayView>); + void(APIENTRY *multiDrawArraysImplementation)(GLenum, const GLint*, const GLsizei*, GLsizei); + void(APIENTRY *multiDrawElementsImplementation)(GLenum, const GLsizei*, GLenum, const void* const*, GLsizei); #endif void(*bindVAOImplementation)(GLuint); diff --git a/src/Magnum/GL/MeshView.cpp b/src/Magnum/GL/MeshView.cpp index 76a5b42c3..0dddaff5c 100644 --- a/src/Magnum/GL/MeshView.cpp +++ b/src/Magnum/GL/MeshView.cpp @@ -84,7 +84,6 @@ MeshView& MeshView::draw(AbstractShaderProgram&& shader, TransformFeedback& xfb, #endif #endif -#ifndef MAGNUM_TARGET_WEBGL void MeshView::multiDrawImplementationDefault(Containers::ArrayView> meshes) { CORRADE_INTERNAL_ASSERT(meshes.size()); @@ -123,10 +122,11 @@ void MeshView::multiDrawImplementationDefault(Containers::ArrayView> meshes) { diff --git a/src/Magnum/GL/MeshView.h b/src/Magnum/GL/MeshView.h index 1cf344120..1ba06a250 100644 --- a/src/Magnum/GL/MeshView.h +++ b/src/Magnum/GL/MeshView.h @@ -257,9 +257,7 @@ class MAGNUM_GL_EXPORT MeshView { friend AbstractShaderProgram; friend Implementation::MeshState; - #ifndef MAGNUM_TARGET_WEBGL static MAGNUM_GL_LOCAL void multiDrawImplementationDefault(Containers::ArrayView> meshes); - #endif #ifdef MAGNUM_TARGET_GLES static MAGNUM_GL_LOCAL void multiDrawImplementationFallback(Containers::ArrayView> meshes); #endif diff --git a/src/Magnum/GL/Test/MeshGLTest.cpp b/src/Magnum/GL/Test/MeshGLTest.cpp index 2ecaac024..2bba4708b 100644 --- a/src/Magnum/GL/Test/MeshGLTest.cpp +++ b/src/Magnum/GL/Test/MeshGLTest.cpp @@ -3005,9 +3005,15 @@ template T MultiChecker::get(PixelFormat format, PixelType type) { #endif void MeshGLTest::multiDraw() { - #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) - if(!Context::current().isExtensionSupported()) - Debug() << Extensions::EXT::multi_draw_arrays::string() << "not supported, using fallback implementation"; + #ifdef MAGNUM_TARGET_GLES + #ifndef MAGNUM_TARGET_WEBGL + if(!Context::current().isExtensionSupported() && + !Context::current().isExtensionSupported()) + Debug{} << "Neither" << Extensions::EXT::multi_draw_arrays::string() << "nor" << Extensions::ANGLE::multi_draw::string() << "is supported, using fallback implementation"; + #else + if(!Context::current().isExtensionSupported()) + Debug{} << Extensions::WEBGL::multi_draw::string() << "is not supported, using fallback implementation"; + #endif #endif typedef Attribute<0, Float> Attribute; @@ -3042,9 +3048,15 @@ void MeshGLTest::multiDraw() { } void MeshGLTest::multiDrawIndexed() { - #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) - if(!Context::current().isExtensionSupported()) - Debug() << Extensions::EXT::multi_draw_arrays::string() << "not supported, using fallback implementation"; + #ifdef MAGNUM_TARGET_GLES + #ifndef MAGNUM_TARGET_WEBGL + if(!Context::current().isExtensionSupported() && + !Context::current().isExtensionSupported()) + Debug{} << "Neither" << Extensions::EXT::multi_draw_arrays::string() << "nor" << Extensions::ANGLE::multi_draw::string() << "is supported, using fallback implementation"; + #else + if(!Context::current().isExtensionSupported()) + Debug{} << Extensions::WEBGL::multi_draw::string() << "is not supported, using fallback implementation"; + #endif #endif Buffer vertices; From acdbcc2ef498997367b733f97afc869b1fc7dafd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 13 Feb 2021 17:13:33 +0100 Subject: [PATCH 38/44] GL: advertise WEBGL_multi_draw only on Emscripten 2.0.0 and up. --- src/Magnum/GL/AbstractShaderProgram.h | 4 +++- src/Magnum/GL/Implementation/MeshState.cpp | 9 ++++++++- src/Magnum/GL/Implementation/driverSpecific.cpp | 10 ++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Magnum/GL/AbstractShaderProgram.h b/src/Magnum/GL/AbstractShaderProgram.h index 7b3a24161..72791a466 100644 --- a/src/Magnum/GL/AbstractShaderProgram.h +++ b/src/Magnum/GL/AbstractShaderProgram.h @@ -844,7 +844,9 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) * is present, and on WebGL if @webgl_extension{WEBGL,multi_draw} is * not present, the functionality is emulated using a sequence of - * @ref draw(MeshView&) calls. + * @ref draw(MeshView&) calls. Note that @webgl_extension{WEBGL,multi_draw} + * is only implemented since Emscripten 2.0.0, so it's not even + * advertised on older versions. * * If @gl_extension{ARB,vertex_array_object} (part of OpenGL 3.0), * OpenGL ES 3.0, WebGL 2.0, @gl_extension{OES,vertex_array_object} in diff --git a/src/Magnum/GL/Implementation/MeshState.cpp b/src/Magnum/GL/Implementation/MeshState.cpp index 7901a6a8e..eeed2fe08 100644 --- a/src/Magnum/GL/Implementation/MeshState.cpp +++ b/src/Magnum/GL/Implementation/MeshState.cpp @@ -136,9 +136,16 @@ MeshState::MeshState(Context& context, ContextState& contextState, std::vector= 20000 multiDrawArraysImplementation = glMultiDrawArraysANGLE; multiDrawElementsImplementation = glMultiDrawElementsANGLE; + #else + /* In Context::setupDriverWorkarounds() we make sure the extension + is not even advertised, so this shouldn't be reached. */ + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + #endif } #endif diff --git a/src/Magnum/GL/Implementation/driverSpecific.cpp b/src/Magnum/GL/Implementation/driverSpecific.cpp index 370c97fd3..c78065eee 100644 --- a/src/Magnum/GL/Implementation/driverSpecific.cpp +++ b/src/Magnum/GL/Implementation/driverSpecific.cpp @@ -560,6 +560,16 @@ void Context::setupDriverWorkarounds() { _setRequiredVersion(EXT::disjoint_timer_query, None); #endif + #ifdef MAGNUM_TARGET_WEBGL + /* The WEBGL_multi_draw entrypoints are only available since Emscripten + 2.0.0: https://github.com/emscripten-core/emscripten/pull/11650 + However, the extension is advertised even on older versions and we have + no way to link to those entrypoints there. */ + #if __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ < 20000 + _setRequiredVersion(WEBGL::multi_draw, None); + #endif + #endif + #undef _setRequiredVersion #ifndef MAGNUM_TARGET_GLES From d0574b83ed0741b5043c5fe8239a0862139f72cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 13 Feb 2021 17:00:01 +0100 Subject: [PATCH 39/44] GL: advertise the "intel-windows-chatty-shader-compiler" workaround. This was done silently until now and I think such platform-specific code should be always exposed as a disableable workaround. Moreover, I need a similar thing for ANGLE, so this comes handy. --- doc/changelog.dox | 4 +++ src/Magnum/GL/AbstractShaderProgram.cpp | 21 +++++++---- src/Magnum/GL/AbstractShaderProgram.h | 5 +++ .../GL/Implementation/ShaderProgramState.cpp | 9 +++++ .../GL/Implementation/ShaderProgramState.h | 1 + src/Magnum/GL/Implementation/ShaderState.cpp | 20 +++++++---- src/Magnum/GL/Implementation/ShaderState.h | 1 + .../GL/Implementation/driverSpecific.cpp | 36 +++---------------- src/Magnum/GL/Shader.cpp | 21 +++++++---- src/Magnum/GL/Shader.h | 5 +++ 10 files changed, 71 insertions(+), 52 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 5b9765be5..bdf3ca48a 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -175,6 +175,10 @@ See also: - Added @ref GL::Framebuffer::Status::IncompleteDimensions for ES2. This enum isn't available on ES3 or desktop GL, but NVidia drivers are known to emit it, which is why it got added. +- Intel/Windows-specific code for silencing useless shader compiler output is + now advertised as a @cpp "intel-windows-chatty-shader-compiler" @ce + workaround, instead of being done silently. See @ref opengl-workarounds for + more information. @subsubsection changelog-latest-changes-math Math library diff --git a/src/Magnum/GL/AbstractShaderProgram.cpp b/src/Magnum/GL/AbstractShaderProgram.cpp index ee21e77cf..7f306e1c1 100644 --- a/src/Magnum/GL/AbstractShaderProgram.cpp +++ b/src/Magnum/GL/AbstractShaderProgram.cpp @@ -46,11 +46,6 @@ namespace Magnum { namespace GL { -namespace Implementation { - /* Defined in Implementation/driverSpecific.cpp */ - bool isProgramLinkLogEmpty(const std::string& result); -} - Int AbstractShaderProgram::maxVertexAttributes() { GLint& value = Context::current().state().shaderProgram->maxVertexAttributes; @@ -508,12 +503,16 @@ bool AbstractShaderProgram::link(std::initializer_list 1) glGetProgramInfoLog(shader._id, message.size(), nullptr, &message[0]); message.resize(Math::max(logLength, 1)-1); + /* Some drivers are chatty and can't keep shut when there's nothing to + be said, handle that as well. */ + Context::current().state().shaderProgram->cleanLogImplementation(message); + /* Show error log */ if(!success) { Error out{Debug::Flag::NoNewlineAtTheEnd}; @@ -522,7 +521,7 @@ bool AbstractShaderProgram::link(std::initializer_list name) { const GLint location = glGetUniformLocation(_id, name); if(location == -1) diff --git a/src/Magnum/GL/AbstractShaderProgram.h b/src/Magnum/GL/AbstractShaderProgram.h index 72791a466..6006cb8cc 100644 --- a/src/Magnum/GL/AbstractShaderProgram.h +++ b/src/Magnum/GL/AbstractShaderProgram.h @@ -1347,6 +1347,11 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { #endif #endif + static MAGNUM_GL_LOCAL void cleanLogImplementationNoOp(std::string& message); + #if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES) + static MAGNUM_GL_LOCAL void cleanLogImplementationIntelWindows(std::string& message); + #endif + void use(); /* diff --git a/src/Magnum/GL/Implementation/ShaderProgramState.cpp b/src/Magnum/GL/Implementation/ShaderProgramState.cpp index 24fe23f34..943f99237 100644 --- a/src/Magnum/GL/Implementation/ShaderProgramState.cpp +++ b/src/Magnum/GL/Implementation/ShaderProgramState.cpp @@ -60,6 +60,15 @@ ShaderProgramState::ShaderProgramState(Context& context, std::vector()) diff --git a/src/Magnum/GL/Implementation/ShaderProgramState.h b/src/Magnum/GL/Implementation/ShaderProgramState.h index ea992308e..42e925ef6 100644 --- a/src/Magnum/GL/Implementation/ShaderProgramState.h +++ b/src/Magnum/GL/Implementation/ShaderProgramState.h @@ -46,6 +46,7 @@ struct ShaderProgramState { #ifndef MAGNUM_TARGET_GLES2 void(AbstractShaderProgram::*transformFeedbackVaryingsImplementation)(Containers::ArrayView, AbstractShaderProgram::TransformFeedbackBufferMode); #endif + void(*cleanLogImplementation)(std::string&); void(AbstractShaderProgram::*uniform1fvImplementation)(GLint, GLsizei, const GLfloat*); void(AbstractShaderProgram::*uniform2fvImplementation)(GLint, GLsizei, const Math::Vector<2, GLfloat>*); diff --git a/src/Magnum/GL/Implementation/ShaderState.cpp b/src/Magnum/GL/Implementation/ShaderState.cpp index 7518d702d..8339e5069 100644 --- a/src/Magnum/GL/Implementation/ShaderState.cpp +++ b/src/Magnum/GL/Implementation/ShaderState.cpp @@ -25,11 +25,10 @@ #include "ShaderState.h" -#include "Magnum/GL/Shader.h" - -#if defined(CORRADE_TARGET_EMSCRIPTEN) && defined(__EMSCRIPTEN_PTHREADS__) +/* Needed only for Emscripten+pthread- / Windows+Intel-specific workarounds, + but I won't bother crafting the preprocessor logic for this. */ #include "Magnum/GL/Context.h" -#endif +#include "Magnum/GL/Shader.h" namespace Magnum { namespace GL { namespace Implementation { @@ -56,9 +55,18 @@ ShaderState::ShaderState(Context& context, std::vector&): addSourceImplementation = &Shader::addSourceImplementationDefault; } - #if !defined(CORRADE_TARGET_EMSCRIPTEN) || !defined(__EMSCRIPTEN_PTHREADS__) - static_cast(context); + #if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES) + if((context.detectedDriver() & Context::DetectedDriver::IntelWindows) && !context.isDriverWorkaroundDisabled("intel-windows-chatty-shader-compiler")) { + cleanLogImplementation = &Shader::cleanLogImplementationIntelWindows; + } else #endif + { + cleanLogImplementation = &Shader::cleanLogImplementationNoOp; + } + + /* Needed only if neither of these ifdefs above hits, but I won't bother + crafting the preprocessor logic for this. */ + static_cast(context); } }}} diff --git a/src/Magnum/GL/Implementation/ShaderState.h b/src/Magnum/GL/Implementation/ShaderState.h index 1f367d58f..7dd6fc52a 100644 --- a/src/Magnum/GL/Implementation/ShaderState.h +++ b/src/Magnum/GL/Implementation/ShaderState.h @@ -52,6 +52,7 @@ struct ShaderState { }; void(Shader::*addSourceImplementation)(std::string); + void(*cleanLogImplementation)(std::string&); GLint maxVertexOutputComponents, maxFragmentInputComponents; diff --git a/src/Magnum/GL/Implementation/driverSpecific.cpp b/src/Magnum/GL/Implementation/driverSpecific.cpp index c78065eee..05a76f5eb 100644 --- a/src/Magnum/GL/Implementation/driverSpecific.cpp +++ b/src/Magnum/GL/Implementation/driverSpecific.cpp @@ -336,6 +336,10 @@ const char* KnownWorkarounds[]{ MeshGLTest::addVertexBufferIntWithShort(). */ "intel-windows-broken-dsa-integer-vertex-attributes", +/* Shader compiler on Intel Windows drivers insists on telling me "No errors." + when it should just stay silent. */ +"intel-windows-chatty-shader-compiler", + /* When using more than just a vertex and fragment shader (geometry shader, e.g.), ARB_explicit_uniform_location on Intel silently uses wrong locations, blowing up with either a non-descript @@ -382,38 +386,6 @@ const char* KnownWorkarounds[]{ } -namespace Implementation { - -/* Used in Shader.cpp (duh) */ -bool isShaderCompilationLogEmpty(const std::string&); -bool isShaderCompilationLogEmpty(const std::string& result) { - #if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES) - /* Intel Windows drivers are too chatty */ - if((Context::current().detectedDriver() & Context::DetectedDriver::IntelWindows) && result == "No errors.\n") - return true; - #else - static_cast(result); - #endif - - return false; -} - -/* Used in AbstractShaderProgram.cpp (duh) */ -bool isProgramLinkLogEmpty(const std::string&); -bool isProgramLinkLogEmpty(const std::string& result) { - #if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES) - /* Intel Windows drivers are too chatty */ - if((Context::current().detectedDriver() & Context::DetectedDriver::IntelWindows) && result == "No errors.\n") - return true; - #else - static_cast(result); - #endif - - return false; -} - -} - auto Context::detectedDriver() -> DetectedDrivers { if(_detectedDrivers) return *_detectedDrivers; diff --git a/src/Magnum/GL/Shader.cpp b/src/Magnum/GL/Shader.cpp index 2e1905ef6..046c1905d 100644 --- a/src/Magnum/GL/Shader.cpp +++ b/src/Magnum/GL/Shader.cpp @@ -48,11 +48,6 @@ typedef char GLchar; namespace Magnum { namespace GL { -namespace Implementation { - /* defined in Implementation/driverSpecific.cpp */ - bool isShaderCompilationLogEmpty(const std::string& result); -} - namespace { std::string shaderName(const Shader::Type type) { @@ -786,12 +781,16 @@ bool Shader::compile(std::initializer_list> shader glGetShaderiv(shader._id, GL_INFO_LOG_LENGTH, &logLength); /* Error or warning message. The string is returned null-terminated, - scrap the \0 at the end afterwards */ + strip the \0 at the end afterwards. */ std::string message(logLength, '\0'); if(message.size() > 1) glGetShaderInfoLog(shader._id, message.size(), nullptr, &message[0]); message.resize(Math::max(logLength, 1)-1); + /* Some drivers are chatty and can't keep shut when there's nothing to + be said, handle that as well. */ + Context::current().state().shader->cleanLogImplementation(message); + /* Show error log */ if(!success) { Error out{Debug::Flag::NoNewlineAtTheEnd}; @@ -800,7 +799,7 @@ bool Shader::compile(std::initializer_list> shader out << "failed with the following message:" << Debug::newline << message; /* Or just warnings, if any */ - } else if(!message.empty() && !Implementation::isShaderCompilationLogEmpty(message)) { + } else if(!message.empty()) { Warning out{Debug::Flag::NoNewlineAtTheEnd}; out << "GL::Shader::compile(): compilation of" << shaderName(shader._type) << "shader"; if(shaders.size() != 1) out << i; @@ -815,6 +814,14 @@ bool Shader::compile(std::initializer_list> shader return allSuccess; } +void Shader::cleanLogImplementationNoOp(std::string&) {} + +#if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES) +void Shader::cleanLogImplementationIntelWindows(std::string& message) { + if(message == "No errors.\n") message = {}; +} +#endif + #ifndef DOXYGEN_GENERATING_OUTPUT Debug& operator<<(Debug& debug, const Shader::Type value) { debug << "GL::Shader::Type" << Debug::nospace; diff --git a/src/Magnum/GL/Shader.h b/src/Magnum/GL/Shader.h index 59f37beb1..5ecef0324 100644 --- a/src/Magnum/GL/Shader.h +++ b/src/Magnum/GL/Shader.h @@ -652,6 +652,11 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject { void MAGNUM_GL_LOCAL addSourceImplementationEmscriptenPthread(std::string source); #endif + static MAGNUM_GL_LOCAL void cleanLogImplementationNoOp(std::string& message); + #if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES) + static MAGNUM_GL_LOCAL void cleanLogImplementationIntelWindows(std::string& message); + #endif + Type _type; GLuint _id; From 0dc181d846e0423d76fd9873eeaaec465299410c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 13 Feb 2021 17:28:51 +0100 Subject: [PATCH 40/44] GL: new "angle-chatty-shader-compiler" workaround. --- doc/changelog.dox | 3 +++ src/Magnum/GL/AbstractShaderProgram.cpp | 6 ++++++ src/Magnum/GL/AbstractShaderProgram.h | 3 +++ src/Magnum/GL/Implementation/ShaderProgramState.cpp | 5 +++++ src/Magnum/GL/Implementation/driverSpecific.cpp | 9 ++++++++- 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index bdf3ca48a..90eafbebc 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -81,6 +81,9 @@ See also: happening during EGL initialization in recent NVidia drivers. See @ref opengl-workarounds and [mosra/magnum#491](https://github.com/mosra/magnum/pull/491) for more information. +- A new @cpp "angle-chatty-shader-compiler" @ce workarounds for silencing + useless linker output on ANGLE. See @ref opengl-workarounds for more + information. @subsubsection changelog-latest-new-math Math library diff --git a/src/Magnum/GL/AbstractShaderProgram.cpp b/src/Magnum/GL/AbstractShaderProgram.cpp index 7f306e1c1..e426d43ed 100644 --- a/src/Magnum/GL/AbstractShaderProgram.cpp +++ b/src/Magnum/GL/AbstractShaderProgram.cpp @@ -544,6 +544,12 @@ void AbstractShaderProgram::cleanLogImplementationIntelWindows(std::string& mess } #endif +#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) +void AbstractShaderProgram::cleanLogImplementationAngle(std::string& message) { + if(message == "\n") message = {}; +} +#endif + Int AbstractShaderProgram::uniformLocationInternal(const Containers::ArrayView name) { const GLint location = glGetUniformLocation(_id, name); if(location == -1) diff --git a/src/Magnum/GL/AbstractShaderProgram.h b/src/Magnum/GL/AbstractShaderProgram.h index 6006cb8cc..b62f58ec1 100644 --- a/src/Magnum/GL/AbstractShaderProgram.h +++ b/src/Magnum/GL/AbstractShaderProgram.h @@ -1351,6 +1351,9 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { #if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES) static MAGNUM_GL_LOCAL void cleanLogImplementationIntelWindows(std::string& message); #endif + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + static MAGNUM_GL_LOCAL void cleanLogImplementationAngle(std::string& message); + #endif void use(); diff --git a/src/Magnum/GL/Implementation/ShaderProgramState.cpp b/src/Magnum/GL/Implementation/ShaderProgramState.cpp index 943f99237..99ca2545f 100644 --- a/src/Magnum/GL/Implementation/ShaderProgramState.cpp +++ b/src/Magnum/GL/Implementation/ShaderProgramState.cpp @@ -65,6 +65,11 @@ ShaderProgramState::ShaderProgramState(Context& context, std::vector Date: Mon, 15 Feb 2021 12:51:28 +0100 Subject: [PATCH 41/44] GL: remove one function pointer indirection for certain glDraw calls. The extension-epcific code paths can reference the GL APIs directly, no need to go through another function. --- src/Magnum/GL/Implementation/MeshState.cpp | 12 +++---- src/Magnum/GL/Implementation/MeshState.h | 4 +-- src/Magnum/GL/Mesh.cpp | 40 ++++------------------ src/Magnum/GL/Mesh.h | 14 -------- 4 files changed, 14 insertions(+), 56 deletions(-) diff --git a/src/Magnum/GL/Implementation/MeshState.cpp b/src/Magnum/GL/Implementation/MeshState.cpp index eeed2fe08..eef00a734 100644 --- a/src/Magnum/GL/Implementation/MeshState.cpp +++ b/src/Magnum/GL/Implementation/MeshState.cpp @@ -159,8 +159,8 @@ MeshState::MeshState(Context& context, ContextState& contextState, std::vector()) { extensions.push_back(Extensions::ANGLE::instanced_arrays::string()); - drawArraysInstancedImplementation = &Mesh::drawArraysInstancedImplementationANGLE; - drawElementsInstancedImplementation = &Mesh::drawElementsInstancedImplementationANGLE; + drawArraysInstancedImplementation = glDrawArraysInstancedANGLE; + drawElementsInstancedImplementation = glDrawElementsInstancedANGLE; } #ifndef MAGNUM_TARGET_WEBGL else if(context.isExtensionSupported() || @@ -169,8 +169,8 @@ MeshState::MeshState(Context& context, ContextState& contextState, std::vector() || context.isExtensionSupported()) { @@ -178,8 +178,8 @@ MeshState::MeshState(Context& context, ContextState& contextState, std::vector*state.drawArraysInstancedImplementation)(baseVertex, count, instanceCount); + state.drawArraysInstancedImplementation #endif + (GLenum(_primitive), baseVertex, count, instanceCount); } /* Indexed mesh with base vertex */ @@ -503,10 +504,11 @@ void Mesh::drawInternal(Int count, Int baseVertex, Int instanceCount, GLintptr i #endif { #ifndef MAGNUM_TARGET_GLES2 - glDrawElementsInstanced(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount); + glDrawElementsInstanced #else - (this->*state.drawElementsInstancedImplementation)(count, indexOffset, instanceCount); + state.drawElementsInstancedImplementation #endif + (GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount); } } } @@ -871,34 +873,4 @@ void Mesh::unbindImplementationDefault() { void Mesh::unbindImplementationVAO() {} -#ifdef MAGNUM_TARGET_GLES2 -void Mesh::drawArraysInstancedImplementationANGLE(const GLint baseVertex, const GLsizei count, const GLsizei instanceCount) { - glDrawArraysInstancedANGLE(GLenum(_primitive), baseVertex, count, instanceCount); -} - -#ifndef MAGNUM_TARGET_WEBGL -void Mesh::drawArraysInstancedImplementationEXT(const GLint baseVertex, const GLsizei count, const GLsizei instanceCount) { - glDrawArraysInstancedEXT(GLenum(_primitive), baseVertex, count, instanceCount); -} - -void Mesh::drawArraysInstancedImplementationNV(const GLint baseVertex, const GLsizei count, const GLsizei instanceCount) { - glDrawArraysInstancedNV(GLenum(_primitive), baseVertex, count, instanceCount); -} -#endif - -void Mesh::drawElementsInstancedImplementationANGLE(const GLsizei count, const GLintptr indexOffset, const GLsizei instanceCount) { - glDrawElementsInstancedANGLE(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount); -} - -#ifndef MAGNUM_TARGET_WEBGL -void Mesh::drawElementsInstancedImplementationEXT(const GLsizei count, const GLintptr indexOffset, const GLsizei instanceCount) { - glDrawElementsInstancedEXT(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount); -} - -void Mesh::drawElementsInstancedImplementationNV(const GLsizei count, const GLintptr indexOffset, const GLsizei instanceCount) { - glDrawElementsInstancedNV(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount); -} -#endif -#endif - }} diff --git a/src/Magnum/GL/Mesh.h b/src/Magnum/GL/Mesh.h index ad78ce5f5..a2c25d402 100644 --- a/src/Magnum/GL/Mesh.h +++ b/src/Magnum/GL/Mesh.h @@ -1133,20 +1133,6 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject { void MAGNUM_GL_LOCAL unbindImplementationDefault(); void MAGNUM_GL_LOCAL unbindImplementationVAO(); - #ifdef MAGNUM_TARGET_GLES2 - void MAGNUM_GL_LOCAL drawArraysInstancedImplementationANGLE(GLint baseVertex, GLsizei count, GLsizei instanceCount); - #ifndef MAGNUM_TARGET_WEBGL - void MAGNUM_GL_LOCAL drawArraysInstancedImplementationEXT(GLint baseVertex, GLsizei count, GLsizei instanceCount); - void MAGNUM_GL_LOCAL drawArraysInstancedImplementationNV(GLint baseVertex, GLsizei count, GLsizei instanceCount); - #endif - - void MAGNUM_GL_LOCAL drawElementsInstancedImplementationANGLE(GLsizei count, GLintptr indexOffset, GLsizei instanceCount); - #ifndef MAGNUM_TARGET_WEBGL - void MAGNUM_GL_LOCAL drawElementsInstancedImplementationEXT(GLsizei count, GLintptr indexOffset, GLsizei instanceCount); - void MAGNUM_GL_LOCAL drawElementsInstancedImplementationNV(GLsizei count, GLintptr indexOffset, GLsizei instanceCount); - #endif - #endif - /* _id, _primitive, _flags set from constructors */ GLuint _id; MeshPrimitive _primitive; From ffdebff048be05cb669de5022cc65e6dd5c9a475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 16 Feb 2021 19:41:27 +0100 Subject: [PATCH 42/44] GL: implement the base instance and base vertex ES and WebGL extensions. AAAAAAAAAAAAAAAAAAAAAAAAAAAAA this was terrible! Why the hell it needs to have so many variants, special cases and exceptions!!! --- doc/changelog.dox | 12 +- doc/opengl-support.dox | 10 +- src/Magnum/GL/AbstractShaderProgram.cpp | 8 +- src/Magnum/GL/AbstractShaderProgram.h | 10 +- src/Magnum/GL/Implementation/MeshState.cpp | 134 +++++ src/Magnum/GL/Implementation/MeshState.h | 22 + .../GL/Implementation/driverSpecific.cpp | 12 + src/Magnum/GL/Mesh.cpp | 122 ++++- src/Magnum/GL/Mesh.h | 54 +- src/Magnum/GL/MeshView.cpp | 54 +- src/Magnum/GL/MeshView.h | 25 +- src/Magnum/GL/Test/MeshGLTest.cpp | 462 +++++++++++++++--- 12 files changed, 797 insertions(+), 128 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 90eafbebc..ad9f3c3a8 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -62,14 +62,14 @@ See also: @webgl_extension{WEBGL,debug_renderer_info} and @webgl_extension{WEBGL,debug_shaders} extensions, no implementation done yet -- Recognizing ANGLE GLES and WebGL multi-draw extensions and using them in - @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>): - - @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) - - @webgl_extension{WEBGL,multi_draw} -- The following extensions are recognized but not used yet: - - @m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_base_vertex_base_instance.txt) +- Recognizing ANGLE GLES and WebGL base vertex, base instance and multi-draw + extensions and using them in @ref GL::AbstractShaderProgram::draw(Mesh&) + and @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>): - @gl_extension{EXT,draw_elements_base_vertex} - @gl_extension{OES,draw_elements_base_vertex} + - @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + - @m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_base_vertex_base_instance.txt) + - @webgl_extension{WEBGL,multi_draw} - @webgl_extension{WEBGL,draw_instanced_base_vertex_base_instance} - @webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance} - Added a @ref GL::AbstractTexture::target() getter to simplify interaction diff --git a/doc/opengl-support.dox b/doc/opengl-support.dox index 050e80605..9c8ce2efa 100644 --- a/doc/opengl-support.dox +++ b/doc/opengl-support.dox @@ -450,7 +450,7 @@ Extension | Status @gl_extension2{ANGLE,texture_compression_dxt3,ANGLE_texture_compression_dxt} | done @gl_extension2{ANGLE,texture_compression_dxt5,ANGLE_texture_compression_dxt} | done @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted) | done -@m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_base_vertex_base_instance.txt) (unlisted) | | +@m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_base_vertex_base_instance.txt) (unlisted) | done except instanced multi-draw @gl_extension{APPLE,texture_format_BGRA8888} | done @gl_extension{APPLE,clip_distance} | done @gl_extension{ARM,shader_framebuffer_fetch} | missing renderer setup and limit query @@ -472,7 +472,7 @@ Extension | Status @gl_extension{EXT,texture_compression_s3tc} | done @gl_extension{EXT,pvrtc_sRGB} | done @gl_extension{EXT,shader_integer_mix} | done (shading language only) -@gl_extension{EXT,draw_elements_base_vertex} | | +@gl_extension{EXT,draw_elements_base_vertex} | done @gl_extension{EXT,texture_norm16} | done @gl_extension{EXT,texture_sRGB_R8} | done @gl_extension{EXT,texture_sRGB_RG8} | done @@ -500,7 +500,7 @@ Extension | Status @gl_extension{OES,stencil1} | done @gl_extension{OES,stencil4} | done @gl_extension{OES,texture_float_linear} | done -@gl_extension{OES,draw_elements_base_vertex} | | +@gl_extension{OES,draw_elements_base_vertex} | done @gl_extension{OVR,multiview} | | @gl_extension{OVR,multiview2} | | @@ -573,8 +573,8 @@ Extension | Status @webgl_extension{WEBGL,compressed_texture_s3tc_srgb} | done @webgl_extension{WEBGL,multi_draw} | done @webgl_extension{WEBGL,blend_equation_advanced_coherent} | done -@webgl_extension{WEBGL,draw_instanced_base_vertex_base_instance} | | -@webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance} | | +@webgl_extension{WEBGL,draw_instanced_base_vertex_base_instance} | done +@webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance} | done except instanced multi-draw @section opengl-unsupported Unsupported OpenGL features diff --git a/src/Magnum/GL/AbstractShaderProgram.cpp b/src/Magnum/GL/AbstractShaderProgram.cpp index e426d43ed..065fc7dad 100644 --- a/src/Magnum/GL/AbstractShaderProgram.cpp +++ b/src/Magnum/GL/AbstractShaderProgram.cpp @@ -358,10 +358,8 @@ void AbstractShaderProgram::draw(Mesh& mesh) { use(); - #ifndef MAGNUM_TARGET_GLES + #ifndef MAGNUM_TARGET_GLES2 mesh.drawInternal(mesh._count, mesh._baseVertex, mesh._instanceCount, mesh._baseInstance, mesh._indexOffset, mesh._indexStart, mesh._indexEnd); - #elif !defined(MAGNUM_TARGET_GLES2) - mesh.drawInternal(mesh._count, mesh._baseVertex, mesh._instanceCount, mesh._indexOffset, mesh._indexStart, mesh._indexEnd); #else mesh.drawInternal(mesh._count, mesh._baseVertex, mesh._instanceCount, mesh._indexOffset); #endif @@ -375,10 +373,8 @@ void AbstractShaderProgram::draw(MeshView& mesh) { use(); - #ifndef MAGNUM_TARGET_GLES + #ifndef MAGNUM_TARGET_GLES2 mesh._original->drawInternal(mesh._count, mesh._baseVertex, mesh._instanceCount, mesh._baseInstance, mesh._indexOffset, mesh._indexStart, mesh._indexEnd); - #elif !defined(MAGNUM_TARGET_GLES2) - mesh._original->drawInternal(mesh._count, mesh._baseVertex, mesh._instanceCount, mesh._indexOffset, mesh._indexStart, mesh._indexEnd); #else mesh._original->drawInternal(mesh._count, mesh._baseVertex, mesh._instanceCount, mesh._indexOffset); #endif diff --git a/src/Magnum/GL/AbstractShaderProgram.h b/src/Magnum/GL/AbstractShaderProgram.h index b62f58ec1..07cb01f77 100644 --- a/src/Magnum/GL/AbstractShaderProgram.h +++ b/src/Magnum/GL/AbstractShaderProgram.h @@ -863,8 +863,14 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * @requires_gl32 Extension @gl_extension{ARB,draw_elements_base_vertex} * if the mesh is indexed and @ref MeshView::baseVertex() is not * `0` - * @requires_gl Specifying base vertex for indexed meshes is not - * available in OpenGL ES or WebGL. + * @requires_es_extension OpenGL ES 3.0 and extension + * @gl_extension{OES,draw_elements_base_vertex} or + * @gl_extension{EXT,draw_elements_base_vertex} if the mesh is + * indexed and @ref MeshView::baseVertex() is not `0` + * @requires_webgl_extension WebGL 2.0 and extension + * @webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance} + * if the mesh is indexed and @ref MeshView::baseVertex() is not + * `0` */ void draw(Containers::ArrayView> meshes); diff --git a/src/Magnum/GL/Implementation/MeshState.cpp b/src/Magnum/GL/Implementation/MeshState.cpp index eef00a734..1f5075893 100644 --- a/src/Magnum/GL/Implementation/MeshState.cpp +++ b/src/Magnum/GL/Implementation/MeshState.cpp @@ -107,6 +107,102 @@ MeshState::MeshState(Context& context, ContextState& contextState, std::vector()) { + extensions.push_back(Extensions::EXT::draw_elements_base_vertex::string()); + + drawElementsBaseVertexImplementation = glDrawElementsBaseVertexEXT; + #ifndef MAGNUM_TARGET_GLES2 + drawRangeElementsBaseVertexImplementation = glDrawRangeElementsBaseVertexEXT; + drawElementsInstancedBaseVertexImplementation = glDrawElementsInstancedBaseVertexEXT; + #endif + } else if(context.isExtensionSupported()) { + extensions.push_back(Extensions::OES::draw_elements_base_vertex::string()); + + drawElementsBaseVertexImplementation = glDrawElementsBaseVertexOES; + #ifndef MAGNUM_TARGET_GLES2 + drawRangeElementsBaseVertexImplementation = glDrawRangeElementsBaseVertexOES; + drawElementsInstancedBaseVertexImplementation = glDrawElementsInstancedBaseVertexOES; + #endif + } else + #else + if(context.isExtensionSupported()) { + extensions.push_back(Extensions::WEBGL::draw_instanced_base_vertex_base_instance::string()); + + /* The WEBGL extension uses the same entrypoints as the ANGLE extension + it was based on, however we wrap it to supply trivial instance count + because there's no non-instanced variant. Only available since + 1.39.15: https://github.com/emscripten-core/emscripten/pull/11054 */ + #if __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 13915 + drawElementsBaseVertexImplementation = Mesh::drawElementsBaseVertexImplementationANGLE; + drawRangeElementsBaseVertexImplementation = Mesh::drawRangeElementsBaseVertexImplementationANGLE; + drawElementsInstancedBaseVertexImplementation = Mesh::drawElementsInstancedBaseVertexImplementationANGLE; + #else + /* In Context::setupDriverWorkarounds() we make sure the extension is + not even advertised, so this shouldn't be reached. */ + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + #endif + } else + #endif + { + drawElementsBaseVertexImplementation = Mesh::drawElementsBaseVertexImplementationAssert; + #ifndef MAGNUM_TARGET_GLES2 + drawRangeElementsBaseVertexImplementation = Mesh::drawRangeElementsBaseVertexImplementationAssert; + drawElementsInstancedBaseVertexImplementation = Mesh::drawElementsInstancedBaseVertexImplementationAssert; + #endif + } + #endif + + /* Base instance draws on ES3 and WebGL2 */ + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_GLES2) + #ifndef MAGNUM_TARGET_WEBGL + #ifndef MAGNUM_TARGET_GLES2 + if(context.isExtensionSupported()) { + extensions.push_back(Extensions::ANGLE::base_vertex_base_instance::string()); + + drawArraysInstancedBaseInstanceImplementation = glDrawArraysInstancedBaseInstanceANGLE; + /* This variant isn't in the ext, emulated using + glDrawElementsInstancedBaseVertexBaseInstanceANGLE */ + drawElementsInstancedBaseInstanceImplementation = Mesh::drawElementsInstancedBaseInstanceImplementationANGLE; + drawElementsInstancedBaseVertexBaseInstanceImplementation = glDrawElementsInstancedBaseVertexBaseInstanceANGLE; + } else + #endif + #else + if(context.isExtensionSupported()) { + extensions.push_back(Extensions::WEBGL::draw_instanced_base_vertex_base_instance::string()); + + /* The WEBGL extension uses the same entrypoints as the ANGLE extension + it was based on. Only available since 1.39.15: + https://github.com/emscripten-core/emscripten/pull/11054 */ + #if __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 13915 + drawArraysInstancedBaseInstanceImplementation = glDrawArraysInstancedBaseInstanceANGLE; + /* This variant isn't in the ext, emulated using + glDrawElementsInstancedBaseVertexBaseInstanceANGLE */ + drawElementsInstancedBaseInstanceImplementation = Mesh::drawElementsInstancedBaseInstanceImplementationANGLE; + drawElementsInstancedBaseVertexBaseInstanceImplementation = glDrawElementsInstancedBaseVertexBaseInstanceANGLE; + #else + /* In Context::setupDriverWorkarounds() we make sure the extension is + not even advertised, so this shouldn't be reached. */ + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + #endif + } else + #endif + { + drawArraysInstancedBaseInstanceImplementation = Mesh::drawArraysInstancedBaseInstanceImplementationAssert; + drawElementsInstancedBaseInstanceImplementation = Mesh::drawElementsInstancedBaseInstanceImplementationAssert; + drawElementsInstancedBaseVertexBaseInstanceImplementation = Mesh::drawElementsInstancedBaseVertexBaseInstanceImplementationAssert; + } + #endif + #ifdef MAGNUM_TARGET_GLES /* Multi draw implementation on ES. Because there's a lot of dispatch logic involved, the multiDrawImplementationDefault then has internal @@ -149,6 +245,44 @@ MeshState::MeshState(Context& context, ContextState& contextState, std::vector()) { + extensions.push_back(Extensions::EXT::draw_elements_base_vertex::string()); + + multiDrawElementsBaseVertexImplementation = glMultiDrawElementsBaseVertexEXT; + } else if(context.isExtensionSupported()) { + extensions.push_back(Extensions::OES::draw_elements_base_vertex::string()); + + /* Yes, it's really EXT, the same as with + EXT_draw_elements_base_vertex. I have no idea why the two + extensions exist and why it isn't just one. */ + multiDrawElementsBaseVertexImplementation = glMultiDrawElementsBaseVertexEXT; + } else + #else + if(context.isExtensionSupported()) { + extensions.push_back(Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance::string()); + + /* The WEBGL extension uses the same entrypoints as the ANGLE + extension it was based on, however we wrap it and supply trivial + instance counts because there's no non-instanced variant. Only + available since 2.0.5: https://github.com/emscripten-core/emscripten/pull/12282 */ + #if __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 20005 + multiDrawElementsBaseVertexImplementation = MeshView::multiDrawElementsBaseVertexImplementationANGLE; + #else + /* In Context::setupDriverWorkarounds() we make sure the extension + is not even advertised, so this shouldn't be reached. */ + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + #endif + } else + #endif + { + multiDrawElementsBaseVertexImplementation = MeshView::multiDrawElementsBaseVertexImplementationAssert; + } + #endif + multiDrawImplementation = &MeshView::multiDrawImplementationDefault; } else multiDrawImplementation = &MeshView::multiDrawImplementationFallback; diff --git a/src/Magnum/GL/Implementation/MeshState.h b/src/Magnum/GL/Implementation/MeshState.h index 3e5c441dc..30a6034f0 100644 --- a/src/Magnum/GL/Implementation/MeshState.h +++ b/src/Magnum/GL/Implementation/MeshState.h @@ -53,15 +53,37 @@ struct MeshState { void(Mesh::*bindImplementation)(); void(Mesh::*unbindImplementation)(); + #ifdef MAGNUM_TARGET_GLES + #if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) + void(APIENTRY *drawElementsBaseVertexImplementation)(GLenum, GLsizei, GLenum, const void*, GLint); + #endif + #ifndef MAGNUM_TARGET_GLES2 + void(APIENTRY *drawRangeElementsBaseVertexImplementation)(GLenum, GLuint, GLuint, GLsizei, GLenum, const void*, GLint); + #endif + #endif + #ifdef MAGNUM_TARGET_GLES2 void(APIENTRY *drawArraysInstancedImplementation)(GLenum, GLint, GLsizei, GLsizei); + #endif + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_GLES2) + void(APIENTRY *drawArraysInstancedBaseInstanceImplementation)(GLenum, GLint, GLsizei, GLsizei, GLuint); + #endif + #ifdef MAGNUM_TARGET_GLES2 void(APIENTRY *drawElementsInstancedImplementation)(GLenum, GLsizei, GLenum, const void*, GLsizei); #endif + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_GLES2) + void(APIENTRY *drawElementsInstancedBaseVertexImplementation)(GLenum, GLsizei, GLenum, const void*, GLsizei, GLint); + void(APIENTRY *drawElementsInstancedBaseInstanceImplementation)(GLenum, GLsizei, GLenum, const void*, GLsizei, GLuint); + void(APIENTRY *drawElementsInstancedBaseVertexBaseInstanceImplementation)(GLenum, GLsizei, GLenum, const void*, GLsizei, GLint, GLuint); + #endif #ifdef MAGNUM_TARGET_GLES void(*multiDrawImplementation)(Containers::ArrayView>); void(APIENTRY *multiDrawArraysImplementation)(GLenum, const GLint*, const GLsizei*, GLsizei); void(APIENTRY *multiDrawElementsImplementation)(GLenum, const GLsizei*, GLenum, const void* const*, GLsizei); + #if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) + void(APIENTRY *multiDrawElementsBaseVertexImplementation)(GLenum, const GLsizei*, GLenum, const void* const*, GLsizei, const GLint*); + #endif #endif void(*bindVAOImplementation)(GLuint); diff --git a/src/Magnum/GL/Implementation/driverSpecific.cpp b/src/Magnum/GL/Implementation/driverSpecific.cpp index 25f9e311b..6d06c3749 100644 --- a/src/Magnum/GL/Implementation/driverSpecific.cpp +++ b/src/Magnum/GL/Implementation/driverSpecific.cpp @@ -547,6 +547,18 @@ void Context::setupDriverWorkarounds() { #if __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ < 20000 _setRequiredVersion(WEBGL::multi_draw, None); #endif + #ifndef MAGNUM_TARGET_GLES2 + /* WEBGL_multi_draw_instanced_base_vertex_base_instance only since + Emscripten 2.0.5: https://github.com/emscripten-core/emscripten/pull/12282 */ + #if __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ < 20005 + _setRequiredVersion(WEBGL::multi_draw_instanced_base_vertex_base_instance, None); + #endif + /* WEBGL_draw_instanced_base_vertex_base_instance only since Emscripten + 1.39.15: https://github.com/emscripten-core/emscripten/pull/11054 */ + #if __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ < 13915 + _setRequiredVersion(WEBGL::draw_instanced_base_vertex_base_instance, None); + #endif + #endif #endif #undef _setRequiredVersion diff --git a/src/Magnum/GL/Mesh.cpp b/src/Magnum/GL/Mesh.cpp index c3340be0f..3fa160a1f 100644 --- a/src/Magnum/GL/Mesh.cpp +++ b/src/Magnum/GL/Mesh.cpp @@ -401,10 +401,8 @@ Mesh& Mesh::setIndexBuffer(Buffer& buffer, const GLintptr offset, const MeshInde return *this; } -#ifndef MAGNUM_TARGET_GLES +#ifndef MAGNUM_TARGET_GLES2 void Mesh::drawInternal(Int count, Int baseVertex, Int instanceCount, UnsignedInt baseInstance, GLintptr indexOffset, Int indexStart, Int indexEnd) -#elif !defined(MAGNUM_TARGET_GLES2) -void Mesh::drawInternal(Int count, Int baseVertex, Int instanceCount, GLintptr indexOffset, Int indexStart, Int indexEnd) #else void Mesh::drawInternal(Int count, Int baseVertex, Int instanceCount, GLintptr indexOffset) #endif @@ -421,15 +419,30 @@ void Mesh::drawInternal(Int count, Int baseVertex, Int instanceCount, GLintptr i /* Indexed mesh with base vertex */ } else if(baseVertex) { - #ifndef MAGNUM_TARGET_GLES + #if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) + #ifndef MAGNUM_TARGET_GLES2 /* Indexed mesh with specified range */ if(indexEnd) { - glDrawRangeElementsBaseVertex(GLenum(_primitive), indexStart, indexEnd, count, GLenum(_indexType), reinterpret_cast(indexOffset), baseVertex); + #ifndef MAGNUM_TARGET_GLES + glDrawRangeElementsBaseVertex + #else + state.drawRangeElementsBaseVertexImplementation + #endif + (GLenum(_primitive), indexStart, indexEnd, count, GLenum(_indexType), reinterpret_cast(indexOffset), baseVertex); /* Indexed mesh */ - } else glDrawElementsBaseVertex(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), baseVertex); + } else + #endif + { + #ifndef MAGNUM_TARGET_GLES + glDrawElementsBaseVertex + #else + state.drawElementsBaseVertexImplementation + #endif + (GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), baseVertex); + } #else - CORRADE_ASSERT_UNREACHABLE("GL::Mesh::draw(): desktop OpenGL is required for base vertex specification in indexed meshes", ); + CORRADE_ASSERT_UNREACHABLE("GL::AbstractShaderProgram::draw(): indexed mesh draw with base vertex specification possible only since WebGL 2.0", ); #endif /* Indexed mesh */ @@ -457,10 +470,15 @@ void Mesh::drawInternal(Int count, Int baseVertex, Int instanceCount, GLintptr i } else { /* Non-indexed mesh */ if(!_indexBuffer.id()) { - #ifndef MAGNUM_TARGET_GLES + #ifndef MAGNUM_TARGET_GLES2 /* Non-indexed mesh with base instance */ if(baseInstance) { - glDrawArraysInstancedBaseInstance(GLenum(_primitive), baseVertex, count, instanceCount, baseInstance); + #ifndef MAGNUM_TARGET_GLES + glDrawArraysInstancedBaseInstance + #else + state.drawArraysInstancedBaseInstanceImplementation + #endif + (GLenum(_primitive), baseVertex, count, instanceCount, baseInstance); /* Non-indexed mesh */ } else @@ -476,28 +494,40 @@ void Mesh::drawInternal(Int count, Int baseVertex, Int instanceCount, GLintptr i /* Indexed mesh with base vertex */ } else if(baseVertex) { - #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - #ifndef MAGNUM_TARGET_GLES + #ifndef MAGNUM_TARGET_GLES2 /* Indexed mesh with base vertex and base instance */ if(baseInstance) { - glDrawElementsInstancedBaseVertexBaseInstance(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount, baseVertex, baseInstance); + #ifndef MAGNUM_TARGET_GLES + glDrawElementsInstancedBaseVertexBaseInstance + #else + state.drawElementsInstancedBaseVertexBaseInstanceImplementation + #endif + (GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount, baseVertex, baseInstance); /* Indexed mesh with base vertex */ - } else - #endif - { - glDrawElementsInstancedBaseVertex(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount, baseVertex); + } else { + #ifndef MAGNUM_TARGET_GLES + glDrawElementsInstancedBaseVertex + #else + state.drawElementsInstancedBaseVertexImplementation + #endif + (GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount, baseVertex); } #else - CORRADE_ASSERT_UNREACHABLE("GL::Mesh::draw(): OpenGL ES 3.2 or desktop GL is required for base vertex specification in indexed meshes", ); + CORRADE_ASSERT_UNREACHABLE("GL::AbstractShaderProgram::draw(): instanced indexed mesh draw with base vertex specification possible only since OpenGL ES 3.0", ); #endif /* Indexed mesh */ } else { - #ifndef MAGNUM_TARGET_GLES + #ifndef MAGNUM_TARGET_GLES2 /* Indexed mesh with base instance */ if(baseInstance) { - glDrawElementsInstancedBaseInstance(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount, baseInstance); + #ifndef MAGNUM_TARGET_GLES + glDrawElementsInstancedBaseInstance + #else + state.drawElementsInstancedBaseInstanceImplementation + #endif + (GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount, baseInstance); /* Instanced mesh */ } else @@ -873,4 +903,58 @@ void Mesh::unbindImplementationDefault() { void Mesh::unbindImplementationVAO() {} +#ifdef MAGNUM_TARGET_GLES +#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) +#if defined(MAGNUM_TARGET_WEBGL) && __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_patch__ >= 13915 +void Mesh::drawElementsBaseVertexImplementationANGLE(GLenum mode, GLsizei count, GLenum type, const void* indices, GLint baseVertex) { + glDrawElementsInstancedBaseVertexBaseInstanceANGLE(mode, count, type, indices, 1, baseVertex, 0); +} +#endif + +void Mesh::drawElementsBaseVertexImplementationAssert(GLenum, GLsizei, GLenum, const void*, GLint) { + CORRADE_ASSERT_UNREACHABLE("GL::AbstractShaderProgram::draw(): no extension available for indexed mesh draw with base vertex specification", ); +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +#if defined(MAGNUM_TARGET_WEBGL) && __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_patch__ >= 13915 +void Mesh::drawRangeElementsBaseVertexImplementationANGLE(const GLenum mode, GLuint, GLuint, GLsizei count, GLenum type, const void* indices, GLint baseVertex) { + glDrawElementsInstancedBaseVertexBaseInstanceANGLE(mode, count, type, indices, 1, baseVertex, 0); +} +#endif + +void Mesh::drawRangeElementsBaseVertexImplementationAssert(GLenum, GLuint, GLuint, GLsizei, GLenum, const void*, GLint) { + CORRADE_ASSERT_UNREACHABLE("GL::AbstractShaderProgram::draw(): no extension available for indexed mesh draw with base vertex specification", ); +} + +void Mesh::drawArraysInstancedBaseInstanceImplementationAssert(GLenum, GLint, GLsizei, GLsizei, GLuint) { + CORRADE_ASSERT_UNREACHABLE("GL::AbstractShaderProgram::draw(): no extension available for instanced mesh draw with base instance specification", ); +} + +#if !defined(MAGNUM_TARGET_WEBGL) || __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_patch__ >= 13915 +void Mesh::drawElementsInstancedBaseInstanceImplementationANGLE(const GLenum mode, const GLsizei count, const GLenum type, const void* const indices, const GLsizei instanceCount, const GLuint baseInstance) { + glDrawElementsInstancedBaseVertexBaseInstanceANGLE(mode, count, type, indices, instanceCount, 0, baseInstance); +} +#endif + +void Mesh::drawElementsInstancedBaseInstanceImplementationAssert(GLenum, GLsizei, GLenum, const void*, GLsizei, GLuint) { + CORRADE_ASSERT_UNREACHABLE("GL::AbstractShaderProgram::draw(): no extension available for instanced indexed mesh draw with base instance specification", ); +} + +void Mesh::drawElementsInstancedBaseVertexBaseInstanceImplementationAssert(GLenum, GLsizei, GLenum, const void*, GLsizei, GLint, GLuint) { + CORRADE_ASSERT_UNREACHABLE("GL::AbstractShaderProgram::draw(): no extension available for instanced indexed mesh draw with base vertex and base instance specification", ); +} + +#if defined(MAGNUM_TARGET_WEBGL) && __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_patch__ >= 13915 +void Mesh::drawElementsInstancedBaseVertexImplementationANGLE(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instanceCount, GLint baseVertex) { + glDrawElementsInstancedBaseVertexBaseInstanceANGLE(mode, count, type, indices, instanceCount, baseVertex, 0); +} +#endif + +void Mesh::drawElementsInstancedBaseVertexImplementationAssert(GLenum, GLsizei, GLenum, const void*, GLsizei, GLint) { + CORRADE_ASSERT_UNREACHABLE("GL::AbstractShaderProgram::draw(): no extension available for instanced indexed mesh draw with base vertex specification", ); +} +#endif +#endif + }} diff --git a/src/Magnum/GL/Mesh.h b/src/Magnum/GL/Mesh.h index a2c25d402..a60395167 100644 --- a/src/Magnum/GL/Mesh.h +++ b/src/Magnum/GL/Mesh.h @@ -608,8 +608,12 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject { * @see @ref setCount(), @ref setBaseInstance() * @requires_gl32 Extension @gl_extension{ARB,draw_elements_base_vertex} * for indexed meshes - * @requires_gles32 Base vertex cannot be specified for indexed meshes - * in OpenGL ES 3.1 or WebGL. + * @requires_gles32 Extension @gl_extension{OES,draw_elements_base_vertex} + * or @gl_extension{EXT,draw_elements_base_vertex} for indexed + * meshes on OpenGL ES 3.1 and older + * @requires_webgl_extension WebGL 2.0 and extension + * @webgl_extension{WEBGL,draw_instanced_base_vertex_base_instance} + * for indexed meshes */ Mesh& setBaseVertex(Int baseVertex) { _baseVertex = baseVertex; @@ -647,7 +651,7 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject { return *this; } - #ifndef MAGNUM_TARGET_GLES + #ifndef MAGNUM_TARGET_GLES2 /** @brief Base instance */ UnsignedInt baseInstance() const { return _baseInstance; } @@ -659,8 +663,10 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject { * Default is @cpp 0 @ce. * @see @ref setInstanceCount(), @ref setBaseVertex() * @requires_gl42 Extension @gl_extension{ARB,base_instance} - * @requires_gl Base instance cannot be specified in OpenGL ES or - * WebGL. + * @requires_es_extension OpenGL ES 3.1 and extension + * @m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_base_vertex_base_instance.txt) + * @requires_webgl_extension WebGL 2.0 and extension + * @webgl_extension{WEBGL,draw_instanced_base_vertex_base_instance} */ Mesh& setBaseInstance(UnsignedInt baseInstance) { _baseInstance = baseInstance; @@ -1068,10 +1074,8 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject { void MAGNUM_GL_LOCAL bindVAO(); - #ifndef MAGNUM_TARGET_GLES + #ifndef MAGNUM_TARGET_GLES2 void drawInternal(Int count, Int baseVertex, Int instanceCount, UnsignedInt baseInstance, GLintptr indexOffset, Int indexStart, Int indexEnd); - #elif !defined(MAGNUM_TARGET_GLES2) - void drawInternal(Int count, Int baseVertex, Int instanceCount, GLintptr indexOffset, Int indexStart, Int indexEnd); #else void drawInternal(Int count, Int baseVertex, Int instanceCount, GLintptr indexOffset); #endif @@ -1133,6 +1137,36 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject { void MAGNUM_GL_LOCAL unbindImplementationDefault(); void MAGNUM_GL_LOCAL unbindImplementationVAO(); + #ifdef MAGNUM_TARGET_GLES + #if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) + #if defined(MAGNUM_TARGET_WEBGL) && __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 13915 + static void MAGNUM_GL_LOCAL drawElementsBaseVertexImplementationANGLE(GLenum mode, GLsizei count, GLenum type, const void* indices, GLint baseVertex); + #endif + static void MAGNUM_GL_LOCAL drawElementsBaseVertexImplementationAssert(GLenum, GLsizei, GLenum, const void*, GLint); + #endif + + #ifndef MAGNUM_TARGET_GLES2 + #if defined(MAGNUM_TARGET_WEBGL) && __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 13915 + static void MAGNUM_GL_LOCAL drawRangeElementsBaseVertexImplementationANGLE(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices, GLint baseVertex); + #endif + static void MAGNUM_GL_LOCAL drawRangeElementsBaseVertexImplementationAssert(GLenum, GLuint, GLuint, GLsizei, GLenum, const void*, GLint); + + static void MAGNUM_GL_LOCAL drawArraysInstancedBaseInstanceImplementationAssert(GLenum, GLint, GLsizei, GLsizei, GLuint); + + #if !defined(MAGNUM_TARGET_WEBGL) || __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 13915 + static void MAGNUM_GL_LOCAL drawElementsInstancedBaseInstanceImplementationANGLE(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instanceCount, GLuint baseInstance); + #endif + static void MAGNUM_GL_LOCAL drawElementsInstancedBaseInstanceImplementationAssert(GLenum, GLsizei, GLenum, const void*, GLsizei, GLuint); + + static void MAGNUM_GL_LOCAL drawElementsInstancedBaseVertexBaseInstanceImplementationAssert(GLenum, GLsizei, GLenum, const void*, GLsizei, GLint, GLuint); + + #if defined(MAGNUM_TARGET_WEBGL) && __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 13915 + static void MAGNUM_GL_LOCAL drawElementsInstancedBaseVertexImplementationANGLE(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instanceCount, GLint baseVertex); + #endif + static void MAGNUM_GL_LOCAL drawElementsInstancedBaseVertexImplementationAssert(GLenum, GLsizei, GLenum, const void*, GLsizei, GLint); + #endif + #endif + /* _id, _primitive, _flags set from constructors */ GLuint _id; MeshPrimitive _primitive; @@ -1144,10 +1178,8 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject { object is constructed using NoCreate). Also fits in the gap. */ bool _constructed{}; Int _count{}, _baseVertex{}, _instanceCount{1}; - #ifndef MAGNUM_TARGET_GLES - UnsignedInt _baseInstance{}; - #endif #ifndef MAGNUM_TARGET_GLES2 + UnsignedInt _baseInstance{}; UnsignedInt _indexStart{}, _indexEnd{}; #endif GLintptr _indexOffset{}; diff --git a/src/Magnum/GL/MeshView.cpp b/src/Magnum/GL/MeshView.cpp index 0dddaff5c..1b16ce9d2 100644 --- a/src/Magnum/GL/MeshView.cpp +++ b/src/Magnum/GL/MeshView.cpp @@ -26,6 +26,7 @@ #include "MeshView.h" #include +#include #include #include "Magnum/GL/AbstractShaderProgram.h" @@ -95,24 +96,16 @@ void MeshView::multiDrawImplementationDefault(Containers::ArrayView baseVertex{meshes.size()}; /* Gather the parameters */ - #ifndef MAGNUM_TARGET_GLES bool hasBaseVertex = false; - #endif std::size_t i = 0; for(MeshView& mesh: meshes) { - CORRADE_ASSERT(mesh._instanceCount == 1, "GL::MeshView::draw(): cannot draw multiple instanced meshes", ); + CORRADE_ASSERT(mesh._instanceCount == 1, "GL::AbstractShaderProgram::draw(): cannot draw multiple instanced meshes", ); count[i] = mesh._count; indices[i] = reinterpret_cast(mesh._indexOffset); baseVertex[i] = mesh._baseVertex; - if(mesh._baseVertex) { - #ifndef MAGNUM_TARGET_GLES - hasBaseVertex = true; - #else - CORRADE_ASSERT(!original._indexBuffer.id(), "GL::MeshView::draw(): desktop OpenGL is required for base vertex specification in indexed meshes", ); - #endif - } + if(mesh._baseVertex) hasBaseVertex = true; ++i; } @@ -131,14 +124,20 @@ void MeshView::multiDrawImplementationDefault(Containers::ArrayView= 20005 +void MeshView::multiDrawElementsBaseVertexImplementationANGLE(const GLenum mode, const GLsizei* const count, const GLenum type, const void* const* const indices, const GLsizei drawCount, const GLint* const baseVertex) { + /** @todo merge with the allocation in multiDrawImplementationDefault */ + Containers::ArrayView instanceCount; + Containers::ArrayView baseInstance; + Containers::ArrayTuple data{ + {Containers::NoInit, std::size_t(drawCount), instanceCount}, + {Containers::ValueInit, std::size_t(drawCount), baseInstance}, + }; + for(GLsizei& i: instanceCount) i = 1; + + glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE(mode, count, type, indices, instanceCount, baseVertex, baseInstance, drawCount); +} +#endif + +void MeshView::multiDrawElementsBaseVertexImplementationAssert(GLenum, const GLsizei*, GLenum, const void* const*, GLsizei, const GLint*) { + CORRADE_ASSERT_UNREACHABLE("GL::AbstractShaderProgram::draw(): no extension available for indexed mesh multi-draw with base vertex specification", ); +} +#endif + }} diff --git a/src/Magnum/GL/MeshView.h b/src/Magnum/GL/MeshView.h index 1ba06a250..7f1bfa5c4 100644 --- a/src/Magnum/GL/MeshView.h +++ b/src/Magnum/GL/MeshView.h @@ -134,8 +134,12 @@ class MAGNUM_GL_EXPORT MeshView { * @cpp 0 @ce. * @requires_gl32 Extension @gl_extension{ARB,draw_elements_base_vertex} * for indexed meshes - * @requires_gles32 Base vertex cannot be specified for indexed meshes - * in OpenGL ES 3.1 or WebGL. + * @requires_es_extension Extension @gl_extension{OES,draw_elements_base_vertex} + * or @gl_extension{EXT,draw_elements_base_vertex} for indexed + * meshes on OpenGL ES 3.1 and older + * @requires_webgl_extension WebGL 2.0 and extension + * @webgl_extension{WEBGL,draw_instanced_base_vertex_base_instance} + * for indexed meshes */ MeshView& setBaseVertex(Int baseVertex) { _baseVertex = baseVertex; @@ -201,7 +205,7 @@ class MAGNUM_GL_EXPORT MeshView { return *this; } - #ifndef MAGNUM_TARGET_GLES + #ifndef MAGNUM_TARGET_GLES2 /** @brief Base instance */ UnsignedInt baseInstance() const { return _baseInstance; } @@ -212,8 +216,10 @@ class MAGNUM_GL_EXPORT MeshView { * Ignored when calling @ref AbstractShaderProgram::drawTransformFeedback(). * Default is @cpp 0 @ce. * @requires_gl42 Extension @gl_extension{ARB,base_instance} - * @requires_gl Base instance cannot be specified in OpenGL ES or - * WebGL. + * @requires_es_extension OpenGL ES 3.1 and extension + * @m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_base_vertex_base_instance.txt) + * @requires_webgl_extension WebGL 2.0 and extension + * @webgl_extension{WEBGL,draw_instanced_base_vertex_base_instance} */ MeshView& setBaseInstance(UnsignedInt baseInstance) { _baseInstance = baseInstance; @@ -262,11 +268,18 @@ class MAGNUM_GL_EXPORT MeshView { static MAGNUM_GL_LOCAL void multiDrawImplementationFallback(Containers::ArrayView> meshes); #endif + #ifdef MAGNUM_TARGET_GLES + #if defined(MAGNUM_TARGET_WEBGL) && !defined(MAGNUM_TARGET_GLES2) && __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 20005 + static MAGNUM_GL_LOCAL void multiDrawElementsBaseVertexImplementationANGLE(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, GLsizei drawCount, const GLint* baseVertex); + #endif + static MAGNUM_GL_LOCAL void multiDrawElementsBaseVertexImplementationAssert(GLenum, const GLsizei*, GLenum, const void* const*, GLsizei, const GLint*); + #endif + Containers::Reference _original; bool _countSet{}; Int _count{}, _baseVertex{}, _instanceCount{1}; - #ifndef MAGNUM_TARGET_GLES + #ifndef MAGNUM_TARGET_GLES2 UnsignedInt _baseInstance{}; #endif GLintptr _indexOffset{}; diff --git a/src/Magnum/GL/Test/MeshGLTest.cpp b/src/Magnum/GL/Test/MeshGLTest.cpp index 2bba4708b..97dfee031 100644 --- a/src/Magnum/GL/Test/MeshGLTest.cpp +++ b/src/Magnum/GL/Test/MeshGLTest.cpp @@ -137,16 +137,38 @@ struct MeshGLTest: OpenGLTester { void unbindVAOBeforeEnteringExternalSection(); void bindScratchVaoWhenEnteringExternalSection(); - #ifndef MAGNUM_TARGET_GLES + #if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) void setBaseVertex(); #endif + #ifdef MAGNUM_TARGET_GLES + void setBaseVertexNoExtensionAvailable(); + void setBaseVertexRangeNoExtensionAvailable(); + #endif void setInstanceCount(); - void setInstanceCountIndexed(); - #ifndef MAGNUM_TARGET_GLES + #ifndef MAGNUM_TARGET_GLES2 void setInstanceCountBaseInstance(); - void setInstanceCountBaseInstanceIndexed(); - void setInstanceCountBaseVertex(); - void setInstanceCountBaseVertexBaseInstance(); + #ifdef MAGNUM_TARGET_GLES + void setInstanceCountBaseInstanceNoExtensionAvailable(); + #endif + #endif + void setInstanceCountIndexed(); + #ifndef MAGNUM_TARGET_GLES2 + void setInstanceCountIndexedBaseInstance(); + #ifdef MAGNUM_TARGET_GLES + void setInstanceCountIndexedBaseInstanceNoExtensionAvailable(); + #endif + #endif + #ifndef MAGNUM_TARGET_GLES2 + void setInstanceCountIndexedBaseVertex(); + #endif + #ifdef MAGNUM_TARGET_GLES + void setInstanceCountIndexedBaseVertexNoExtensionAvailable(); + #endif + #ifndef MAGNUM_TARGET_GLES2 + void setInstanceCountIndexedBaseVertexBaseInstance(); + #ifdef MAGNUM_TARGET_GLES + void setInstanceCountIndexedBaseVertexBaseInstanceNoExtensionAvailable(); + #endif #endif void addVertexBufferInstancedFloat(); @@ -160,9 +182,13 @@ struct MeshGLTest: OpenGLTester { void multiDraw(); void multiDrawIndexed(); - #ifndef MAGNUM_TARGET_GLES + void multiDrawInstanced(); + #if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) void multiDrawBaseVertex(); #endif + #ifdef MAGNUM_TARGET_GLES + void multiDrawBaseVertexNoExtensionAvailable(); + #endif }; MeshGLTest::MeshGLTest() { @@ -259,16 +285,38 @@ MeshGLTest::MeshGLTest() { &MeshGLTest::unbindVAOBeforeEnteringExternalSection, &MeshGLTest::bindScratchVaoWhenEnteringExternalSection, - #ifndef MAGNUM_TARGET_GLES + #if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) &MeshGLTest::setBaseVertex, #endif + #ifdef MAGNUM_TARGET_GLES + &MeshGLTest::setBaseVertexNoExtensionAvailable, + &MeshGLTest::setBaseVertexRangeNoExtensionAvailable, + #endif &MeshGLTest::setInstanceCount, - &MeshGLTest::setInstanceCountIndexed, - #ifndef MAGNUM_TARGET_GLES + #ifndef MAGNUM_TARGET_GLES2 &MeshGLTest::setInstanceCountBaseInstance, - &MeshGLTest::setInstanceCountBaseInstanceIndexed, - &MeshGLTest::setInstanceCountBaseVertex, - &MeshGLTest::setInstanceCountBaseVertexBaseInstance, + #ifdef MAGNUM_TARGET_GLES + &MeshGLTest::setInstanceCountBaseInstanceNoExtensionAvailable, + #endif + #endif + &MeshGLTest::setInstanceCountIndexed, + #ifndef MAGNUM_TARGET_GLES2 + &MeshGLTest::setInstanceCountIndexedBaseInstance, + #ifdef MAGNUM_TARGET_GLES + &MeshGLTest::setInstanceCountIndexedBaseInstanceNoExtensionAvailable, + #endif + #endif + #ifndef MAGNUM_TARGET_GLES2 + &MeshGLTest::setInstanceCountIndexedBaseVertex, + #endif + #ifdef MAGNUM_TARGET_GLES + &MeshGLTest::setInstanceCountIndexedBaseVertexNoExtensionAvailable, + #endif + #ifndef MAGNUM_TARGET_GLES2 + &MeshGLTest::setInstanceCountIndexedBaseVertexBaseInstance, + #ifdef MAGNUM_TARGET_GLES + &MeshGLTest::setInstanceCountIndexedBaseVertexBaseInstanceNoExtensionAvailable, + #endif #endif &MeshGLTest::addVertexBufferInstancedFloat, @@ -282,8 +330,12 @@ MeshGLTest::MeshGLTest() { &MeshGLTest::multiDraw, &MeshGLTest::multiDrawIndexed, - #ifndef MAGNUM_TARGET_GLES - &MeshGLTest::multiDrawBaseVertex + &MeshGLTest::multiDrawInstanced, + #if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) + &MeshGLTest::multiDrawBaseVertex, + #endif + #ifdef MAGNUM_TARGET_GLES + &MeshGLTest::multiDrawBaseVertexNoExtensionAvailable #endif }); } @@ -1966,7 +2018,6 @@ const Float indexedVertexData[] = { 1.0f, -0.5f }; -#ifndef MAGNUM_TARGET_GLES const Float indexedVertexDataBaseVertex[] = { 0.0f, 0.0f, /* Offset */ @@ -1995,7 +2046,6 @@ const Float indexedVertexDataBaseVertex[] = { 0.4f, 0.0f, -0.9f, 1.0f, -0.5f }; -#endif #ifndef MAGNUM_TARGET_GLES2 constexpr Color4ub indexedResult{64 + 15 + 97, 17 + 164 + 28, 56 + 17, 255}; @@ -2485,10 +2535,19 @@ void MeshGLTest::bindScratchVaoWhenEnteringExternalSection() { #endif } -#ifndef MAGNUM_TARGET_GLES +#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) void MeshGLTest::setBaseVertex() { + #ifndef MAGNUM_TARGET_GLES if(!Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::draw_elements_base_vertex::string() + std::string(" is not available.")); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!Context::current().isExtensionSupported() && + !Context::current().isExtensionSupported()) + CORRADE_SKIP(std::string{"Neither "} + Extensions::OES::draw_elements_base_vertex::string() + " nor " + Extensions::EXT::draw_elements_base_vertex::string() + " is available."); + #else + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::WEBGL::draw_instanced_base_vertex_base_instance::string() + std::string{" is not available."}); + #endif Buffer vertices; vertices.setData(indexedVertexDataBaseVertex, BufferUsage::StaticDraw); @@ -2513,6 +2572,72 @@ void MeshGLTest::setBaseVertex() { } #endif +#ifdef MAGNUM_TARGET_GLES +void MeshGLTest::setBaseVertexNoExtensionAvailable() { + #ifndef MAGNUM_TARGET_WEBGL + if(Context::current().isVersionSupported(Version::GLES320)) + CORRADE_SKIP("OpenGL ES 3.2 is available."); + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::draw_elements_base_vertex::string() + std::string{" is available."}); + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::OES::draw_elements_base_vertex::string() + std::string{" is available."}); + #elif !defined(MAGNUM_TARGET_GLES2) + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::WEBGL::draw_instanced_base_vertex_base_instance::string() + std::string{" is available."}); + #endif + + constexpr UnsignedShort indexData[] = { 2, 1, 0 }; + Buffer indices{Buffer::TargetHint::ElementArray}; + indices.setData(indexData, BufferUsage::StaticDraw); + + Mesh mesh; + mesh.setCount(3) + .setBaseVertex(1) + .setIndexBuffer(indices, 0, MeshIndexType::UnsignedShort); + + std::ostringstream out; + Error redirectError{&out}; + MultipleShader{}.draw(mesh); + #if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) + CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): no extension available for indexed mesh draw with base vertex specification\n"); + #else + CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): indexed mesh draw with base vertex specification possible only since WebGL 2.0\n"); + #endif +} + +void MeshGLTest::setBaseVertexRangeNoExtensionAvailable() { + #ifndef MAGNUM_TARGET_WEBGL + if(Context::current().isVersionSupported(Version::GLES320)) + CORRADE_SKIP("OpenGL ES 3.2 is available."); + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::draw_elements_base_vertex::string() + std::string{" is available."}); + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::OES::draw_elements_base_vertex::string() + std::string{" is available."}); + #elif !defined(MAGNUM_TARGET_GLES2) + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::WEBGL::draw_instanced_base_vertex_base_instance::string() + std::string{" is available."}); + #endif + + constexpr UnsignedShort indexData[] = { 2, 1, 0 }; + Buffer indices{Buffer::TargetHint::ElementArray}; + indices.setData(indexData, BufferUsage::StaticDraw); + + Mesh mesh; + mesh.setCount(3) + .setBaseVertex(1) + .setIndexBuffer(indices, 0, MeshIndexType::UnsignedShort, 0, 2); + + std::ostringstream out; + Error redirectError{&out}; + MultipleShader{}.draw(mesh); + #if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) + CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): no extension available for indexed mesh draw with base vertex specification\n"); + #else + CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): indexed mesh draw with base vertex specification possible only since WebGL 2.0\n"); + #endif +} +#endif + void MeshGLTest::setInstanceCount() { /* Verbatim copy of addVertexBufferFloat() with added extension check and setInstanceCount() call. It would just render three times the same @@ -2561,6 +2686,71 @@ void MeshGLTest::setInstanceCount() { CORRADE_COMPARE(value, 96); } +#ifndef MAGNUM_TARGET_GLES2 +void MeshGLTest::setInstanceCountBaseInstance() { + /* Verbatim copy of setInstanceCount() with additional extension check and + setBaseInstance() call. It would just render three times the same + value. I'm too lazy to invent proper test case, so I'll just check that + it didn't generate any error and rendered something */ + + #ifndef MAGNUM_TARGET_GLES + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::draw_instanced::string() + std::string(" is not available.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::base_instance::string() + std::string(" is not available.")); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ANGLE::base_vertex_base_instance::string() + std::string{" is not available."}); + #else + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::WEBGL::draw_instanced_base_vertex_base_instance::string() + std::string{" is not available."}); + #endif + + typedef Attribute<0, Float> Attribute; + + const Float data[] = { 0.0f, -0.7f, Math::unpack(96) }; + Buffer buffer; + buffer.setData(data, BufferUsage::StaticDraw); + + Mesh mesh; + mesh.setBaseVertex(1) + .setInstanceCount(3) + .setBaseInstance(72) + .addVertexBuffer(buffer, 4, Attribute()); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + const auto value = Checker(FloatShader("float", "vec4(valueInterpolated, 0.0, 0.0, 0.0)"), + RenderbufferFormat::RGBA8, + mesh).get(PixelFormat::RGBA, PixelType::UnsignedByte); + + MAGNUM_VERIFY_NO_GL_ERROR(); + CORRADE_COMPARE(value, 96); +} + +#ifdef MAGNUM_TARGET_GLES +void MeshGLTest::setInstanceCountBaseInstanceNoExtensionAvailable() { + #ifndef MAGNUM_TARGET_WEBGL + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ANGLE::base_vertex_base_instance::string() + std::string{" is available."}); + #else + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::WEBGL::draw_instanced_base_vertex_base_instance::string() + std::string{" is available."}); + #endif + + Mesh mesh; + mesh.setCount(3) + .setInstanceCount(2) + .setBaseInstance(1); + + std::ostringstream out; + Error redirectError{&out}; + MultipleShader{}.draw(mesh); + CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): no extension available for instanced mesh draw with base instance specification\n"); +} +#endif +#endif + void MeshGLTest::setInstanceCountIndexed() { /* Verbatim copy of setIndexBuffer() with added extension check and setInstanceCount() call. It would just render three times the same @@ -2611,50 +2801,25 @@ void MeshGLTest::setInstanceCountIndexed() { CORRADE_COMPARE(value, indexedResult); } -#ifndef MAGNUM_TARGET_GLES -void MeshGLTest::setInstanceCountBaseInstance() { - /* Verbatim copy of setInstanceCount() with additional extension check and - setBaseInstance() call. It would just render three times the same - value. I'm too lazy to invent proper test case, so I'll just check that - it didn't generate any error and rendered something */ - - if(!Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::draw_instanced::string() + std::string(" is not available.")); - if(!Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::base_instance::string() + std::string(" is not available.")); - - typedef Attribute<0, Float> Attribute; - - const Float data[] = { 0.0f, -0.7f, Math::unpack(96) }; - Buffer buffer; - buffer.setData(data, BufferUsage::StaticDraw); - - Mesh mesh; - mesh.setBaseVertex(1) - .setInstanceCount(3) - .setBaseInstance(72) - .addVertexBuffer(buffer, 4, Attribute()); - - MAGNUM_VERIFY_NO_GL_ERROR(); - - const auto value = Checker(FloatShader("float", "vec4(valueInterpolated, 0.0, 0.0, 0.0)"), - RenderbufferFormat::RGBA8, - mesh).get(PixelFormat::RGBA, PixelType::UnsignedByte); - - MAGNUM_VERIFY_NO_GL_ERROR(); - CORRADE_COMPARE(value, 96); -} - -void MeshGLTest::setInstanceCountBaseInstanceIndexed() { +#ifndef MAGNUM_TARGET_GLES2 +void MeshGLTest::setInstanceCountIndexedBaseInstance() { /* Verbatim copy of setInstanceCountIndexed() with additional extension check and setBaseInstance() call. It would just render three times the same value. I'm too lazy to invent proper test case, so I'll just check that it didn't generate any error and rendered something */ + #ifndef MAGNUM_TARGET_GLES if(!Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::draw_instanced::string() + std::string(" is not available.")); if(!Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::base_instance::string() + std::string(" is not available.")); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ANGLE::base_vertex_base_instance::string() + std::string{" is not available."}); + #else + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::WEBGL::draw_instanced_base_vertex_base_instance::string() + std::string{" is not available."}); + #endif Buffer vertices; vertices.setData(indexedVertexData, BufferUsage::StaticDraw); @@ -2679,16 +2844,54 @@ void MeshGLTest::setInstanceCountBaseInstanceIndexed() { CORRADE_COMPARE(value, indexedResult); } -void MeshGLTest::setInstanceCountBaseVertex() { +#ifdef MAGNUM_TARGET_GLES +void MeshGLTest::setInstanceCountIndexedBaseInstanceNoExtensionAvailable() { + #ifndef MAGNUM_TARGET_WEBGL + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ANGLE::base_vertex_base_instance::string() + std::string{" is available."}); + #else + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::WEBGL::draw_instanced_base_vertex_base_instance::string() + std::string{" is available."}); + #endif + + constexpr UnsignedShort indexData[] = { 2, 1, 0 }; + Buffer indices{Buffer::TargetHint::ElementArray}; + indices.setData(indexData, BufferUsage::StaticDraw); + + Mesh mesh; + mesh.setCount(3) + .setInstanceCount(2) + .setBaseInstance(1) + .setIndexBuffer(indices, 0, MeshIndexType::UnsignedShort); + + std::ostringstream out; + Error redirectError{&out}; + MultipleShader{}.draw(mesh); + CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): no extension available for instanced indexed mesh draw with base instance specification\n"); +} +#endif +#endif + +#ifndef MAGNUM_TARGET_GLES2 +void MeshGLTest::setInstanceCountIndexedBaseVertex() { /* Verbatim copy of setBaseVertex() with additional extension check and setInstanceCount() call. It would just render three times the same value. I'm too lazy to invent proper test case, so I'll just check that it didn't generate any error and rendered something */ + #ifndef MAGNUM_TARGET_GLES if(!Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::draw_instanced::string() + std::string(" is not available.")); if(!Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::draw_elements_base_vertex::string() + std::string(" is not available.")); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!Context::current().isExtensionSupported() && + !Context::current().isExtensionSupported()) + CORRADE_SKIP(std::string{"Neither "} + Extensions::OES::draw_elements_base_vertex::string() + " nor " + Extensions::EXT::draw_elements_base_vertex::string() + " is available."); + #else + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::WEBGL::draw_instanced_base_vertex_base_instance::string() + std::string{" is not available."}); + #endif Buffer vertices; vertices.setData(indexedVertexDataBaseVertex, BufferUsage::StaticDraw); @@ -2712,19 +2915,66 @@ void MeshGLTest::setInstanceCountBaseVertex() { MAGNUM_VERIFY_NO_GL_ERROR(); CORRADE_COMPARE(value, indexedResult); } +#endif + +#ifdef MAGNUM_TARGET_GLES +void MeshGLTest::setInstanceCountIndexedBaseVertexNoExtensionAvailable() { + #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(Context::current().isVersionSupported(Version::GLES320)) + CORRADE_SKIP("OpenGL ES 3.2 is available."); + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::draw_elements_base_vertex::string() + std::string{" is available."}); + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::OES::draw_elements_base_vertex::string() + std::string{" is available."}); + #else + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::WEBGL::draw_instanced_base_vertex_base_instance::string() + std::string{" is available."}); + #endif + #endif -void MeshGLTest::setInstanceCountBaseVertexBaseInstance() { + constexpr UnsignedShort indexData[] = { 2, 1, 0 }; + Buffer indices{Buffer::TargetHint::ElementArray}; + indices.setData(indexData, BufferUsage::StaticDraw); + + Mesh mesh; + mesh.setCount(3) + .setInstanceCount(2) + .setBaseVertex(1) + .setIndexBuffer(indices, 0, MeshIndexType::UnsignedShort); + + std::ostringstream out; + Error redirectError{&out}; + MultipleShader{}.draw(mesh); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): no extension available for instanced indexed mesh draw with base vertex specification\n"); + #else + CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): instanced indexed mesh draw with base vertex specification possible only since OpenGL ES 3.0\n"); + #endif +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +void MeshGLTest::setInstanceCountIndexedBaseVertexBaseInstance() { /* Verbatim copy of setInstanceCountBaseVertex() with added extension check and setBaseInstance() call. It would just render three times the same value. I'm too lazy to invent proper test case, so I'll just check that it didn't generate any error and rendered something */ + #ifndef MAGNUM_TARGET_GLES if(!Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::draw_instanced::string() + std::string(" is not available.")); if(!Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::draw_elements_base_vertex::string() + std::string(" is not available.")); if(!Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::base_instance::string() + std::string(" is not available.")); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ANGLE::base_vertex_base_instance::string() + std::string{" is not available."}); + #else + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::WEBGL::draw_instanced_base_vertex_base_instance::string() + std::string{" is not available."}); + #endif Buffer vertices; vertices.setData(indexedVertexDataBaseVertex, BufferUsage::StaticDraw); @@ -2749,6 +2999,38 @@ void MeshGLTest::setInstanceCountBaseVertexBaseInstance() { MAGNUM_VERIFY_NO_GL_ERROR(); CORRADE_COMPARE(value, indexedResult); } + +#ifdef MAGNUM_TARGET_GLES +void MeshGLTest::setInstanceCountIndexedBaseVertexBaseInstanceNoExtensionAvailable() { + #ifndef MAGNUM_TARGET_WEBGL + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ANGLE::base_vertex_base_instance::string() + std::string{" is available."}); + #else + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::WEBGL::draw_instanced_base_vertex_base_instance::string() + std::string{" is available."}); + #endif + + constexpr UnsignedShort indexData[] = { 2, 1, 0 }; + Buffer indices{Buffer::TargetHint::ElementArray}; + indices.setData(indexData, BufferUsage::StaticDraw); + + Mesh mesh; + mesh.setCount(3) + .setInstanceCount(2) + .setBaseVertex(1) + .setBaseInstance(1) + .setIndexBuffer(indices, 0, MeshIndexType::UnsignedShort); + + std::ostringstream out; + Error redirectError{&out}; + MultipleShader{}.draw(mesh); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): no extension available for instanced indexed mesh draw with base vertex and base instance specification\n"); + #else + CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): instanced indexed mesh draw with base vertex specification possible only since OpenGL 3.0\n"); + #endif +} +#endif #endif void MeshGLTest::addVertexBufferInstancedFloat() { @@ -3079,10 +3361,31 @@ void MeshGLTest::multiDrawIndexed() { CORRADE_COMPARE(value, indexedResult); } -#ifndef MAGNUM_TARGET_GLES +void MeshGLTest::multiDrawInstanced() { + Mesh mesh; + MeshView view{mesh}; + view.setCount(3) + .setInstanceCount(2); + + std::ostringstream out; + Error redirectError{&out}; + MultipleShader{}.draw({view, view}); + CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): cannot draw multiple instanced meshes\n"); +} + +#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) void MeshGLTest::multiDrawBaseVertex() { + #ifndef MAGNUM_TARGET_GLES if(!Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::draw_elements_base_vertex::string() + std::string(" is not available.")); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!Context::current().isExtensionSupported() && + !Context::current().isExtensionSupported()) + CORRADE_SKIP(std::string{"Neither "} + Extensions::OES::draw_elements_base_vertex::string() + " nor " + Extensions::EXT::draw_elements_base_vertex::string() + " is available."); + #else + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance::string() + std::string{" is not available."}); + #endif Buffer vertices; vertices.setData(indexedVertexDataBaseVertex, BufferUsage::StaticDraw); @@ -3106,6 +3409,53 @@ void MeshGLTest::multiDrawBaseVertex() { } #endif +#ifdef MAGNUM_TARGET_GLES +void MeshGLTest::multiDrawBaseVertexNoExtensionAvailable() { + #ifdef MAGNUM_TARGET_GLES + /* If the multidraw extensions aren't available, we can't test this assert, + only the assert in the fallback path, which is already tested above. */ + #ifndef MAGNUM_TARGET_WEBGL + if(!Context::current().isExtensionSupported() && + !Context::current().isExtensionSupported()) + CORRADE_SKIP(std::string{"Neither "} + Extensions::EXT::multi_draw_arrays::string() + " nor " + Extensions::ANGLE::multi_draw::string() + " is available."); + #else + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::WEBGL::multi_draw::string() + std::string{" is not available."}); + #endif + #endif + + #ifndef MAGNUM_TARGET_WEBGL + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::draw_elements_base_vertex::string() + std::string{" is available."}); + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::OES::draw_elements_base_vertex::string() + std::string{" is available."}); + #elif !defined(MAGNUM_TARGET_GLES2) + if(Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance::string() + std::string{" is available."}); + #endif + + constexpr UnsignedShort indexData[] = { 2, 1, 0 }; + Buffer indices{Buffer::TargetHint::ElementArray}; + indices.setData(indexData, BufferUsage::StaticDraw); + + Mesh mesh; + mesh.setIndexBuffer(indices, 0, MeshIndexType::UnsignedShort); + + MeshView view{mesh}; + view.setCount(3) + .setBaseVertex(1); + + std::ostringstream out; + Error redirectError{&out}; + MultipleShader{}.draw({view, view}); + #if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) + CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): no extension available for indexed mesh multi-draw with base vertex specification\n"); + #else + CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): indexed mesh multi-draw with base vertex specification possible only since WebGL 2.0\n"); + #endif +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::GL::Test::MeshGLTest) From 755d97121cbc6d1edd420f0dd01091f676c6d255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 16 Feb 2021 19:50:32 +0100 Subject: [PATCH 43/44] doc: discourage using MeshIndexType::UnsignedByte. --- src/Magnum/GL/Mesh.h | 15 ++++++++++++--- src/Magnum/Mesh.h | 13 +++++++------ src/Magnum/Vk/Mesh.h | 9 ++++++++- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/Magnum/GL/Mesh.h b/src/Magnum/GL/Mesh.h index a60395167..e236b523c 100644 --- a/src/Magnum/GL/Mesh.h +++ b/src/Magnum/GL/Mesh.h @@ -172,11 +172,20 @@ MAGNUM_GL_EXPORT MeshPrimitive meshPrimitive(Magnum::MeshPrimitive primitive); @m_enum_values_as_keywords */ enum class MeshIndexType: GLenum { - UnsignedByte = GL_UNSIGNED_BYTE, /**< Unsigned byte */ - UnsignedShort = GL_UNSIGNED_SHORT, /**< Unsigned short */ + /** + * @relativeref{Magnum,UnsignedByte}. + * + * Even though OpenGL historically supports 8-bit indices, using this type + * is discouraged on contemporary GPU architectures. Prefer using 16-bit + * indices instead. + */ + UnsignedByte = GL_UNSIGNED_BYTE, + + /** @relativeref{Magnum,UnsignedShort} */ + UnsignedShort = GL_UNSIGNED_SHORT, /** - * Unsigned int + * @relativeref{Magnum,UnsignedInt} * @requires_gles30 Extension @gl_extension{OES,element_index_uint} * in OpenGL ES 2.0. * @requires_webgl20 Extension @webgl_extension{OES,element_index_uint} diff --git a/src/Magnum/Mesh.h b/src/Magnum/Mesh.h index 36b4b9351..5ef337e51 100644 --- a/src/Magnum/Mesh.h +++ b/src/Magnum/Mesh.h @@ -249,17 +249,18 @@ enum class MeshIndexType: UnsignedByte { /* Zero reserved for an invalid type (but not being a named value) */ /** - * Unsigned byte + * @relativeref{Magnum,UnsignedByte}. * * Corresponds to @ref GL::MeshIndexType::UnsignedByte / - * @ref Vk::MeshIndexTyúe::UnsignedByte. Note that using this - * type is discouraged, at least AMD GPUs are known to suggest (via debug - * output) using 16-byte types instead for better efficiency. + * @ref Vk::MeshIndexType::UnsignedByte. Even though OpenGL supports this + * type and Vulkan can as well via an extension, using this type is + * discouraged on contemporary GPU architectures. Prefer using 16-bit + * indices instead. */ UnsignedByte = 1, /** - * Unsigned short + * @relativeref{Magnum,UnsignedShort}. * * Corresponds to @ref GL::MeshIndexType::UnsignedShort / * @ref Vk::MeshIndexType::UnsignedShort. @@ -267,7 +268,7 @@ enum class MeshIndexType: UnsignedByte { UnsignedShort, /** - * Unsigned int + * @relativeref{Magnum,UnsignedInt}. * * Corresponds to @ref GL::MeshIndexType::UnsignedInt / * @ref Vk::MeshIndexType::UnsignedInt. diff --git a/src/Magnum/Vk/Mesh.h b/src/Magnum/Vk/Mesh.h index c6a821ce4..2ce1a7829 100644 --- a/src/Magnum/Vk/Mesh.h +++ b/src/Magnum/Vk/Mesh.h @@ -44,7 +44,14 @@ Wraps a @type_vk_keyword{IndexType}. */ enum class MeshIndexType: Int { /** - * @ref Magnum::UnsignedByte "UnsignedByte" + * @ref Magnum::UnsignedByte "UnsignedByte". + * + * @m_class{m-note m-success} + * + * @par + * Discouraged on contemporary GPU architectures, prefer to use 16-bit + * indices instead. + * * @requires_vk_feature @ref DeviceFeature::IndexTypeUnsignedByte */ UnsignedByte = VK_INDEX_TYPE_UINT8_EXT, From df6da797ab3c5a71e519438df68c8b8964b385b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 16 Feb 2021 19:51:02 +0100 Subject: [PATCH 44/44] package: toggle more ES extensions for higher test coverage. --- package/archlinux/PKGBUILD-es2 | 4 +++- package/archlinux/PKGBUILD-es2desktop | 4 +++- package/archlinux/PKGBUILD-es3 | 4 +++- package/archlinux/PKGBUILD-es3desktop | 4 +++- package/ci/unix-desktop-gles.sh | 5 ++++- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/package/archlinux/PKGBUILD-es2 b/package/archlinux/PKGBUILD-es2 index 8ce8e50ec..9074df035 100644 --- a/package/archlinux/PKGBUILD-es2 +++ b/package/archlinux/PKGBUILD-es2 @@ -53,7 +53,9 @@ build() { check() { cd "$_rootdir/build-es2" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 - MAGNUM_DISABLE_EXTENSIONS="GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_OES_vertex_array_object GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_OES_vertex_array_object GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex GL_OES_draw_elements_base_vertex" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_OES_vertex_array_object GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex GL_OES_draw_elements_base_vertex GL_EXT_multi_draw_arrays GL_ANGLE_multi_draw" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest } diff --git a/package/archlinux/PKGBUILD-es2desktop b/package/archlinux/PKGBUILD-es2desktop index a756ba744..99b1fdcde 100644 --- a/package/archlinux/PKGBUILD-es2desktop +++ b/package/archlinux/PKGBUILD-es2desktop @@ -57,7 +57,9 @@ build() { check() { cd "$_rootdir/build-es2desktop" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 - MAGNUM_DISABLE_EXTENSIONS="GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_OES_vertex_array_object GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_OES_vertex_array_object GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex GL_OES_draw_elements_base_vertex" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_OES_vertex_array_object GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex GL_OES_draw_elements_base_vertex GL_EXT_multi_draw_arrays GL_ANGLE_multi_draw" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest } diff --git a/package/archlinux/PKGBUILD-es3 b/package/archlinux/PKGBUILD-es3 index 71c276d26..62eaedb6f 100644 --- a/package/archlinux/PKGBUILD-es3 +++ b/package/archlinux/PKGBUILD-es3 @@ -53,7 +53,9 @@ build() { check() { cd "$_rootdir/build-es3" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 - MAGNUM_DISABLE_EXTENSIONS="GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex GL_OES_draw_elements_base_vertex GL_ANGLE_base_vertex_base_instance" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex GL_OES_draw_elements_base_vertex GL_ANGLE_base_vertex_base_instance GL_EXT_multi_draw_arrays GL_ANGLE_multi_draw" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest } diff --git a/package/archlinux/PKGBUILD-es3desktop b/package/archlinux/PKGBUILD-es3desktop index 3f0b1ebb5..08a949144 100644 --- a/package/archlinux/PKGBUILD-es3desktop +++ b/package/archlinux/PKGBUILD-es3desktop @@ -57,7 +57,9 @@ build() { check() { cd "$_rootdir/build-es3desktop" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 - MAGNUM_DISABLE_EXTENSIONS="GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex GL_OES_draw_elements_base_vertex GL_ANGLE_base_vertex_base_instance" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex GL_OES_draw_elements_base_vertex GL_ANGLE_base_vertex_base_instance GL_EXT_multi_draw_arrays GL_ANGLE_multi_draw" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest } diff --git a/package/ci/unix-desktop-gles.sh b/package/ci/unix-desktop-gles.sh index 57efd2486..80a153493 100755 --- a/package/ci/unix-desktop-gles.sh +++ b/package/ci/unix-desktop-gles.sh @@ -57,7 +57,10 @@ cmake .. \ -G Ninja ninja $NINJA_JOBS CORRADE_TEST_COLOR=ON ctest -V -if [ "$TARGET_GLES2" == "ON" ]; then CORRADE_TEST_COLOR=ON MAGNUM_DISABLE_EXTENSIONS="OES_vertex_array_object" ctest -V -R GLTest; fi +MAGNUM_DISABLE_EXTENSIONS="GL_OES_vertex_array_object GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest +MAGNUM_DISABLE_EXTENSIONS="GL_OES_vertex_array_object GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex GL_OES_draw_elements_base_vertex GL_ANGLE_base_vertex_base_instance" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest +MAGNUM_DISABLE_EXTENSIONS="GL_OES_vertex_array_object GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex GL_OES_draw_elements_base_vertex GL_ANGLE_base_vertex_base_instance GL_EXT_multi_draw_arrays GL_ANGLE_multi_draw" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest +MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest Debug/bin/magnum-gl-info --limits > /dev/null # Test install, after running the tests as for them it shouldn't be needed