diff --git a/doc/changelog.dox b/doc/changelog.dox index b6bc3b172..ad9f3c3a8 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -62,11 +62,16 @@ 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 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 with raw GL code - Exposed @gl_extension{ARB,buffer_storage} as @@ -76,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 @@ -170,6 +178,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 @@ -378,6 +390,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 @@ -430,6 +447,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/doc/opengl-support.dox b/doc/opengl-support.dox index 9a30dac23..9c8ce2efa 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) | 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) | 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 @@ -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} | done @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} | done @gl_extension{OVR,multiview} | | @gl_extension{OVR,multiview2} | | @@ -567,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} | 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/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/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/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index 6cf1ecffc..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" @@ -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{}))); @@ -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 2483db162..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** | | @@ -614,6 +613,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 +630,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 @@ -878,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 7215f8a66..9791802f3 100644 --- a/doc/vulkan-support.dox +++ b/doc/vulkan-support.dox @@ -122,7 +122,9 @@ 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} | | @vk_extension{KHR,portability_subset} | done except properties @vk_extension{KHR,deferred_host_operations} | | diff --git a/doc/vulkan-wrapping.dox b/doc/vulkan-wrapping.dox index b64d5873f..cd369c011 100644 --- a/doc/vulkan-wrapping.dox +++ b/doc/vulkan-wrapping.dox @@ -188,5 +188,26 @@ 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}. +- 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/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 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/GL/AbstractShaderProgram.cpp b/src/Magnum/GL/AbstractShaderProgram.cpp index ee21e77cf..065fc7dad 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; @@ -363,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 @@ -380,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 @@ -508,12 +499,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 +517,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 804ddef34..07cb01f77 100644 --- a/src/Magnum/GL/AbstractShaderProgram.h +++ b/src/Magnum/GL/AbstractShaderProgram.h @@ -840,9 +840,13 @@ 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 - * @ref draw(MeshView&) calls. + * 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. 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 @@ -859,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); @@ -1343,6 +1353,14 @@ 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 + #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/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/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/Magnum/GL/Implementation/MeshState.cpp b/src/Magnum/GL/Implementation/MeshState.cpp index 955697e52..1f5075893 100644 --- a/src/Magnum/GL/Implementation/MeshState.cpp +++ b/src/Magnum/GL/Implementation/MeshState.cpp @@ -107,17 +107,185 @@ 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 + extension-specific codepaths based on whether EXT, OES, ANGLE or + whichever entrypoints are supported. */ #ifndef MAGNUM_TARGET_WEBGL - /* Multi draw implementation on ES */ - if(context.isExtensionSupported()) { - 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. Only available since 2.0.0: + https://github.com/emscripten-core/emscripten/pull/11650 */ + #if __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 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 + + /* These function pointers make sense only if the general multi-draw + extension is supported. Also, not on WebGL 1 at all. */ + #if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) + #ifndef MAGNUM_TARGET_WEBGL + if(context.isExtensionSupported()) { + 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; - #else - multiDrawImplementation = &MeshView::multiDrawImplementationFallback; - #endif #endif #ifdef MAGNUM_TARGET_GLES2 @@ -125,8 +293,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() || @@ -135,8 +303,8 @@ MeshState::MeshState(Context& context, ContextState& contextState, std::vector() || context.isExtensionSupported()) { @@ -144,8 +312,8 @@ MeshState::MeshState(Context& context, ContextState& contextState, std::vector>); + 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/ShaderProgramState.cpp b/src/Magnum/GL/Implementation/ShaderProgramState.cpp index 24fe23f34..99ca2545f 100644 --- a/src/Magnum/GL/Implementation/ShaderProgramState.cpp +++ b/src/Magnum/GL/Implementation/ShaderProgramState.cpp @@ -60,6 +60,20 @@ 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 370c97fd3..6d06c3749 100644 --- a/src/Magnum/GL/Implementation/driverSpecific.cpp +++ b/src/Magnum/GL/Implementation/driverSpecific.cpp @@ -40,6 +40,13 @@ namespace { /* Search the code for the following strings to see where they are implemented. */ const char* KnownWorkarounds[]{ /* [workarounds] */ +#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) +/* ANGLE's shader linker insists on returning a message consisting of a + single newline on success, causing annoying noise in the console. Similar to + "intel-windows-chatty-shader-compiler". */ +"angle-chatty-shader-compiler", +#endif + #if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES) /* Calling glBufferData(), glMapBuffer(), glMapBufferRange() or glUnmapBuffer() on ANY buffer when ANY buffer is attached to a currently bound @@ -336,6 +343,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. See also "angle-chatty-shader-compiler". */ +"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 +393,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; @@ -560,6 +539,28 @@ 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 + #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 #ifndef MAGNUM_TARGET_GLES diff --git a/src/Magnum/GL/Mesh.cpp b/src/Magnum/GL/Mesh.cpp index eb5a37cd9..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,56 +470,75 @@ 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 #endif { #ifndef MAGNUM_TARGET_GLES2 - glDrawArraysInstanced(GLenum(_primitive), baseVertex, count, instanceCount); + glDrawArraysInstanced #else - (this->*state.drawArraysInstancedImplementation)(baseVertex, count, instanceCount); + state.drawArraysInstancedImplementation #endif + (GLenum(_primitive), baseVertex, count, instanceCount); } /* 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 #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,32 +903,56 @@ 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); +#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 -#ifndef MAGNUM_TARGET_WEBGL -void Mesh::drawArraysInstancedImplementationEXT(const GLint baseVertex, const GLsizei count, const GLsizei instanceCount) { - glDrawArraysInstancedEXT(GLenum(_primitive), baseVertex, count, instanceCount); +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 -void Mesh::drawArraysInstancedImplementationNV(const GLint baseVertex, const GLsizei count, const GLsizei instanceCount) { - glDrawArraysInstancedNV(GLenum(_primitive), baseVertex, count, instanceCount); +#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::drawElementsInstancedImplementationANGLE(const GLsizei count, const GLintptr indexOffset, const GLsizei instanceCount) { - glDrawElementsInstancedANGLE(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount); +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", ); } -#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::drawArraysInstancedBaseInstanceImplementationAssert(GLenum, GLint, GLsizei, GLsizei, GLuint) { + CORRADE_ASSERT_UNREACHABLE("GL::AbstractShaderProgram::draw(): no extension available for instanced mesh draw with base instance specification", ); } -void Mesh::drawElementsInstancedImplementationNV(const GLsizei count, const GLintptr indexOffset, const GLsizei instanceCount) { - glDrawElementsInstancedNV(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount); +#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 ad78ce5f5..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} @@ -608,8 +617,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 +660,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 +672,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 +1083,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,17 +1146,33 @@ 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); + #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 - 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); + #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 @@ -1158,10 +1187,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 76a5b42c3..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" @@ -84,7 +85,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()); @@ -96,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; } @@ -123,33 +115,40 @@ void MeshView::multiDrawImplementationDefault(Containers::ArrayView> meshes) { @@ -157,10 +156,10 @@ void MeshView::multiDrawImplementationFallback(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 c79a513cb..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; @@ -257,16 +263,23 @@ 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 + + #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/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; diff --git a/src/Magnum/GL/Test/MeshGLTest.cpp b/src/Magnum/GL/Test/MeshGLTest.cpp index 2ecaac024..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 + + 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 -void MeshGLTest::setInstanceCountBaseVertexBaseInstance() { +#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() { @@ -3005,9 +3287,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 +3330,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; @@ -3067,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); @@ -3094,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) 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/Magnum/Mesh.h b/src/Magnum/Mesh.h index 620f914fb..5ef337e51 100644 --- a/src/Magnum/Mesh.h +++ b/src/Magnum/Mesh.h @@ -240,38 +240,38 @@ 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 { /* Zero reserved for an invalid type (but not being a named value) */ /** - * Unsigned byte + * @relativeref{Magnum,UnsignedByte}. * * Corresponds to @ref GL::MeshIndexType::UnsignedByte / - * @val_vk_keyword{INDEX_TYPE_UINT8_EXT,IndexType}. 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 / - * @val_vk_keyword{INDEX_TYPE_UINT16,IndexType}. + * @ref Vk::MeshIndexType::UnsignedShort. */ UnsignedShort, /** - * Unsigned int + * @relativeref{Magnum,UnsignedInt}. * * Corresponds to @ref GL::MeshIndexType::UnsignedInt / - * @val_vk_keyword{INDEX_TYPE_UINT32,IndexType}. + * @ref Vk::MeshIndexType::UnsignedInt. */ UnsignedInt }; 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/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..11c51889b 100644 --- a/src/Magnum/ShaderTools/CMakeLists.txt +++ b/src/Magnum/ShaderTools/CMakeLists.txt @@ -30,14 +30,19 @@ 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) +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) @@ -46,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") @@ -59,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/ShaderTools.h b/src/Magnum/ShaderTools/ShaderTools.h index 3d901a533..6440c1d65 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,11 +29,14 @@ * @brief Forward declarations for the @ref Magnum::ShaderTools namespace */ -namespace Magnum { namespace Trade { +#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..9351679d1 --- /dev/null +++ b/src/Magnum/ShaderTools/Stage.cpp @@ -0,0 +1,61 @@ +/* + 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) + _c(Kernel) + #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..b4b78f027 --- /dev/null +++ b/src/Magnum/ShaderTools/Stage.h @@ -0,0 +1,97 @@ +#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 */ + + /** + * 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 +}; + +/** +@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..bb15276e9 100644 --- a/src/Magnum/ShaderTools/Test/CMakeLists.txt +++ b/src/Magnum/ShaderTools/Test/CMakeLists.txt @@ -37,9 +37,15 @@ 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(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..fb5fe774f --- /dev/null +++ b/src/Magnum/ShaderTools/Test/SpirvTest.cpp @@ -0,0 +1,265 @@ +/* + 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", {}}, + /* 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)} +}; + +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 000000000..1b90af3a0 Binary files /dev/null and b/src/Magnum/ShaderTools/Test/SpirvTestFiles/entrypoint-interface.spv differ 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 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..ea4098ef9 100644 --- a/src/Magnum/ShaderTools/shaderconverter.cpp +++ b/src/Magnum/ShaderTools/shaderconverter.cpp @@ -33,6 +33,8 @@ #include "Magnum/Implementation/converterUtilities.h" #include "Magnum/Math/Functions.h" #include "Magnum/ShaderTools/AbstractConverter.h" +#include "Magnum/ShaderTools/Stage.h" +#include "Magnum/ShaderTools/Implementation/spirv.h" namespace Magnum { @@ -62,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 @@ -74,9 +76,10 @@ magnum-shaderconverter [-h|--help] [--validate] [--link] Arguments: - `input` --- input file(s) -- `output` --- output file, ignored if `--validate` is present. 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. @@ -86,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 @@ -169,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, ignored if --validate is present") + .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") @@ -192,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 @@ -231,11 +297,17 @@ 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; } + + /* 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) { @@ -263,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{} : @@ -285,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; @@ -334,7 +420,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; } @@ -359,7 +445,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; } @@ -368,7 +454,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; } @@ -386,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")) { @@ -393,7 +505,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; } @@ -416,7 +528,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; } @@ -440,7 +552,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; } 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/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])}; 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.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..0fb488259 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,12 +373,24 @@ 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); + /** + * @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 @@ -399,6 +423,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 = {}); @@ -744,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); @@ -765,6 +819,7 @@ class MAGNUM_VK_EXPORT CommandBuffer { VkCommandPool _pool; /* Used only for vkFreeCommandBuffers() */ VkCommandBuffer _handle; HandleFlags _flags; + DynamicRasterizationStates _dynamicRasterizationStates; }; }} diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index 80c4072ba..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, @@ -412,6 +414,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 +537,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..1d5d4c34c 100644 --- a/src/Magnum/Vk/DeviceFeatures.h +++ b/src/Magnum/Vk/DeviceFeatures.h @@ -59,18 +59,22 @@ 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, /** * Whether the full 32-bit range is supported for indexed draw calls when - * using @val_vk{INDEX_TYPE_UINT32,IndexType}. - * @see @ref DeviceFeature::IndexTypeUint8 + * using @ref MeshIndexType::UnsignedInt. + * @see @ref DeviceFeature::IndexTypeUnsignedByte * @todo expose the `maxDrawIndexedIndexValue` limit */ - FullDrawIndexUint32, + FullDrawIndexUnsignedInt, /** * Whether image views with @val_vk{IMAGE_VIEW_TYPE_CUBE_ARRAY,ImageViewType} @@ -1267,17 +1271,17 @@ 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 */ /** * The @ref DynamicRasterizationState::CullMode, * @relativeref{DynamicRasterizationState,FrontFace}, - * @relativeref{DynamicRasterizationState,PrimitiveTopology}, + * @relativeref{DynamicRasterizationState,MeshPrimitive}, * @relativeref{DynamicRasterizationState,ViewportWithCount}, * @relativeref{DynamicRasterizationState,ScissorWithCount}, * @relativeref{DynamicRasterizationState,VertexInputBindingStride}, @@ -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..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 */ @@ -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/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/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/DeviceState.cpp b/src/Magnum/Vk/Implementation/DeviceState.cpp index ffbdd3e4b..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" @@ -85,6 +86,12 @@ DeviceState::DeviceState(Device& device, Containers::Array()) { + cmdBindVertexBuffersImplementation = &CommandBuffer::bindVertexBuffersImplementationEXT; + } else { + cmdBindVertexBuffersImplementation = &CommandBuffer::bindVertexBuffersImplementationDefault; + } + if(device.isExtensionEnabled()) { cmdCopyBufferImplementation = &CommandBuffer::copyBufferImplementationKHR; cmdCopyImageImplementation = &CommandBuffer::copyImageImplementationKHR; @@ -107,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/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..2ce1a7829 --- /dev/null +++ b/src/Magnum/Vk/Mesh.h @@ -0,0 +1,413 @@ +#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". + * + * @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, + + /** @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.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..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 @@ -307,7 +308,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 +347,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 +373,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() { @@ -409,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/Pipeline.cpp b/src/Magnum/Vk/Pipeline.cpp index 05f2d80ba..217d9fec0 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" @@ -42,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; @@ -241,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; } @@ -277,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 @@ -293,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 */ @@ -303,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 = {}; } @@ -324,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 = {}; @@ -386,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; } @@ -451,4 +481,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/Pipeline.h b/src/Magnum/Vk/Pipeline.h index 554968675..694958db8 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; }; /** @@ -317,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 @@ -332,12 +350,23 @@ 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 */ + /** + * 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 }; @@ -505,6 +534,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, @@ -512,6 +548,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, @@ -596,7 +639,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 * @@ -654,7 +700,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 @@ -722,7 +771,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 diff --git a/src/Magnum/Vk/RasterizationPipelineCreateInfo.h b/src/Magnum/Vk/RasterizationPipelineCreateInfo.h index b647f5255..34ce4ddbf 100644 --- a/src/Magnum/Vk/RasterizationPipelineCreateInfo.h +++ b/src/Magnum/Vk/RasterizationPipelineCreateInfo.h @@ -159,12 +159,13 @@ 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} */ - PrimitiveTopology, + MeshPrimitive, /** * Both the number of viewports and their ranges set in @@ -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, @@ -273,6 +274,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 @@ -512,6 +525,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/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 6655ef8fa..7cce44722 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) @@ -49,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) @@ -140,6 +144,7 @@ set_target_properties( VkIntegrationTest VkLayerPropertiesTest VkMemoryTest + VkMeshTest VkMeshLayoutTest VkPipelineTest VkPipelineLayoutTest @@ -148,20 +153,41 @@ set_target_properties( VkResultTest VkRenderPassTest VkShaderTest + VkShaderSetTest VkStructureHelpersTest VkVersionTest VkVertexFormatTest 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 +202,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 +249,7 @@ if(BUILD_VK_TESTS) VkImageViewVkTest VkInstanceVkTest VkMemoryVkTest + VkMeshVkTest VkPipelineVkTest VkPipelineLayoutVkTest VkQueueVkTest 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"); } }}}} 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/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/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}; 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 000000000..a833ae877 Binary files /dev/null and b/src/Magnum/Vk/Test/MeshTestFiles/flat.spv differ 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 000000000..b41ef549b Binary files /dev/null and b/src/Magnum/Vk/Test/MeshTestFiles/flat.tga differ diff --git a/src/Magnum/Vk/Test/MeshTestFiles/noop.spv b/src/Magnum/Vk/Test/MeshTestFiles/noop.spv new file mode 100644 index 000000000..6c1915598 Binary files /dev/null and b/src/Magnum/Vk/Test/MeshTestFiles/noop.spv differ diff --git a/src/Magnum/Vk/Test/MeshTestFiles/noop.spvasm b/src/Magnum/Vk/Test/MeshTestFiles/noop.spvasm new file mode 100644 index 000000000..890c9529d --- /dev/null +++ b/src/Magnum/Vk/Test/MeshTestFiles/noop.spvasm @@ -0,0 +1,30 @@ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %ver "ver" %gl_Position + OpEntryPoint Fragment %fra "fra" %fragmentColor + OpExecutionMode %fra OriginUpperLeft + OpDecorate %gl_Position BuiltIn Position + OpDecorate %fragmentColor Location 0 + + %void = OpTypeVoid + %10 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_Position = OpVariable %_ptr_Output_v4float Output +%fragmentColor = OpVariable %_ptr_Output_v4float Output + + %9 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %9 %9 %9 %9 + + %ver = OpFunction %void None %10 + %ver_ = OpLabel + OpStore %gl_Position %11 + OpReturn + OpFunctionEnd + + %fra = OpFunction %void None %10 + %fra_ = OpLabel + OpStore %fragmentColor %11 + OpReturn + OpFunctionEnd diff --git a/src/Magnum/Vk/Test/MeshTestFiles/noop.tga b/src/Magnum/Vk/Test/MeshTestFiles/noop.tga new file mode 100644 index 000000000..999e67c76 Binary files /dev/null and b/src/Magnum/Vk/Test/MeshTestFiles/noop.tga differ diff --git a/src/Magnum/Vk/Test/MeshTestFiles/nullcolor.tga b/src/Magnum/Vk/Test/MeshTestFiles/nullcolor.tga new file mode 100644 index 000000000..b656bacb6 Binary files /dev/null and b/src/Magnum/Vk/Test/MeshTestFiles/nullcolor.tga differ diff --git a/src/Magnum/Vk/Test/MeshTestFiles/vertexcolor.spv b/src/Magnum/Vk/Test/MeshTestFiles/vertexcolor.spv new file mode 100644 index 000000000..2aa9f5c90 Binary files /dev/null and b/src/Magnum/Vk/Test/MeshTestFiles/vertexcolor.spv differ 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 000000000..228b40b32 Binary files /dev/null and b/src/Magnum/Vk/Test/MeshTestFiles/vertexcolor.tga differ 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/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) diff --git a/src/Magnum/Vk/Test/PipelineVkTest.cpp b/src/Magnum/Vk/Test/PipelineVkTest.cpp index d365cf630..52412bc7d 100644 --- a/src/Magnum/Vk/Test/PipelineVkTest.cpp +++ b/src/Magnum/Vk/Test/PipelineVkTest.cpp @@ -62,9 +62,13 @@ struct PipelineVkTest: VulkanTester { void constructCompute(); void constructMove(); - void wrap(); + void wrapRasterization(); + void wrapCompute(); - void cmdBind(); + void dynamicRasterizationStatesNotRasterization(); + + void cmdBindRasterization(); + void cmdBindCompute(); void cmdBarrier(); void cmdBarrierExecutionOnly(); @@ -81,9 +85,13 @@ PipelineVkTest::PipelineVkTest() { &PipelineVkTest::constructCompute, &PipelineVkTest::constructMove, - &PipelineVkTest::wrap, + &PipelineVkTest::wrapRasterization, + &PipelineVkTest::wrapCompute, + + &PipelineVkTest::dynamicRasterizationStatesNotRasterization, - &PipelineVkTest::cmdBind, + &PipelineVkTest::cmdBindRasterization, + &PipelineVkTest::cmdBindCompute, &PipelineVkTest::cmdBarrier, &PipelineVkTest::cmdBarrierExecutionOnly, @@ -117,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{}}; @@ -134,10 +142,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 */ @@ -145,6 +155,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{}}; @@ -181,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{}}; @@ -224,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{}}; @@ -241,16 +255,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() { @@ -276,25 +290,51 @@ 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, VertexFormat::Vector4, 0) + .addAttribute(1, 0, 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}}) + .setDynamicStates(DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias) + }; 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); + CORRADE_COMPARE(b.dynamicRasterizationStates(), DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias); Pipeline c{NoCreate}; c = std::move(b); @@ -302,13 +342,65 @@ 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_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, + 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, VertexFormat::Vector4, 0) + .addAttribute(1, 0, 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); + + VkPipeline pipeline{}; + CORRADE_COMPARE(Result(device()->CreateGraphicsPipelines(device(), {}, 1, + RasterizationPipelineCreateInfo{shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1} + .setViewport({{}, {200, 200}}) + .setDynamicStates(DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias), + nullptr, &pipeline)), Result::Success); + + 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{ @@ -333,7 +425,87 @@ void PipelineVkTest::wrap() { device()->DestroyPipeline(device(), pipeline, nullptr); } -void PipelineVkTest::cmdBind() { +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::cmdBindRasterization() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + + 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, VertexFormat::Vector4, 0) + .addAttribute(1, 0, 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)}}; 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}; 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 000000000..4614c82cf Binary files /dev/null and b/src/Magnum/Vk/Test/ShaderTestFiles/vert-frag.spv differ 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 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; 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 a54bd2612..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,8 +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_texture_sRGB_R8 optional -extension EXT_texture_sRGB_RG8 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 @@ -108,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 @@ -143,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 12380c2e8..2bce54cde 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 @@ -1238,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); @@ -1281,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); @@ -1433,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 **); @@ -1472,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 @@ -1515,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 @@ -1667,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 a58fd626d..3eda78f32 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 @@ -1137,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); @@ -1280,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); @@ -1432,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 **); @@ -1471,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 @@ -1614,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 @@ -1766,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 8185431be..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 @@ -17,7 +19,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 +58,8 @@ 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 extension EXT_polygon_offset_clamp 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 68349f21d..86428b761 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 @@ -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 e6ec050ee..7557e4c77 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 @@ -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 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/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; 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 + 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/AnyConverter.cpp b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp index fd0c08e43..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) { @@ -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 */ @@ -204,15 +228,76 @@ 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()); - /* 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); - if(formatFrom.isEmpty() || formatTo.isEmpty()) return {}; + /* 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) + ); + if(formatFrom.isEmpty()) return {}; + const Containers::StringView formatTo = stringForFormat( + _state->outputFormat != Format::Unspecified ? _state->outputFormat : formatForExtension("ShaderTools::AnyConverter::convertFileToFile():", to) + ); + 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 + really handled yet but I think that's okay for now. */ const std::string plugin = Utility::formatString( formatFrom == formatTo ? "{}ShaderConverter" : "{}To{}ShaderConverter", formatFrom, formatTo); @@ -276,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 46521b32f..859f20445 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`, @@ -86,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 @@ -138,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/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 diff --git a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp index 06452901d..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 @@ -34,6 +34,7 @@ #include #include "Magnum/ShaderTools/AbstractConverter.h" +#include "Magnum/ShaderTools/Stage.h" #include "configure.h" @@ -42,30 +43,75 @@ namespace Magnum { namespace ShaderTools { namespace Test { namespace { struct AnyConverterTest: TestSuite::Tester { explicit AnyConverterTest(); - void validate(); - void validateNotSupported(); - void validatePreprocessNotSupported(); - void validatePropagateFlags(); - void validatePropagateInputVersion(); - void validatePropagateOutputVersion(); - void validatePropagatePreprocess(); - - void convert(); - 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(); void detectConvert(); - - void unknown(); + void detectConvertExplicitFormat(); /* Explicitly forbid system-wide plugin dependencies. Tests that need those have their own manager. */ @@ -96,33 +142,80 @@ constexpr struct { }; AnyConverterTest::AnyConverterTest() { - addTests({&AnyConverterTest::validate, - &AnyConverterTest::validateNotSupported, - &AnyConverterTest::validatePreprocessNotSupported, - &AnyConverterTest::validatePropagateFlags, - &AnyConverterTest::validatePropagateInputVersion, - &AnyConverterTest::validatePropagateOutputVersion, - &AnyConverterTest::validatePropagatePreprocess, - - &AnyConverterTest::convert, - &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)); + addTests({&AnyConverterTest::detectValidateExplicitFormat}); + addInstancedTests({&AnyConverterTest::detectConvert}, Containers::arraySize(DetectConvertData)); - addTests({&AnyConverterTest::unknown}); + addTests({&AnyConverterTest::detectConvertExplicitFormat}); /* Load the plugin directly from the build tree. Otherwise it's static and already loaded. */ @@ -134,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); @@ -152,11 +245,39 @@ void AnyConverterTest::validate() { std::make_pair(true, Utility::formatString("WARNING: {}:10: 'reserved__identifier' : identifiers containing consecutive underscores (\"__\") are reserved", filename))); } -void AnyConverterTest::validateNotSupported() { +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; + 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::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); @@ -179,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); @@ -204,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); @@ -228,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); @@ -252,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); @@ -277,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); @@ -303,11 +603,46 @@ void AnyConverterTest::convert() { "WARNING: {}:10: 'reserved__identifier' : identifiers containing consecutive underscores (\"__\") are reserved\n", inputFilename)); } -void AnyConverterTest::convertNotSupported() { - CORRADE_SKIP("No plugin that would support just validation exists."); +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; + 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::convertFileToFileUnknownOutput() { + 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::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); @@ -339,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); @@ -362,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); @@ -385,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); @@ -408,14 +743,14 @@ 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" "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); @@ -439,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); @@ -463,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); @@ -497,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); @@ -521,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); @@ -545,55 +880,716 @@ void AnyConverterTest::convertPropagateOptimization() { "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); +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_COMPARE(converter->validateFile({}, data.filename), - std::make_pair(false, "")); + CORRADE_VERIFY(!converter->convertFileToData({}, "file.spv")); #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT - CORRADE_COMPARE(out.str(), Utility::formatString( - "PluginManager::Manager::load(): plugin {0} is not static and was not found in nonexistent\n" - "ShaderTools::AnyConverter::validateFile(): cannot load the {0} plugin\n", data.plugin)); + 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(), Utility::formatString( - "PluginManager::Manager::load(): plugin {0} was not found\n" - "ShaderTools::AnyConverter::validateFile(): cannot load the {0} plugin\n", data.plugin)); + 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::detectConvert() { - auto&& data = DetectConvertData[testCaseInstanceId()]; - setTestCaseDescription(data.name); +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->convertFileToFile({}, data.from, Utility::Directory::join(ANYSHADERCONVERTER_TEST_OUTPUT_DIR, data.to))); - #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT - CORRADE_COMPARE(out.str(), Utility::formatString( - "PluginManager::Manager::load(): plugin {0} is not static and was not found in nonexistent\n" - "ShaderTools::AnyConverter::convertFileToFile(): cannot load the {0} plugin\n", data.plugin)); - #else - CORRADE_COMPARE(out.str(), Utility::formatString( - "PluginManager::Manager::load(): plugin {0} was not found\n" - "ShaderTools::AnyConverter::convertFileToFile(): cannot load the {0} plugin\n", data.plugin)); - #endif + CORRADE_VERIFY(!converter->convertFileToData({}, "file.spv")); + CORRADE_COMPARE(out.str(), "ShaderTools::AnyConverter::convertFileToData(): no output format specified\n"); } -void AnyConverterTest::unknown() { - std::ostringstream output; - Error redirectError{&output}; +void AnyConverterTest::convertFileToDataNotSupported() { + CORRADE_SKIP("No plugin that would support just conversion exists."); +} - 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"); +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); + + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_COMPARE(converter->validateFile({}, data.filename), + std::make_pair(false, "")); + #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT + CORRADE_COMPARE(out.str(), Utility::formatString( + "PluginManager::Manager::load(): plugin {0} is not static and was not found in nonexistent\n" + "ShaderTools::AnyConverter::validateFile(): cannot load the {0} plugin\n", data.plugin)); + #else + CORRADE_COMPARE(out.str(), Utility::formatString( + "PluginManager::Manager::load(): plugin {0} was not found\n" + "ShaderTools::AnyConverter::validateFile(): cannot load the {0} plugin\n", data.plugin)); + #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); + + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToFile({}, data.from, Utility::Directory::join(ANYSHADERCONVERTER_TEST_OUTPUT_DIR, data.to))); + #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT + CORRADE_COMPARE(out.str(), Utility::formatString( + "PluginManager::Manager::load(): plugin {0} is not static and was not found in nonexistent\n" + "ShaderTools::AnyConverter::convertFileToFile(): cannot load the {0} plugin\n", data.plugin)); + #else + CORRADE_COMPARE(out.str(), Utility::formatString( + "PluginManager::Manager::load(): plugin {0} was not found\n" + "ShaderTools::AnyConverter::convertFileToFile(): cannot load the {0} plugin\n", data.plugin)); + #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 } }}}}