diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2019-03-18 18:19:26 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-03-18 18:19:26 -0400 |
| commit | 71885de27c973a73b7d020f5ebbe86e16b86d7e4 (patch) | |
| tree | 1d4ab43ac820fdb88611ce587f7613e13a58c6d2 | |
| parent | 70048715cec251a23871747e342e5cf938c97255 (diff) | |
First pass support for half on vk (#912)
* Look at getting half to work on vk.
* Alter half test so can always produce consistent test results.
* First pass working half on vk.
* Improve comments for vulkan extensions around half.
* Upgraded vulkan headers to v1.1.103
https://github.com/KhronosGroup/Vulkan-Headers
* * Add getFeatures on Render interface
* Vulkan renderer determines at startup if it can support half
* Parse render-features on render-test
* Small changes to half-calc.slang test.
* Structured buffer half access works as expected for Vk, but isn't for dx12, so disable for now.
* Require the half feature for renderers for the half-structured-buffer.slang test.
* * Added ToolReturnCode to be more rigerous about how a return code is passed back from a tool
* Added support for a tool being able to pass back an 'ignored' result.
* Used enum codes to indicate meanings
* Made spawnAndWait return a ToolReturnCode
* Ignore tests that don't have required render-feature
* Fix macro line continuation usage.
* Check dx12 has half support.
* Checking for half on dx12 - if CheckFeatureSupport fails, don't fail renderer initialization.
* Fix typo.
| -rw-r--r-- | source/slang/emit.cpp | 46 | ||||
| -rw-r--r-- | tests/compute/half-calc.slang | 32 | ||||
| -rw-r--r-- | tests/compute/half-calc.slang.expected.txt | 4 | ||||
| -rw-r--r-- | tests/compute/half-structured-buffer.slang | 30 | ||||
| -rw-r--r-- | tests/compute/half-structured-buffer.slang.expected.txt | 16 | ||||
| -rw-r--r-- | tools/gfx/render-d3d11.cpp | 3 | ||||
| -rw-r--r-- | tools/gfx/render-d3d12.cpp | 21 | ||||
| -rw-r--r-- | tools/gfx/render-gl.cpp | 3 | ||||
| -rw-r--r-- | tools/gfx/render-vk.cpp | 81 | ||||
| -rw-r--r-- | tools/gfx/render.h | 3 | ||||
| -rw-r--r-- | tools/gfx/vk-api.cpp | 4 | ||||
| -rw-r--r-- | tools/gfx/vk-api.h | 7 | ||||
| -rw-r--r-- | tools/gfx/vk-module.h | 3 | ||||
| -rw-r--r-- | tools/render-test/options.cpp | 21 | ||||
| -rw-r--r-- | tools/render-test/options.h | 2 | ||||
| -rw-r--r-- | tools/render-test/render-test-main.cpp | 11 | ||||
| -rw-r--r-- | tools/slang-test/slang-test-main.cpp | 4 |
17 files changed, 276 insertions, 15 deletions
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index d2de479b4..84585f248 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -47,6 +47,8 @@ struct ExtensionUsageTracker StringBuilder glslExtensionRequireLines; ProfileVersion profileVersion = ProfileVersion::GLSL_110; + + bool hasHalfExtension = false; }; void requireGLSLExtension( @@ -76,6 +78,21 @@ void requireGLSLVersionImpl( } } +void requireGLSLHalfExtension(ExtensionUsageTracker* tracker) +{ + if (!tracker->hasHalfExtension) + { + // https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GL_EXT_shader_16bit_storage.txt + requireGLSLExtension(tracker, "GL_EXT_shader_16bit_storage"); + + // https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GL_EXT_shader_explicit_arithmetic_types.txt + // Use GL_KHX_shader_explicit_arithmetic_types because that is what appears defined in glslang + requireGLSLExtension(tracker, "GL_KHX_shader_explicit_arithmetic_types"); + + tracker->hasHalfExtension = true; + } +} + // Shared state for an entire emit session struct SharedEmitContext @@ -810,7 +827,12 @@ struct EmitVisitor case kIROp_BoolType: Emit("b"); break; - case kIROp_HalfType: Emit("f16"); break; + case kIROp_HalfType: + { + _requireHalf(); + Emit("f16"); + break; + } case kIROp_DoubleType: Emit("d"); break; case kIROp_VectorType: @@ -1197,6 +1219,14 @@ struct EmitVisitor } } + void _requireHalf() + { + if (getTarget(context) == CodeGenTarget::GLSL) + { + requireGLSLHalfExtension(&context->shared->extensionUsageTracker); + } + } + void emitSimpleTypeImpl(IRType* type) { switch (type->op) @@ -1217,7 +1247,19 @@ struct EmitVisitor case kIROp_UIntType: Emit("uint"); return; case kIROp_UInt64Type: Emit("uint64_t"); return; - case kIROp_HalfType: Emit("half"); return; + case kIROp_HalfType: + { + _requireHalf(); + if (getTarget(context) == CodeGenTarget::GLSL) + { + Emit("float16_t"); + } + else + { + Emit("half"); + } + return; + } case kIROp_FloatType: Emit("float"); return; case kIROp_DoubleType: Emit("double"); return; diff --git a/tests/compute/half-calc.slang b/tests/compute/half-calc.slang new file mode 100644 index 000000000..b47678dae --- /dev/null +++ b/tests/compute/half-calc.slang @@ -0,0 +1,32 @@ +//TEST(compute):COMPARE_COMPUTE:-dx12 -compute -use-dxil -profile cs_6_2 -render-features half +//TEST(compute):COMPARE_COMPUTE:-vk -compute -use-dxil -profile cs_6_2 -render-features half +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out + +// Test for doing a calculation using half + +RWStructuredBuffer<float> outputBuffer; + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint tid = dispatchThreadID.x; + + //half2 v0 = { 1, -2 }; + //half2 v1 = { -2, 4 }; + + //half2 v2 = (v0 * 2.0f + v1); + + // This should work (it compiles on dxc, but slang doesn't seem to have overloads yet) + //half offset = length(v2); + + //half offset = v2.x + v2.y; + + half offset = 0.0f; + + half v = tid; + v *= half(3.0f); + v += half(1.0f); + v += offset; + + outputBuffer[tid] = v; +}
\ No newline at end of file diff --git a/tests/compute/half-calc.slang.expected.txt b/tests/compute/half-calc.slang.expected.txt new file mode 100644 index 000000000..2915a0dbc --- /dev/null +++ b/tests/compute/half-calc.slang.expected.txt @@ -0,0 +1,4 @@ +3F800000 +40800000 +40E00000 +41200000 diff --git a/tests/compute/half-structured-buffer.slang b/tests/compute/half-structured-buffer.slang new file mode 100644 index 000000000..e67aba55e --- /dev/null +++ b/tests/compute/half-structured-buffer.slang @@ -0,0 +1,30 @@ +//TEST(compute):COMPARE_COMPUTE:-vk -compute -profile cs_6_2 -render-features half +//Disable on Dx12 for now - because writing to structured buffer produces unexpected results +//DISABLE_TEST(compute):COMPARE_COMPUTE:-dx12 -compute -use-dxil -profile cs_6_2 -render-features half +//TEST_INPUT:ubuffer(data=[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], stride=16):dxbinding(0),glbinding(0),out + +struct Thing +{ + uint pos; + float radius; + half4 color; +}; + +RWStructuredBuffer<Thing> outputBuffer; + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint tid = dispatchThreadID.x; + + float v = float(tid); + + float base = v* 4.0f; + + Thing thing; + thing.pos = tid; + thing.color = half4(base, base + 1.0f, base + 2.0f, base + 3.0f); + thing.radius = v; + + outputBuffer[tid] = thing; +}
\ No newline at end of file diff --git a/tests/compute/half-structured-buffer.slang.expected.txt b/tests/compute/half-structured-buffer.slang.expected.txt new file mode 100644 index 000000000..9170926f3 --- /dev/null +++ b/tests/compute/half-structured-buffer.slang.expected.txt @@ -0,0 +1,16 @@ +0 +0 +3C000000 +42004000 +1 +3F800000 +45004400 +47004600 +2 +40000000 +48804800 +49804900 +3 +40400000 +4A804A00 +4B804B00 diff --git a/tools/gfx/render-d3d11.cpp b/tools/gfx/render-d3d11.cpp index 690b869ca..8b961c8f2 100644 --- a/tools/gfx/render-d3d11.cpp +++ b/tools/gfx/render-d3d11.cpp @@ -54,6 +54,7 @@ public: // Renderer implementation virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; + virtual const List<String>& getFeatures() override { return m_features; } virtual void setClearColor(const float color[4]) override; virtual void clearFrame() override; virtual void presentFrame() override; @@ -329,6 +330,8 @@ public: Desc m_desc; float m_clearColor[4] = { 0, 0, 0, 0 }; + + List<String> m_features; }; Renderer* createD3D11Renderer() diff --git a/tools/gfx/render-d3d12.cpp b/tools/gfx/render-d3d12.cpp index 0b611a219..5483e7e4b 100644 --- a/tools/gfx/render-d3d12.cpp +++ b/tools/gfx/render-d3d12.cpp @@ -55,6 +55,7 @@ class D3D12Renderer : public Renderer public: // Renderer implementation virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; + virtual const List<String>& getFeatures() override { return m_features; } virtual void setClearColor(const float color[4]) override; virtual void clearFrame() override; virtual void presentFrame() override; @@ -571,6 +572,8 @@ protected: PFN_D3D12_SERIALIZE_ROOT_SIGNATURE m_D3D12SerializeRootSignature = nullptr; HWND m_hwnd = nullptr; + + List<String> m_features; }; Renderer* createD3D12Renderer() @@ -1397,6 +1400,8 @@ Result D3D12Renderer::initialize(const Desc& desc, void* inWindowHandle) return SLANG_FAIL; } + + FlagCombiner combiner; // TODO: we should probably provide a command-line option // to override UseDebug of default rather than leave it @@ -1426,6 +1431,22 @@ Result D3D12Renderer::initialize(const Desc& desc, void* inWindowHandle) return SLANG_FAIL; } + // Find what features are supported + { + // Check this is how this is laid out... + SLANG_COMPILE_TIME_ASSERT(D3D_SHADER_MODEL_6_0 == 0x60); + + D3D12_FEATURE_DATA_SHADER_MODEL featureShaderMode; + featureShaderMode.HighestShaderModel = D3D_SHADER_MODEL(0x62); + + if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &featureShaderMode, sizeof(featureShaderMode))) && + featureShaderMode.HighestShaderModel >= 0x62) + { + // With sm_6_2 we have half + m_features.Add("half"); + } + } + m_numRenderFrames = 3; m_numRenderTargets = 2; diff --git a/tools/gfx/render-gl.cpp b/tools/gfx/render-gl.cpp index 879ff7a3c..a2d379778 100644 --- a/tools/gfx/render-gl.cpp +++ b/tools/gfx/render-gl.cpp @@ -81,6 +81,7 @@ public: // Renderer implementation virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; + virtual const List<String>& getFeatures() override { return m_features; } virtual void setClearColor(const float color[4]) override; virtual void clearFrame() override; virtual void presentFrame() override; @@ -354,6 +355,8 @@ public: Desc m_desc; + List<String> m_features; + // Declare a function pointer for each OpenGL // extension function we need to load #define DECLARE_GL_EXTENSION_FUNC(NAME, TYPE) TYPE NAME; diff --git a/tools/gfx/render-vk.cpp b/tools/gfx/render-vk.cpp index e291c7e9c..defc08355 100644 --- a/tools/gfx/render-vk.cpp +++ b/tools/gfx/render-vk.cpp @@ -42,6 +42,7 @@ public: // Renderer implementation virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; + virtual const List<String>& getFeatures() override { return m_features; } virtual void setClearColor(const float color[4]) override; virtual void clearFrame() override; virtual void presentFrame() override; @@ -488,6 +489,7 @@ public: float m_clearColor[4] = { 0, 0, 0, 0 }; Desc m_desc; + List<String> m_features; }; /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VkRenderer::Buffer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ @@ -898,6 +900,8 @@ SlangResult VKRenderer::initialize(const Desc& desc, void* inWindowHandle) { VK_KHR_SURFACE_EXTENSION_NAME, + VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, + #if SLANG_WINDOWS_FAMILY VK_KHR_WIN32_SURFACE_EXTENSION_NAME, #else @@ -949,6 +953,71 @@ SlangResult VKRenderer::initialize(const Desc& desc, void* inWindowHandle) SLANG_RETURN_ON_FAIL(m_api.initPhysicalDevice(physicalDevices[selectedDeviceIndex])); + List<const char*> deviceExtensions; + deviceExtensions.Add(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + + VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; + deviceCreateInfo.queueCreateInfoCount = 1; + + deviceCreateInfo.pEnabledFeatures = &m_api.m_deviceFeatures; + + // Get the device features (doesn't use, but useful when debugging) + if (m_api.vkGetPhysicalDeviceFeatures2) + { + VkPhysicalDeviceFeatures2 deviceFeatures2 = {}; + deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + m_api.vkGetPhysicalDeviceFeatures2(m_api.m_physicalDevice, &deviceFeatures2); + } + + VkPhysicalDeviceProperties basicProps = {}; + m_api.vkGetPhysicalDeviceProperties(m_api.m_physicalDevice, &basicProps); + + // Get the API version + const uint32_t majorVersion = VK_VERSION_MAJOR(basicProps.apiVersion); + const uint32_t minorVersion = VK_VERSION_MINOR(basicProps.apiVersion); + + // Float16 features + // Need in this scope because it will be linked into the device creation (if it is available) + VkPhysicalDeviceFloat16Int8FeaturesKHR float16Features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR }; + + // API version check, can't use vkGetPhysicalDeviceProperties2 yet since this device might not support it + if (VK_MAKE_VERSION(majorVersion, minorVersion, 0) >= VK_API_VERSION_1_1 && + m_api.vkGetPhysicalDeviceProperties2 && + m_api.vkGetPhysicalDeviceFeatures2) + { + VkPhysicalDeviceProperties2 physicalDeviceProps2; + + physicalDeviceProps2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + physicalDeviceProps2.pNext = nullptr; + physicalDeviceProps2.properties = {}; + + m_api.vkGetPhysicalDeviceProperties2(m_api.m_physicalDevice, &physicalDeviceProps2); + + // Get device features + VkPhysicalDeviceFeatures2 deviceFeatures2 = {}; + deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + + // Link together for lookup + float16Features.pNext = deviceFeatures2.pNext; + deviceFeatures2.pNext = &float16Features; + + m_api.vkGetPhysicalDeviceFeatures2(m_api.m_physicalDevice, &deviceFeatures2); + + // If we have float16 features then enable + if (float16Features.shaderFloat16) + { + // Link into the creation features + float16Features.pNext = (void*)deviceCreateInfo.pNext; + deviceCreateInfo.pNext = &float16Features; + + // Add the Float16 extension + deviceExtensions.Add(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); + + // We have half support + m_features.Add("half"); + } + } + int queueFamilyIndex = m_api.findQueue(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT); assert(queueFamilyIndex >= 0); @@ -958,18 +1027,10 @@ SlangResult VKRenderer::initialize(const Desc& desc, void* inWindowHandle) queueCreateInfo.queueCount = 1; queueCreateInfo.pQueuePriorities = &queuePriority; - char const* const deviceExtensions[] = - { - VK_KHR_SWAPCHAIN_EXTENSION_NAME, - }; - - VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; - deviceCreateInfo.queueCreateInfoCount = 1; deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; - deviceCreateInfo.pEnabledFeatures = &m_api.m_deviceFeatures; - deviceCreateInfo.enabledExtensionCount = SLANG_COUNT_OF(deviceExtensions); - deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions; + deviceCreateInfo.enabledExtensionCount = uint32_t(deviceExtensions.Count()); + deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.Buffer(); SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateDevice(m_api.m_physicalDevice, &deviceCreateInfo, nullptr, &m_device)); SLANG_RETURN_ON_FAIL(m_api.initDeviceProcs(m_device)); diff --git a/tools/gfx/render.h b/tools/gfx/render.h index b91fb513f..775e71613 100644 --- a/tools/gfx/render.h +++ b/tools/gfx/render.h @@ -785,6 +785,9 @@ public: virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) = 0; + bool hasFeature(const Slang::UnownedStringSlice& feature) { return getFeatures().IndexOf(Slang::String(feature)) != UInt(-1); } + virtual const Slang::List<Slang::String>& getFeatures() = 0; + virtual void setClearColor(const float color[4]) = 0; virtual void clearFrame() = 0; diff --git a/tools/gfx/vk-api.cpp b/tools/gfx/vk-api.cpp index 4030e43ba..6e250e281 100644 --- a/tools/gfx/vk-api.cpp +++ b/tools/gfx/vk-api.cpp @@ -49,11 +49,15 @@ Slang::Result VulkanApi::initInstanceProcs(VkInstance instance) VK_API_ALL_INSTANCE_PROCS(VK_API_GET_INSTANCE_PROC) + // Get optional + VK_API_INSTANCE_PROCS_OPT(VK_API_GET_INSTANCE_PROC) + if (!areDefined(ProcType::Instance)) { return SLANG_FAIL; } + m_instance = instance; return SLANG_OK; } diff --git a/tools/gfx/vk-api.h b/tools/gfx/vk-api.h index 5ec28ef6e..001f44d19 100644 --- a/tools/gfx/vk-api.h +++ b/tools/gfx/vk-api.h @@ -10,6 +10,11 @@ namespace gfx { x(vkCreateInstance) \ /* */ +#define VK_API_INSTANCE_PROCS_OPT(x) \ + x(vkGetPhysicalDeviceFeatures2) \ + x(vkGetPhysicalDeviceProperties2) \ + /* */ + #define VK_API_INSTANCE_PROCS(x) \ x(vkCreateDevice) \ x(vkCreateDebugReportCallbackEXT) \ @@ -146,6 +151,8 @@ namespace gfx { VK_API_ALL_GLOBAL_PROCS(x) \ VK_API_ALL_INSTANCE_PROCS(x) \ VK_API_ALL_DEVICE_PROCS(x) \ + \ + VK_API_INSTANCE_PROCS_OPT(x) \ /* */ #define VK_API_DECLARE_PROC(NAME) PFN_##NAME NAME = nullptr; diff --git a/tools/gfx/vk-module.h b/tools/gfx/vk-module.h index efa166d08..4d18823ca 100644 --- a/tools/gfx/vk-module.h +++ b/tools/gfx/vk-module.h @@ -1,4 +1,4 @@ -// vk-module.h +// vk-module.h #pragma once #include "../../slang.h" @@ -12,6 +12,7 @@ #endif #define VK_NO_PROTOTYPES + #include <vulkan/include/vulkan/vulkan.h> namespace gfx { diff --git a/tools/render-test/options.cpp b/tools/render-test/options.cpp index b6208c4b3..1171a2a03 100644 --- a/tools/render-test/options.cpp +++ b/tools/render-test/options.cpp @@ -9,7 +9,11 @@ #include "../../source/core/slang-writer.h" #include "../../source/core/slang-render-api-util.h" +#include "../../source/core/list.h" +#include "../../source/core/slang-string-util.h" + namespace renderer_test { +using namespace Slang; static const Options gDefaultOptions; @@ -98,6 +102,23 @@ SlangResult parseOptions(int argc, const char*const* argv, Slang::WriterHelper s } gOptions.profileName = *argCursor++; } + else if (strcmp(arg, "-render-features") == 0 || strcmp(arg, "-render-feature") == 0) + { + if (argCursor == argEnd) + { + stdError.print("expected argument for '%s' option\n", arg); + return SLANG_FAIL; + } + const char* value = *argCursor++; + + List<UnownedStringSlice> values; + StringUtil::split(UnownedStringSlice(value), ',', values); + + for (const auto& value : values) + { + gOptions.renderFeatures.Add(value); + } + } else if( strcmp(arg, "-xslang") == 0 ) { // This is an option that we want to pass along to Slang diff --git a/tools/render-test/options.h b/tools/render-test/options.h index 6c1482ab4..7b55b9ac0 100644 --- a/tools/render-test/options.h +++ b/tools/render-test/options.h @@ -55,6 +55,8 @@ struct Options int slangArgCount = 0; bool useDXIL = false; + + Slang::List<Slang::String> renderFeatures; /// Required render features for this test to run }; extern Options gOptions; diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp index 0be84f52f..ee408f283 100644 --- a/tools/render-test/render-test-main.cpp +++ b/tools/render-test/render-test-main.cpp @@ -574,6 +574,17 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe } } + { + for (const auto& feature : gOptions.renderFeatures) + { + // If doesn't have required feature... we have to give up + if (!renderer->hasFeature(feature.getUnownedSlice())) + { + return SLANG_E_NOT_AVAILABLE; + } + } + } + // Use the profile name set on options if set profileName = gOptions.profileName ? gOptions.profileName : profileName; diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp index d385e868d..0b4b83198 100644 --- a/tools/slang-test/slang-test-main.cpp +++ b/tools/slang-test/slang-test-main.cpp @@ -1694,7 +1694,7 @@ static RenderApiType _findRenderApi(const List<String>& args, bool onlyExplicit) return targetLanguageRenderer; } -static void _addSythesizedTest(RenderApiType rendererType, const List<TestOptions>& renderTests, List<TestOptions>& outSynthesizedTests) +static void _addSynthesizedTest(RenderApiType rendererType, const List<TestOptions>& renderTests, List<TestOptions>& outSynthesizedTests) { for (const auto& test : renderTests) { @@ -1784,7 +1784,7 @@ void runTestsOnFile( const int index = ByteEncodeUtil::calcMsb8(missingApis); SLANG_ASSERT(index >= 0 && index <= int(RenderApiType::CountOf)); - _addSythesizedTest(RenderApiType(index), renderTests, synthesizedTests); + _addSynthesizedTest(RenderApiType(index), renderTests, synthesizedTests); // Disable the bit missingApis &= ~(RenderApiFlags(1) << index); |
