/*------------------------------------------------------------------------ * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2017 The Khronos Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Synchronization tests for resources shared with DX11 keyed mutex *//*--------------------------------------------------------------------*/ #include "vktSynchronizationWin32KeyedMutexTests.hpp" #include "vkDeviceUtil.hpp" #include "vkPlatform.hpp" #include "vkCmdUtil.hpp" #include "vktTestCaseUtil.hpp" #include "deSharedPtr.hpp" #include "vktSynchronizationUtil.hpp" #include "vktSynchronizationOperation.hpp" #include "vktSynchronizationOperationTestData.hpp" #include "vktExternalMemoryUtil.hpp" #include "vktTestGroupUtil.hpp" #include "vktCustomInstancesDevices.hpp" #include "tcuResultCollector.hpp" #include "tcuTestLog.hpp" #include "tcuCommandLine.hpp" #if (DE_OS == DE_OS_WIN32) # define WIN32_LEAN_AND_MEAN # define NOMINMAX # include # include # include # include # include typedef HRESULT (WINAPI * LPD3DX11COMPILEFROMMEMORY)(LPCSTR, SIZE_T, LPCSTR, CONST D3D10_SHADER_MACRO*, LPD3D10INCLUDE, LPCSTR, LPCSTR, UINT, UINT, void*, /* ID3DX11ThreadPump */ ID3D10Blob** , ID3D10Blob** , HRESULT*); #endif using tcu::TestLog; using namespace vkt::ExternalMemoryUtil; namespace vkt { using namespace vk; namespace synchronization { namespace { using namespace vk; using de::SharedPtr; static const ResourceDescription s_resourcesWin32KeyedMutex[] = { { RESOURCE_TYPE_BUFFER, tcu::IVec4( 0x4000, 0, 0, 0), vk::VK_IMAGE_TYPE_LAST, vk::VK_FORMAT_UNDEFINED, (vk::VkImageAspectFlags)0, vk::VK_SAMPLE_COUNT_1_BIT }, // 16 KiB (min max UBO range) { RESOURCE_TYPE_BUFFER, tcu::IVec4(0x40000, 0, 0, 0), vk::VK_IMAGE_TYPE_LAST, vk::VK_FORMAT_UNDEFINED, (vk::VkImageAspectFlags)0, vk::VK_SAMPLE_COUNT_1_BIT }, // 256 KiB { RESOURCE_TYPE_IMAGE, tcu::IVec4(128, 128, 0, 0), vk::VK_IMAGE_TYPE_2D, vk::VK_FORMAT_R8_UNORM, vk::VK_IMAGE_ASPECT_COLOR_BIT, vk::VK_SAMPLE_COUNT_1_BIT }, { RESOURCE_TYPE_IMAGE, tcu::IVec4(128, 128, 0, 0), vk::VK_IMAGE_TYPE_2D, vk::VK_FORMAT_R16_UINT, vk::VK_IMAGE_ASPECT_COLOR_BIT, vk::VK_SAMPLE_COUNT_1_BIT }, { RESOURCE_TYPE_IMAGE, tcu::IVec4(128, 128, 0, 0), vk::VK_IMAGE_TYPE_2D, vk::VK_FORMAT_R8G8B8A8_UNORM, vk::VK_IMAGE_ASPECT_COLOR_BIT, vk::VK_SAMPLE_COUNT_1_BIT }, { RESOURCE_TYPE_IMAGE, tcu::IVec4(128, 128, 0, 0), vk::VK_IMAGE_TYPE_2D, vk::VK_FORMAT_R16G16B16A16_UINT, vk::VK_IMAGE_ASPECT_COLOR_BIT, vk::VK_SAMPLE_COUNT_1_BIT }, { RESOURCE_TYPE_IMAGE, tcu::IVec4(128, 128, 0, 0), vk::VK_IMAGE_TYPE_2D, vk::VK_FORMAT_R32G32B32A32_SFLOAT, vk::VK_IMAGE_ASPECT_COLOR_BIT, vk::VK_SAMPLE_COUNT_1_BIT }, }; struct TestConfig { TestConfig (const ResourceDescription& resource_, OperationName writeOp_, OperationName readOp_, vk::VkExternalMemoryHandleTypeFlagBits memoryHandleTypeBuffer_, vk::VkExternalMemoryHandleTypeFlagBits memoryHandleTypeImage_) : resource (resource_) , writeOp (writeOp_) , readOp (readOp_) , memoryHandleTypeBuffer (memoryHandleTypeBuffer_) , memoryHandleTypeImage (memoryHandleTypeImage_) { } const ResourceDescription resource; const OperationName writeOp; const OperationName readOp; const vk::VkExternalMemoryHandleTypeFlagBits memoryHandleTypeBuffer; const vk::VkExternalMemoryHandleTypeFlagBits memoryHandleTypeImage; }; bool checkQueueFlags (vk::VkQueueFlags availableFlags, const vk::VkQueueFlags neededFlags) { if ((availableFlags & (vk::VK_QUEUE_GRAPHICS_BIT | vk::VK_QUEUE_COMPUTE_BIT)) != 0) availableFlags |= vk::VK_QUEUE_TRANSFER_BIT; return (availableFlags & neededFlags) != 0; } class SimpleAllocation : public vk::Allocation { public: SimpleAllocation (const vk::DeviceInterface& vkd, vk::VkDevice device, const vk::VkDeviceMemory memory); ~SimpleAllocation (void); private: const vk::DeviceInterface& m_vkd; const vk::VkDevice m_device; }; SimpleAllocation::SimpleAllocation (const vk::DeviceInterface& vkd, vk::VkDevice device, const vk::VkDeviceMemory memory) : Allocation (memory, 0, DE_NULL) , m_vkd (vkd) , m_device (device) { } SimpleAllocation::~SimpleAllocation (void) { m_vkd.freeMemory(m_device, getMemory(), DE_NULL); } CustomInstance createTestInstance (Context& context) { std::vector extensions; extensions.push_back("VK_KHR_get_physical_device_properties2"); extensions.push_back("VK_KHR_external_memory_capabilities"); return createCustomInstanceWithExtensions(context, extensions); } vk::Move createTestDevice (Context& context, vk::VkInstance instance, const vk::InstanceInterface& vki, vk::VkPhysicalDevice physicalDevice) { const bool validationEnabled = context.getTestContext().getCommandLine().isValidationEnabled(); const deUint32 apiVersion = context.getUsedApiVersion(); const vk::PlatformInterface& vkp = context.getPlatformInterface(); const float priority = 0.0f; const std::vector queueFamilyProperties = vk::getPhysicalDeviceQueueFamilyProperties(vki, physicalDevice); std::vector queueFamilyIndices (queueFamilyProperties.size(), 0xFFFFFFFFu); std::vector extensions; if (!isCoreDeviceExtension(apiVersion, "VK_KHR_external_memory")) extensions.push_back("VK_KHR_external_memory"); if (!isCoreDeviceExtension(apiVersion, "VK_KHR_dedicated_allocation")) extensions.push_back("VK_KHR_dedicated_allocation"); if (!isCoreDeviceExtension(apiVersion, "VK_KHR_get_memory_requirements2")) extensions.push_back("VK_KHR_get_memory_requirements2"); extensions.push_back("VK_KHR_external_memory_win32"); extensions.push_back("VK_KHR_win32_keyed_mutex"); const auto& features = context.getDeviceFeatures(); try { std::vector queues; for (size_t ndx = 0; ndx < queueFamilyProperties.size(); ndx++) { const vk::VkDeviceQueueCreateInfo createInfo = { vk::VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, DE_NULL, 0u, (deUint32)ndx, 1u, &priority }; queues.push_back(createInfo); } const vk::VkDeviceCreateInfo createInfo = { vk::VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, DE_NULL, 0u, (deUint32)queues.size(), &queues[0], 0u, DE_NULL, (deUint32)extensions.size(), extensions.empty() ? DE_NULL : &extensions[0], &features }; return createCustomDevice(validationEnabled, vkp, instance, vki, physicalDevice, &createInfo); } catch (const vk::Error& error) { if (error.getError() == vk::VK_ERROR_EXTENSION_NOT_PRESENT) TCU_THROW(NotSupportedError, "Required extensions not supported"); else throw; } } deUint32 chooseMemoryType (deUint32 bits) { DE_ASSERT(bits != 0); for (deUint32 memoryTypeIndex = 0; (1u << memoryTypeIndex) <= bits; memoryTypeIndex++) { if ((bits & (1u << memoryTypeIndex)) != 0) return memoryTypeIndex; } DE_FATAL("No supported memory types"); return -1; } bool isOpaqueHandleType (const vk::VkExternalMemoryHandleTypeFlagBits handleType) { switch (handleType) { case vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT: case vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT: case vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT: return true; case vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT: case vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT: case vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT: case vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT: case vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT: case vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT: case vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID: return false; default: TCU_THROW(InternalError, "Unknown handle type or multiple bits set"); } } vk::Move importMemory (const vk::DeviceInterface& vkd, vk::VkDevice device, const vk::VkMemoryRequirements& requirements, vk::VkExternalMemoryHandleTypeFlagBits externalType, NativeHandle& handle, bool requiresDedicated, vk::VkBuffer buffer, vk::VkImage image) { const vk::VkMemoryDedicatedAllocateInfo dedicatedInfo = { vk::VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, DE_NULL, image, buffer, }; const vk::VkImportMemoryWin32HandleInfoKHR importInfo = { vk::VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR, (requiresDedicated) ? &dedicatedInfo : DE_NULL, externalType, handle.getWin32Handle(), (vk::pt::Win32LPCWSTR)NULL }; deUint32 handleCompatibleMemoryTypeBits = ~0u; if(!isOpaqueHandleType(externalType)) { vk::VkMemoryWin32HandlePropertiesKHR memoryWin32HandleProperties = { vk::VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHR, DE_NULL, 0u }; VK_CHECK(vkd.getMemoryWin32HandlePropertiesKHR(device, externalType, handle.getWin32Handle(), &memoryWin32HandleProperties)); handleCompatibleMemoryTypeBits &= memoryWin32HandleProperties.memoryTypeBits; } const vk::VkMemoryAllocateInfo info = { vk::VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, &importInfo, requirements.size, chooseMemoryType(requirements.memoryTypeBits & handleCompatibleMemoryTypeBits) }; vk::Move memory (vk::allocateMemory(vkd, device, &info)); handle.disown(); return memory; } de::MovePtr importAndBindMemory (const vk::DeviceInterface& vkd, vk::VkDevice device, vk::VkBuffer buffer, NativeHandle& nativeHandle, vk::VkExternalMemoryHandleTypeFlagBits externalType) { const vk::VkBufferMemoryRequirementsInfo2 requirementsInfo = { vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2, DE_NULL, buffer, }; vk::VkMemoryDedicatedRequirements dedicatedRequirements = { vk::VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, DE_NULL, VK_FALSE, VK_FALSE, }; vk::VkMemoryRequirements2 requirements = { vk::VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &dedicatedRequirements, { 0u, 0u, 0u, }, }; vkd.getBufferMemoryRequirements2(device, &requirementsInfo, &requirements); vk::Move memory = importMemory(vkd, device, requirements.memoryRequirements, externalType, nativeHandle, !!dedicatedRequirements.requiresDedicatedAllocation, buffer, DE_NULL); VK_CHECK(vkd.bindBufferMemory(device, buffer, *memory, 0u)); return de::MovePtr(new SimpleAllocation(vkd, device, memory.disown())); } de::MovePtr importAndBindMemory (const vk::DeviceInterface& vkd, vk::VkDevice device, vk::VkImage image, NativeHandle& nativeHandle, vk::VkExternalMemoryHandleTypeFlagBits externalType) { const vk::VkImageMemoryRequirementsInfo2 requirementsInfo = { vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, DE_NULL, image, }; vk::VkMemoryDedicatedRequirements dedicatedRequirements = { vk::VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, DE_NULL, VK_FALSE, VK_FALSE, }; vk::VkMemoryRequirements2 requirements = { vk::VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &dedicatedRequirements, { 0u, 0u, 0u, }, }; vkd.getImageMemoryRequirements2(device, &requirementsInfo, &requirements); vk::Move memory = importMemory(vkd, device, requirements.memoryRequirements, externalType, nativeHandle, !!dedicatedRequirements.requiresDedicatedAllocation, DE_NULL, image); VK_CHECK(vkd.bindImageMemory(device, image, *memory, 0u)); return de::MovePtr(new SimpleAllocation(vkd, device, memory.disown())); } de::MovePtr importResource (const vk::DeviceInterface& vkd, vk::VkDevice device, const ResourceDescription& resourceDesc, const std::vector& queueFamilyIndices, const OperationSupport& readOp, const OperationSupport& writeOp, NativeHandle& nativeHandle, vk::VkExternalMemoryHandleTypeFlagBits externalType) { if (resourceDesc.type == RESOURCE_TYPE_IMAGE) { const vk::VkExtent3D extent = { (deUint32)resourceDesc.size.x(), de::max(1u, (deUint32)resourceDesc.size.y()), de::max(1u, (deUint32)resourceDesc.size.z()) }; const vk::VkImageSubresourceRange subresourceRange = { resourceDesc.imageAspect, 0u, 1u, 0u, 1u }; const vk::VkImageSubresourceLayers subresourceLayers = { resourceDesc.imageAspect, 0u, 0u, 1u }; const vk::VkExternalMemoryImageCreateInfo externalInfo = { vk::VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, DE_NULL, (vk::VkExternalMemoryHandleTypeFlags)externalType }; const vk::VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL; const vk::VkImageCreateInfo createInfo = { vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, &externalInfo, 0u, resourceDesc.imageType, resourceDesc.imageFormat, extent, 1u, 1u, resourceDesc.imageSamples, tiling, readOp.getInResourceUsageFlags() | writeOp.getOutResourceUsageFlags(), vk::VK_SHARING_MODE_EXCLUSIVE, (deUint32)queueFamilyIndices.size(), &queueFamilyIndices[0], vk::VK_IMAGE_LAYOUT_UNDEFINED }; vk::Move image = vk::createImage(vkd, device, &createInfo); de::MovePtr allocation = importAndBindMemory(vkd, device, *image, nativeHandle, externalType); return de::MovePtr(new Resource(image, allocation, extent, resourceDesc.imageType, resourceDesc.imageFormat, subresourceRange, subresourceLayers, tiling)); } else { const vk::VkDeviceSize offset = 0u; const vk::VkDeviceSize size = static_cast(resourceDesc.size.x()); const vk::VkBufferUsageFlags usage = readOp.getInResourceUsageFlags() | writeOp.getOutResourceUsageFlags(); const vk::VkExternalMemoryBufferCreateInfo externalInfo = { vk::VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO, DE_NULL, (vk::VkExternalMemoryHandleTypeFlags)externalType }; const vk::VkBufferCreateInfo createInfo = { vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, &externalInfo, 0u, size, usage, vk::VK_SHARING_MODE_EXCLUSIVE, (deUint32)queueFamilyIndices.size(), &queueFamilyIndices[0] }; vk::Move buffer = vk::createBuffer(vkd, device, &createInfo); de::MovePtr allocation = importAndBindMemory(vkd, device, *buffer, nativeHandle, externalType); return de::MovePtr(new Resource(resourceDesc.type, buffer, allocation, offset, size)); } } void recordWriteBarrier (const vk::DeviceInterface& vkd, vk::VkCommandBuffer commandBuffer, const Resource& resource, const SyncInfo& writeSync, deUint32 writeQueueFamilyIndex, const SyncInfo& readSync) { const vk::VkPipelineStageFlags srcStageMask = static_cast(writeSync.stageMask); const vk::VkAccessFlags srcAccessMask = static_cast(writeSync.accessMask); const vk::VkPipelineStageFlags dstStageMask = static_cast(readSync.stageMask); const vk::VkAccessFlags dstAccessMask = static_cast(readSync.accessMask); const vk::VkDependencyFlags dependencyFlags = 0; if (resource.getType() == RESOURCE_TYPE_IMAGE) { const vk::VkImageMemoryBarrier barrier = { vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, DE_NULL, srcAccessMask, dstAccessMask, writeSync.imageLayout, readSync.imageLayout, writeQueueFamilyIndex, VK_QUEUE_FAMILY_EXTERNAL, resource.getImage().handle, resource.getImage().subresourceRange }; vkd.cmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, 0u, (const vk::VkMemoryBarrier*)DE_NULL, 0u, (const vk::VkBufferMemoryBarrier*)DE_NULL, 1u, (const vk::VkImageMemoryBarrier*)&barrier); } else { const vk::VkBufferMemoryBarrier barrier = { vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, DE_NULL, srcAccessMask, dstAccessMask, writeQueueFamilyIndex, VK_QUEUE_FAMILY_EXTERNAL, resource.getBuffer().handle, 0u, VK_WHOLE_SIZE }; vkd.cmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, 0u, (const vk::VkMemoryBarrier*)DE_NULL, 1u, (const vk::VkBufferMemoryBarrier*)&barrier, 0u, (const vk::VkImageMemoryBarrier*)DE_NULL); } } void recordReadBarrier (const vk::DeviceInterface& vkd, vk::VkCommandBuffer commandBuffer, const Resource& resource, const SyncInfo& writeSync, const SyncInfo& readSync, deUint32 readQueueFamilyIndex) { const vk::VkPipelineStageFlags srcStageMask = static_cast(readSync.stageMask); const vk::VkAccessFlags srcAccessMask = static_cast(readSync.accessMask); const vk::VkPipelineStageFlags dstStageMask = static_cast(readSync.stageMask); const vk::VkAccessFlags dstAccessMask = static_cast(readSync.accessMask); const vk::VkDependencyFlags dependencyFlags = 0; if (resource.getType() == RESOURCE_TYPE_IMAGE) { const vk::VkImageMemoryBarrier barrier = { vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, DE_NULL, srcAccessMask, dstAccessMask, writeSync.imageLayout, readSync.imageLayout, VK_QUEUE_FAMILY_EXTERNAL, readQueueFamilyIndex, resource.getImage().handle, resource.getImage().subresourceRange }; vkd.cmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, 0u, (const vk::VkMemoryBarrier*)DE_NULL, 0u, (const vk::VkBufferMemoryBarrier*)DE_NULL, 1u, (const vk::VkImageMemoryBarrier*)&barrier); } else { const vk::VkBufferMemoryBarrier barrier = { vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, DE_NULL, srcAccessMask, dstAccessMask, VK_QUEUE_FAMILY_EXTERNAL, readQueueFamilyIndex, resource.getBuffer().handle, 0u, VK_WHOLE_SIZE }; vkd.cmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, 0u, (const vk::VkMemoryBarrier*)DE_NULL, 1u, (const vk::VkBufferMemoryBarrier*)&barrier, 0u, (const vk::VkImageMemoryBarrier*)DE_NULL); } } std::vector getFamilyIndices (const std::vector& properties) { std::vector indices (properties.size(), 0); for (deUint32 ndx = 0; ndx < properties.size(); ndx++) indices[ndx] = ndx; return indices; } class DX11Operation { public: enum Buffer { BUFFER_VK_WRITE, BUFFER_VK_READ, BUFFER_COUNT, }; enum KeyedMutex { KEYED_MUTEX_INIT = 0, KEYED_MUTEX_VK_WRITE = 1, KEYED_MUTEX_DX_COPY = 2, KEYED_MUTEX_VK_VERIFY = 3, KEYED_MUTEX_DONE = 4, }; #if (DE_OS == DE_OS_WIN32) DX11Operation (const ResourceDescription& resourceDesc, vk::VkExternalMemoryHandleTypeFlagBits memoryHandleType, ID3D11Device* pDevice, ID3D11DeviceContext* pContext, LPD3DX11COMPILEFROMMEMORY fnD3DX11CompileFromMemory, pD3DCompile fnD3DCompile) : m_resourceDesc (resourceDesc) , m_pDevice (pDevice) , m_pContext (pContext) , m_fnD3DX11CompileFromMemory (fnD3DX11CompileFromMemory) , m_fnD3DCompile (fnD3DCompile) , m_pRenderTargetView (0) , m_pVertexShader (0) , m_pPixelShader (0) , m_pVertexBuffer (0) , m_pTextureRV (0) , m_pSamplerLinear (0) , m_numFrames (0) { HRESULT hr; if (memoryHandleType == vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT || memoryHandleType == vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT) m_isMemNtHandle = true; else m_isMemNtHandle = false; m_securityAttributes.lpSecurityDescriptor = 0; for (UINT i = 0; i < BUFFER_COUNT; i++) { m_pTexture[i] = NULL; m_pBuffer[i] = NULL; m_keyedMutex[i] = NULL; } if (m_resourceDesc.type == RESOURCE_TYPE_BUFFER) { // SHARED_NTHANDLE is not supported with CreateBuffer(). TCU_CHECK_INTERNAL(!m_isMemNtHandle); D3D11_BUFFER_DESC descBuf = { }; descBuf.ByteWidth = (UINT)m_resourceDesc.size.x(); descBuf.Usage = D3D11_USAGE_DEFAULT; descBuf.BindFlags = 0; descBuf.CPUAccessFlags = 0; descBuf.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; descBuf.StructureByteStride = 0; for (UINT i = 0; i < BUFFER_COUNT; ++i) { hr = m_pDevice->CreateBuffer(&descBuf, NULL, &m_pBuffer[i]); if (FAILED(hr)) TCU_FAIL("Failed to create a buffer"); m_sharedMemHandle[i] = 0; IDXGIResource* tempResource = NULL; hr = m_pBuffer[i]->QueryInterface(__uuidof(IDXGIResource), (void**)&tempResource); if (FAILED(hr)) TCU_FAIL("Query interface of IDXGIResource failed"); hr = tempResource->GetSharedHandle(&m_sharedMemHandle[i]); tempResource->Release(); if (FAILED(hr)) TCU_FAIL("Failed to get DX shared handle"); hr = m_pBuffer[i]->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&m_keyedMutex[i]); if (FAILED(hr)) TCU_FAIL("Query interface of IDXGIKeyedMutex failed"); // Take ownership of the lock. m_keyedMutex[i]->AcquireSync(KEYED_MUTEX_INIT, INFINITE); } // Release the buffer write lock for Vulkan to write into. m_keyedMutex[BUFFER_VK_WRITE]->ReleaseSync(KEYED_MUTEX_VK_WRITE); m_sharedMemSize = descBuf.ByteWidth; m_sharedMemOffset = 0; } else { DE_ASSERT(m_resourceDesc.type == RESOURCE_TYPE_IMAGE); for (UINT i = 0; i < BUFFER_COUNT; ++i) { D3D11_TEXTURE2D_DESC descColor = { }; descColor.Width = m_resourceDesc.size.x(); descColor.Height = m_resourceDesc.size.y(); descColor.MipLevels = 1; descColor.ArraySize = 1; descColor.Format = getDxgiFormat(m_resourceDesc.imageFormat); descColor.SampleDesc.Count = 1; descColor.SampleDesc.Quality = 0; descColor.Usage = D3D11_USAGE_DEFAULT; descColor.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; descColor.CPUAccessFlags = 0; if (m_isMemNtHandle) descColor.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX | D3D11_RESOURCE_MISC_SHARED_NTHANDLE; else descColor.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; hr = m_pDevice->CreateTexture2D(&descColor, NULL, &m_pTexture[i]); if (FAILED(hr)) TCU_FAIL("Unable to create DX11 texture"); m_sharedMemHandle[i] = 0; if (m_isMemNtHandle) { IDXGIResource1* tempResource1 = NULL; hr = m_pTexture[i]->QueryInterface(__uuidof(IDXGIResource1), (void**)&tempResource1); if (FAILED(hr)) TCU_FAIL("Unable to query IDXGIResource1 interface"); hr = tempResource1->CreateSharedHandle(getSecurityAttributes(), DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, /*lpName*/NULL, &m_sharedMemHandle[i]); tempResource1->Release(); if (FAILED(hr)) TCU_FAIL("Enable to get DX shared handle"); } else { IDXGIResource* tempResource = NULL; hr = m_pTexture[i]->QueryInterface(__uuidof(IDXGIResource), (void**)&tempResource); if (FAILED(hr)) TCU_FAIL("Query interface of IDXGIResource failed"); hr = tempResource->GetSharedHandle(&m_sharedMemHandle[i]); tempResource->Release(); if (FAILED(hr)) TCU_FAIL("Failed to get DX shared handle"); } hr = m_pTexture[i]->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&m_keyedMutex[i]); if (FAILED(hr)) TCU_FAIL("Unable to query DX11 keyed mutex interface"); // Take ownership of the lock. m_keyedMutex[i]->AcquireSync(KEYED_MUTEX_INIT, INFINITE); } m_sharedMemSize = 0; m_sharedMemOffset = 0; hr = m_pDevice->CreateRenderTargetView(m_pTexture[BUFFER_VK_READ], NULL, &m_pRenderTargetView); if (FAILED(hr)) TCU_FAIL("Unable to create DX11 render target view"); m_pContext->OMSetRenderTargets(1, &m_pRenderTargetView, NULL); // Setup the viewport D3D11_VIEWPORT vp; vp.Width = (FLOAT)m_resourceDesc.size.x(); vp.Height = (FLOAT)m_resourceDesc.size.y(); vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = 0; vp.TopLeftY = 0; m_pContext->RSSetViewports(1, &vp); // Compile the vertex shader LPCSTR shader = "Texture2D txDiffuse : register(t0);\n" "SamplerState samLinear : register(s0);\n" "struct VS_INPUT\n" "{\n" " float4 Pos : POSITION;\n" " float2 Tex : TEXCOORD0;\n" "};\n" "struct PS_INPUT\n" "{\n" " float4 Pos : SV_POSITION;\n" " float2 Tex : TEXCOORD0;\n" "};\n" "PS_INPUT VS(VS_INPUT input)\n" "{\n" " PS_INPUT output = (PS_INPUT)0;\n" " output.Pos = input.Pos;\n" " output.Tex = input.Tex;\n" "\n" " return output;\n" "}\n" "float4 PS(PS_INPUT input) : SV_Target\n" "{\n" " return txDiffuse.Sample(samLinear, input.Tex);\n" "}\n"; // Define the input layout D3D11_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; createShaders(shader, "VS", "vs_4_0", ARRAYSIZE(layout), layout, &m_pVertexShader, "PS", "ps_4_0", &m_pPixelShader); struct SimpleVertex { float Pos[3]; float Tex[2]; }; SimpleVertex vertices[] = { { { -1.f, -1.f, 0.0f }, { 0.0f, 1.0f } }, { { -1.f, 1.f, 0.0f }, { 0.0f, 0.0f } }, { { 1.f, -1.f, 0.0f }, { 1.0f, 1.0f } }, { { 1.f, 1.f, 0.0f }, { 1.0f, 0.0f } }, }; D3D11_BUFFER_DESC bd = { }; bd.Usage = D3D11_USAGE_DEFAULT; bd.ByteWidth = sizeof (vertices); bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; bd.CPUAccessFlags = 0; D3D11_SUBRESOURCE_DATA InitData = { }; InitData.pSysMem = vertices; hr = m_pDevice->CreateBuffer(&bd, &InitData, &m_pVertexBuffer); if (FAILED(hr)) TCU_FAIL("Failed to create DX11 vertex buffer"); // Set vertex buffer UINT stride = sizeof (SimpleVertex); UINT offset = 0; m_pContext->IASetVertexBuffers(0, 1, &m_pVertexBuffer, &stride, &offset); // Set primitive topology m_pContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); m_pTextureRV = NULL; D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = { }; SRVDesc.Format = getDxgiFormat(m_resourceDesc.imageFormat); SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; SRVDesc.Texture2D.MipLevels = 1; hr = m_pDevice->CreateShaderResourceView(m_pTexture[BUFFER_VK_WRITE], &SRVDesc, &m_pTextureRV); if (FAILED(hr)) TCU_FAIL("Failed to create DX11 resource view"); // Create the sample state D3D11_SAMPLER_DESC sampDesc = { }; sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; sampDesc.MinLOD = 0; sampDesc.MaxLOD = D3D11_FLOAT32_MAX; hr = m_pDevice->CreateSamplerState(&sampDesc, &m_pSamplerLinear); if (FAILED(hr)) TCU_FAIL("Failed to create DX11 sampler state"); // Release the lock for VK to write into the texture. m_keyedMutex[BUFFER_VK_WRITE]->ReleaseSync(KEYED_MUTEX_VK_WRITE); } } ~DX11Operation () { cleanup(); } #endif // #if (DE_OS == DE_OS_WIN32) NativeHandle getNativeHandle (Buffer buffer) { #if (DE_OS == DE_OS_WIN32) return NativeHandle((m_isMemNtHandle) ? NativeHandle::WIN32HANDLETYPE_NT : NativeHandle::WIN32HANDLETYPE_KMT, vk::pt::Win32Handle(m_sharedMemHandle[buffer])); #else DE_UNREF(buffer); return NativeHandle(); #endif } void copyMemory () { #if (DE_OS == DE_OS_WIN32) m_keyedMutex[BUFFER_VK_WRITE]->AcquireSync(KEYED_MUTEX_DX_COPY, INFINITE); if (m_resourceDesc.type == RESOURCE_TYPE_BUFFER) { m_pContext->CopySubresourceRegion(m_pBuffer[BUFFER_VK_READ], 0, 0, 0, 0, m_pBuffer[BUFFER_VK_WRITE], 0, NULL); } else { m_pContext->OMSetRenderTargets(1, &m_pRenderTargetView, NULL); const FLOAT gray[] = { 0.f, 0.f, 1.f, 1.f }; m_pContext->ClearRenderTargetView(m_pRenderTargetView, gray); m_pContext->VSSetShader(m_pVertexShader, NULL, 0); m_pContext->PSSetShader(m_pPixelShader, NULL, 0); m_pContext->PSSetShaderResources(0, 1, &m_pTextureRV); m_pContext->PSSetSamplers(0, 1, &m_pSamplerLinear); m_pContext->Draw(4, 0); } m_keyedMutex[BUFFER_VK_WRITE]->ReleaseSync(KEYED_MUTEX_DONE); m_keyedMutex[BUFFER_VK_READ]->ReleaseSync(KEYED_MUTEX_VK_VERIFY); #endif // #if (DE_OS == DE_OS_WIN32) } #if (DE_OS == DE_OS_WIN32) void d3dx11CompileShader (const char* shaderCode, const char * entryPoint, const char* shaderModel, ID3D10Blob** ppBlobOut) { HRESULT hr; ID3D10Blob* pErrorBlob; hr = m_fnD3DX11CompileFromMemory (shaderCode, strlen(shaderCode), "Memory", NULL, NULL, entryPoint, shaderModel, 0, 0, NULL, ppBlobOut, &pErrorBlob, NULL); if (pErrorBlob) pErrorBlob->Release(); if (FAILED(hr)) TCU_FAIL("D3DX11CompileFromMemory failed to compile shader"); } void d3dCompileShader (const char* shaderCode, const char * entryPoint, const char* shaderModel, ID3DBlob** ppBlobOut) { HRESULT hr; ID3DBlob* pErrorBlob; hr = m_fnD3DCompile (shaderCode, strlen(shaderCode), NULL, NULL, NULL, entryPoint, shaderModel, 0, 0, ppBlobOut, &pErrorBlob); if (pErrorBlob) pErrorBlob->Release(); if (FAILED(hr)) TCU_FAIL("D3DCompile failed to compile shader"); } void createShaders (const char* shaderSrc, const char* vsEntryPoint, const char* vsShaderModel, UINT numLayoutDesc, D3D11_INPUT_ELEMENT_DESC* pLayoutDesc, ID3D11VertexShader** pVertexShader, const char* psEntryPoint, const char* psShaderModel, ID3D11PixelShader** pPixelShader) { HRESULT hr; if (m_fnD3DX11CompileFromMemory) { // VS ID3D10Blob* pVSBlob; d3dx11CompileShader(shaderSrc, vsEntryPoint, vsShaderModel, &pVSBlob); hr = m_pDevice->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, pVertexShader); if (FAILED(hr)) TCU_FAIL("Failed to create DX11 vertex shader"); ID3D11InputLayout *pVertexLayout; hr = m_pDevice->CreateInputLayout(pLayoutDesc, numLayoutDesc, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &pVertexLayout); if (FAILED(hr)) TCU_FAIL("Failed to create vertex input layout"); m_pContext->IASetInputLayout(pVertexLayout); pVertexLayout->Release(); pVSBlob->Release(); // PS ID3D10Blob* pPSBlob; d3dx11CompileShader(shaderSrc, psEntryPoint, psShaderModel, &pPSBlob); hr = m_pDevice->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, pPixelShader); if (FAILED(hr)) TCU_FAIL("Failed to create DX11 pixel shader"); } else { // VS ID3DBlob* pVSBlob; d3dCompileShader(shaderSrc, vsEntryPoint, vsShaderModel, &pVSBlob); hr = m_pDevice->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, pVertexShader); if (FAILED(hr)) TCU_FAIL("Failed to create DX11 vertex shader"); ID3D11InputLayout *pVertexLayout; hr = m_pDevice->CreateInputLayout(pLayoutDesc, numLayoutDesc, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &pVertexLayout); if (FAILED(hr)) TCU_FAIL("Failed to create vertex input layout"); m_pContext->IASetInputLayout(pVertexLayout); pVertexLayout->Release(); pVSBlob->Release(); // PS ID3DBlob* pPSBlob; d3dCompileShader(shaderSrc, psEntryPoint, psShaderModel, &pPSBlob); hr = m_pDevice->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, pPixelShader); if (FAILED(hr)) TCU_FAIL("Failed to create DX11 pixel shader"); } } #endif // #if (DE_OS == DE_OS_WIN32) private: #if (DE_OS == DE_OS_WIN32) void cleanup () { if (m_securityAttributes.lpSecurityDescriptor) { freeSecurityDescriptor(m_securityAttributes.lpSecurityDescriptor); m_securityAttributes.lpSecurityDescriptor = NULL; } if (m_pContext) m_pContext->ClearState(); if (m_pRenderTargetView) { m_pRenderTargetView->Release(); m_pRenderTargetView = NULL; } if (m_pSamplerLinear) { m_pSamplerLinear->Release(); m_pSamplerLinear = NULL; } if (m_pTextureRV) { m_pTextureRV->Release(); m_pTextureRV = NULL; } if (m_pVertexBuffer) { m_pVertexBuffer->Release(); m_pVertexBuffer = NULL; } if (m_pVertexShader) { m_pVertexShader->Release(); m_pVertexShader = NULL; } if (m_pPixelShader) { m_pPixelShader->Release(); m_pPixelShader = NULL; } for (int i = 0; i < BUFFER_COUNT; i++) { if (m_keyedMutex[i]) { m_keyedMutex[i]->AcquireSync(KEYED_MUTEX_DONE, INFINITE); m_keyedMutex[i]->Release(); m_keyedMutex[i] = NULL; } if (m_isMemNtHandle && m_sharedMemHandle[i]) { CloseHandle(m_sharedMemHandle[i]); m_sharedMemHandle[i] = 0; } if (m_pBuffer[i]) { m_pBuffer[i]->Release(); m_pBuffer[i] = NULL; } if (m_pTexture[i]) { m_pTexture[i]->Release(); m_pTexture[i] = NULL; } } } static void* getSecurityDescriptor () { PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR)deCalloc(SECURITY_DESCRIPTOR_MIN_LENGTH + 2 * sizeof (void**)); if (pSD) { PSID* ppEveryoneSID = (PSID*)((PBYTE)pSD + SECURITY_DESCRIPTOR_MIN_LENGTH); PACL* ppACL = (PACL*)((PBYTE)ppEveryoneSID + sizeof(PSID*)); bool res = InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION); DE_ASSERT(res); SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, ppEveryoneSID); EXPLICIT_ACCESS ea = { }; ea.grfAccessPermissions = STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL; ea.grfAccessMode = SET_ACCESS; ea.grfInheritance = INHERIT_ONLY; ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; ea.Trustee.ptstrName = (LPTSTR)*ppEveryoneSID; SetEntriesInAcl(1, &ea, NULL, ppACL); res = SetSecurityDescriptorDacl(pSD, TRUE, *ppACL, FALSE); DE_ASSERT(res); } return pSD; } static void freeSecurityDescriptor (void* pSD) { if (pSD) { PSID* ppEveryoneSID = (PSID*)((PBYTE)pSD + SECURITY_DESCRIPTOR_MIN_LENGTH); PACL* ppACL = (PACL*)((PBYTE)ppEveryoneSID + sizeof(PSID*)); if (*ppEveryoneSID) FreeSid(*ppEveryoneSID); if (*ppACL) LocalFree(*ppACL); deFree(pSD); } } static DXGI_FORMAT getDxgiFormat (vk::VkFormat format) { switch (format) { case vk::VK_FORMAT_R8_UNORM: return DXGI_FORMAT_R8_UNORM; case vk::VK_FORMAT_R16_UINT: return DXGI_FORMAT_R16_UINT; case vk::VK_FORMAT_R8G8B8A8_UNORM: return DXGI_FORMAT_R8G8B8A8_UNORM; case vk::VK_FORMAT_R16G16B16A16_UINT: return DXGI_FORMAT_R16G16B16A16_UINT; case vk::VK_FORMAT_R32G32B32A32_SFLOAT: return DXGI_FORMAT_R32G32B32A32_FLOAT; case vk::VK_FORMAT_D16_UNORM: return DXGI_FORMAT_D16_UNORM; case vk::VK_FORMAT_D32_SFLOAT: return DXGI_FORMAT_D32_FLOAT; default: TCU_CHECK_INTERNAL(!"Unsupported DXGI format"); return DXGI_FORMAT_UNKNOWN; } } ResourceDescription m_resourceDesc; deUint64 m_sharedMemSize; deUint64 m_sharedMemOffset; HANDLE m_sharedMemHandle[BUFFER_COUNT]; bool m_isMemNtHandle; ID3D11Device* m_pDevice; ID3D11DeviceContext* m_pContext; LPD3DX11COMPILEFROMMEMORY m_fnD3DX11CompileFromMemory; pD3DCompile m_fnD3DCompile; ID3D11RenderTargetView* m_pRenderTargetView; ID3D11VertexShader* m_pVertexShader; ID3D11PixelShader* m_pPixelShader; ID3D11Buffer* m_pVertexBuffer; ID3D11ShaderResourceView* m_pTextureRV; ID3D11SamplerState* m_pSamplerLinear; ID3D11Texture2D* m_pTexture[BUFFER_COUNT]; ID3D11Buffer* m_pBuffer[BUFFER_COUNT]; IDXGIKeyedMutex* m_keyedMutex[BUFFER_COUNT]; UINT m_numFrames; SECURITY_ATTRIBUTES m_securityAttributes; SECURITY_ATTRIBUTES* getSecurityAttributes () { m_securityAttributes.nLength = sizeof (SECURITY_ATTRIBUTES); m_securityAttributes.bInheritHandle = TRUE; if (!m_securityAttributes.lpSecurityDescriptor) m_securityAttributes.lpSecurityDescriptor = getSecurityDescriptor(); return &m_securityAttributes; } #endif // #if (DE_OS == DE_OS_WIN32) }; class DX11OperationSupport { public: DX11OperationSupport (const vk::InstanceInterface& vki, vk::VkPhysicalDevice physicalDevice) #if (DE_OS == DE_OS_WIN32) : m_hD3D11Lib (0) , m_hD3DX11Lib (0) , m_hD3DCompilerLib (0) , m_hDxgiLib (0) , m_fnD3D11CreateDevice (0) , m_fnD3DX11CompileFromMemory (0) , m_fnD3DCompile (0) #endif { #if (DE_OS == DE_OS_WIN32) HRESULT hr; vk::VkPhysicalDeviceIDProperties propertiesId = { vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES }; vk::VkPhysicalDeviceProperties2 properties = { vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 }; properties.pNext = &propertiesId; vki.getPhysicalDeviceProperties2(physicalDevice, &properties); if (!propertiesId.deviceLUIDValid) TCU_FAIL("Physical device deviceLUIDValid is not valid"); m_hD3D11Lib = LoadLibrary("d3d11.dll"); if (!m_hD3D11Lib) TCU_FAIL("Failed to load d3d11.dll"); m_fnD3D11CreateDevice = (LPD3D11CREATEDEVICE) GetProcAddress(m_hD3D11Lib, "D3D11CreateDevice"); if (!m_fnD3D11CreateDevice) TCU_FAIL("Unable to find D3D11CreateDevice() function"); m_hD3DX11Lib = LoadLibrary("d3dx11_42.dll"); if (m_hD3DX11Lib) m_fnD3DX11CompileFromMemory = (LPD3DX11COMPILEFROMMEMORY) GetProcAddress(m_hD3DX11Lib, "D3DX11CompileFromMemory"); else { m_hD3DCompilerLib = LoadLibrary("d3dcompiler_43.dll"); if (!m_hD3DCompilerLib) m_hD3DCompilerLib = LoadLibrary("d3dcompiler_47.dll"); if (!m_hD3DCompilerLib) TCU_FAIL("Unable to load DX11 d3dcompiler_43.dll or d3dcompiler_47.dll"); m_fnD3DCompile = (pD3DCompile)GetProcAddress(m_hD3DCompilerLib, "D3DCompile"); if (!m_fnD3DCompile) TCU_FAIL("Unable to load find D3DCompile"); } m_hDxgiLib = LoadLibrary("dxgi.dll"); if (!m_hDxgiLib) TCU_FAIL("Unable to load DX11 dxgi.dll"); typedef HRESULT (WINAPI *LPCREATEDXGIFACTORY1)(REFIID riid, void** ppFactory); LPCREATEDXGIFACTORY1 CreateDXGIFactory1 = (LPCREATEDXGIFACTORY1)GetProcAddress(m_hDxgiLib, "CreateDXGIFactory1"); if (!CreateDXGIFactory1) TCU_FAIL("Unable to load find CreateDXGIFactory1"); IDXGIFactory1* pFactory = NULL; hr = CreateDXGIFactory1(__uuidof(IDXGIFactory), (void**)&pFactory); if (FAILED(hr)) TCU_FAIL("Unable to create IDXGIFactory interface"); IDXGIAdapter *pAdapter = NULL; for (UINT i = 0; pFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND; ++i) { DXGI_ADAPTER_DESC desc; pAdapter->GetDesc(&desc); if (deMemCmp(&desc.AdapterLuid, propertiesId.deviceLUID, VK_LUID_SIZE) == 0) break; } pFactory->Release(); D3D_FEATURE_LEVEL fLevel[] = {D3D_FEATURE_LEVEL_11_0}; UINT devflags = D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS | // no separate D3D11 worker thread #if 0 D3D11_CREATE_DEVICE_DEBUG | // useful for diagnosing DX failures #endif D3D11_CREATE_DEVICE_SINGLETHREADED; hr = m_fnD3D11CreateDevice (pAdapter, pAdapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, NULL, devflags, fLevel, DE_LENGTH_OF_ARRAY(fLevel), D3D11_SDK_VERSION, &m_pDevice, NULL, &m_pContext); if (pAdapter) { pAdapter->Release(); } if (!m_pDevice) TCU_FAIL("Failed to created DX11 device"); if (!m_pContext) TCU_FAIL("Failed to created DX11 context"); #else DE_UNREF(vki); DE_UNREF(physicalDevice); TCU_THROW(NotSupportedError, "OS not supported"); #endif } ~DX11OperationSupport () { #if (DE_OS == DE_OS_WIN32) cleanup (); #endif } #if (DE_OS == DE_OS_WIN32) void cleanup () { if (m_pContext) { m_pContext->Release(); m_pContext = 0; } if (m_pDevice) { m_pDevice->Release(); m_pDevice = 0; } if (m_hDxgiLib) { FreeLibrary(m_hDxgiLib); m_hDxgiLib = 0; } if (m_hD3DCompilerLib) { FreeLibrary(m_hD3DCompilerLib); m_hD3DCompilerLib = 0; } if (m_hD3DX11Lib) { FreeLibrary(m_hD3DX11Lib); m_hD3DX11Lib = 0; } if (m_hD3D11Lib) { FreeLibrary(m_hD3D11Lib); m_hD3D11Lib = 0; } } #endif virtual de::MovePtr build (const ResourceDescription& resourceDesc, vk::VkExternalMemoryHandleTypeFlagBits memoryHandleType) const { #if (DE_OS == DE_OS_WIN32) return de::MovePtr(new DX11Operation(resourceDesc, memoryHandleType, m_pDevice, m_pContext, m_fnD3DX11CompileFromMemory, m_fnD3DCompile)); #else DE_UNREF(resourceDesc); DE_UNREF(memoryHandleType); TCU_THROW(NotSupportedError, "OS not supported"); #endif } private: #if (DE_OS == DE_OS_WIN32) typedef HRESULT (WINAPI *LPD3D11CREATEDEVICE)(IDXGIAdapter*, D3D_DRIVER_TYPE, HMODULE, UINT, const D3D_FEATURE_LEVEL*, UINT, UINT, ID3D11Device **, D3D_FEATURE_LEVEL*, ID3D11DeviceContext**); HMODULE m_hD3D11Lib; HMODULE m_hD3DX11Lib; HMODULE m_hD3DCompilerLib; HMODULE m_hDxgiLib; LPD3D11CREATEDEVICE m_fnD3D11CreateDevice; LPD3DX11COMPILEFROMMEMORY m_fnD3DX11CompileFromMemory; pD3DCompile m_fnD3DCompile; ID3D11Device* m_pDevice; ID3D11DeviceContext* m_pContext; #endif }; // Class to wrap a singleton instance and device class InstanceAndDevice { InstanceAndDevice (Context& context) : m_instance (createTestInstance(context)) , m_vki (m_instance.getDriver()) , m_physicalDevice (vk::chooseDevice(m_vki, m_instance, context.getTestContext().getCommandLine())) , m_logicalDevice (createTestDevice(context, m_instance, m_vki, m_physicalDevice)) , m_supportDX11 (new DX11OperationSupport(m_vki, m_physicalDevice)) { } public: static vk::VkInstance getInstance(Context& context) { if (!m_instanceAndDevice) m_instanceAndDevice = SharedPtr(new InstanceAndDevice(context)); return m_instanceAndDevice->m_instance; } static const vk::InstanceDriver& getDriver() { DE_ASSERT(m_instanceAndDevice); return m_instanceAndDevice->m_instance.getDriver(); } static vk::VkPhysicalDevice getPhysicalDevice() { DE_ASSERT(m_instanceAndDevice); return m_instanceAndDevice->m_physicalDevice; } static const Unique& getDevice() { DE_ASSERT(m_instanceAndDevice); return m_instanceAndDevice->m_logicalDevice; } static const de::UniquePtr& getSupportDX11() { DE_ASSERT(m_instanceAndDevice); return m_instanceAndDevice->m_supportDX11; } static void collectMessages() { DE_ASSERT(m_instanceAndDevice); m_instanceAndDevice->m_instance.collectMessages(); } static void destroy() { m_instanceAndDevice.clear(); } private: CustomInstance m_instance; const vk::InstanceDriver& m_vki; const vk::VkPhysicalDevice m_physicalDevice; const Unique m_logicalDevice; const de::UniquePtr m_supportDX11; static SharedPtr m_instanceAndDevice; }; SharedPtr InstanceAndDevice::m_instanceAndDevice; class Win32KeyedMutexTestInstance : public TestInstance { public: Win32KeyedMutexTestInstance (Context& context, TestConfig config); virtual tcu::TestStatus iterate (void); private: const TestConfig m_config; const de::UniquePtr m_supportWriteOp; const de::UniquePtr m_supportReadOp; const vk::VkInstance m_instance; const vk::InstanceDriver& m_vki; const vk::VkPhysicalDevice m_physicalDevice; const std::vector m_queueFamilies; const std::vector m_queueFamilyIndices; const vk::Unique& m_device; const vk::DeviceDriver m_vkd; const vk::VkExternalMemoryHandleTypeFlagBits m_memoryHandleType; // \todo Should this be moved to the group same way as in the other tests? PipelineCacheData m_pipelineCacheData; tcu::ResultCollector m_resultCollector; size_t m_queueNdx; bool m_useDedicatedAllocation; }; Win32KeyedMutexTestInstance::Win32KeyedMutexTestInstance (Context& context, TestConfig config) : TestInstance (context) , m_config (config) , m_supportWriteOp (makeOperationSupport(config.writeOp, config.resource)) , m_supportReadOp (makeOperationSupport(config.readOp, config.resource)) , m_instance (InstanceAndDevice::getInstance(context)) , m_vki (InstanceAndDevice::getDriver()) , m_physicalDevice (InstanceAndDevice::getPhysicalDevice()) , m_queueFamilies (vk::getPhysicalDeviceQueueFamilyProperties(m_vki, m_physicalDevice)) , m_queueFamilyIndices (getFamilyIndices(m_queueFamilies)) , m_device (InstanceAndDevice::getDevice()) , m_vkd (context.getPlatformInterface(), m_instance, *m_device) , m_memoryHandleType ((m_config.resource.type == RESOURCE_TYPE_IMAGE) ? m_config.memoryHandleTypeImage : m_config.memoryHandleTypeBuffer) , m_resultCollector (context.getTestContext().getLog()) , m_queueNdx (0) , m_useDedicatedAllocation (false) { #if (DE_OS == DE_OS_WIN32) TestLog& log = m_context.getTestContext().getLog(); // Check resource support if (m_config.resource.type == RESOURCE_TYPE_IMAGE) { if (m_memoryHandleType == vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT && !IsWindows8OrGreater()) TCU_THROW(NotSupportedError, "Memory handle type not supported by this OS"); const vk::VkPhysicalDeviceExternalImageFormatInfo externalInfo = { vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, DE_NULL, m_memoryHandleType }; const vk::VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = { vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, &externalInfo, m_config.resource.imageFormat, m_config.resource.imageType, vk::VK_IMAGE_TILING_OPTIMAL, m_supportReadOp->getInResourceUsageFlags() | m_supportWriteOp->getOutResourceUsageFlags(), 0u }; vk::VkExternalImageFormatProperties externalProperties = { vk::VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES, DE_NULL, { 0u, 0u, 0u } }; vk::VkImageFormatProperties2 formatProperties = { vk::VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, &externalProperties, { { 0u, 0u, 0u }, 0u, 0u, 0u, 0u, } }; const vk::VkResult res = m_vki.getPhysicalDeviceImageFormatProperties2(m_physicalDevice, &imageFormatInfo, &formatProperties); if (res == vk::VK_ERROR_FORMAT_NOT_SUPPORTED) TCU_THROW(NotSupportedError, "Handle type is not compatible"); VK_CHECK(res); // \todo How to log this nicely? log << TestLog::Message << "External image format properties: " << imageFormatInfo << "\n"<< externalProperties << TestLog::EndMessage; if ((externalProperties.externalMemoryProperties.externalMemoryFeatures & vk::VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT) == 0) TCU_THROW(NotSupportedError, "Importing image resource not supported"); if (externalProperties.externalMemoryProperties.externalMemoryFeatures & vk::VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT) m_useDedicatedAllocation = true; } else { if (m_memoryHandleType == vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT && !IsWindows8OrGreater()) TCU_THROW(NotSupportedError, "Memory handle type not supported by this OS"); const vk::VkPhysicalDeviceExternalBufferInfo info = { vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO, DE_NULL, 0u, m_supportReadOp->getInResourceUsageFlags() | m_supportWriteOp->getOutResourceUsageFlags(), m_memoryHandleType }; vk::VkExternalBufferProperties properties = { vk::VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES, DE_NULL, { 0u, 0u, 0u} }; m_vki.getPhysicalDeviceExternalBufferProperties(m_physicalDevice, &info, &properties); log << TestLog::Message << "External buffer properties: " << info << "\n" << properties << TestLog::EndMessage; if ((properties.externalMemoryProperties.externalMemoryFeatures & vk::VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT) == 0) TCU_THROW(NotSupportedError, "Importing memory type not supported"); if (properties.externalMemoryProperties.externalMemoryFeatures & vk::VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT) m_useDedicatedAllocation = true; } #else DE_UNREF(m_useDedicatedAllocation); TCU_THROW(NotSupportedError, "OS not supported"); #endif } tcu::TestStatus Win32KeyedMutexTestInstance::iterate (void) { TestLog& log (m_context.getTestContext().getLog()); try { const deUint32 queueFamily = (deUint32)m_queueNdx; const tcu::ScopedLogSection queuePairSection (log, "Queue-" + de::toString(queueFamily), "Queue-" + de::toString(queueFamily)); const vk::VkQueue queue (getDeviceQueue(m_vkd, *m_device, queueFamily, 0u)); const vk::Unique commandPool (createCommandPool(m_vkd, *m_device, 0u, queueFamily)); const vk::Unique commandBufferWrite (allocateCommandBuffer(m_vkd, *m_device, *commandPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY)); const vk::Unique commandBufferRead (allocateCommandBuffer(m_vkd, *m_device, *commandPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY)); vk::SimpleAllocator allocator (m_vkd, *m_device, vk::getPhysicalDeviceMemoryProperties(m_vki, m_physicalDevice)); OperationContext operationContext (m_context, SynchronizationType::LEGACY, m_vki, m_vkd, m_physicalDevice, *m_device, allocator, m_context.getBinaryCollection(), m_pipelineCacheData); if (!checkQueueFlags(m_queueFamilies[m_queueNdx].queueFlags, vk::VK_QUEUE_GRAPHICS_BIT)) TCU_THROW(NotSupportedError, "Operation not supported by the source queue"); const de::UniquePtr dx11Op (InstanceAndDevice::getSupportDX11()->build(m_config.resource, m_memoryHandleType)); NativeHandle nativeHandleWrite = dx11Op->getNativeHandle(DX11Operation::BUFFER_VK_WRITE); const de::UniquePtr resourceWrite (importResource(m_vkd, *m_device, m_config.resource, m_queueFamilyIndices, *m_supportReadOp, *m_supportWriteOp, nativeHandleWrite, m_memoryHandleType)); NativeHandle nativeHandleRead = dx11Op->getNativeHandle(DX11Operation::BUFFER_VK_READ); const de::UniquePtr resourceRead (importResource(m_vkd, *m_device, m_config.resource, m_queueFamilyIndices, *m_supportReadOp, *m_supportWriteOp, nativeHandleRead, m_memoryHandleType)); const de::UniquePtr writeOp (m_supportWriteOp->build(operationContext, *resourceWrite)); const de::UniquePtr readOp (m_supportReadOp->build(operationContext, *resourceRead)); const SyncInfo writeSync = writeOp->getOutSyncInfo(); const SyncInfo readSync = readOp->getInSyncInfo(); beginCommandBuffer(m_vkd, *commandBufferWrite); writeOp->recordCommands(*commandBufferWrite); recordWriteBarrier(m_vkd, *commandBufferWrite, *resourceWrite, writeSync, queueFamily, readSync); endCommandBuffer(m_vkd, *commandBufferWrite); beginCommandBuffer(m_vkd, *commandBufferRead); recordReadBarrier(m_vkd, *commandBufferRead, *resourceRead, writeSync, readSync, queueFamily); readOp->recordCommands(*commandBufferRead); endCommandBuffer(m_vkd, *commandBufferRead); { vk::VkDeviceMemory memory = resourceWrite->getMemory(); deUint64 keyInit = DX11Operation::KEYED_MUTEX_VK_WRITE; deUint32 timeout = 0xFFFFFFFF; // INFINITE deUint64 keyExternal = DX11Operation::KEYED_MUTEX_DX_COPY; vk::VkWin32KeyedMutexAcquireReleaseInfoKHR keyedMutexInfo = { vk::VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHR, DE_NULL, 1, &memory, &keyInit, &timeout, 1, &memory, &keyExternal, }; const vk::VkCommandBuffer commandBuffer = *commandBufferWrite; const vk::VkSubmitInfo submitInfo = { vk::VK_STRUCTURE_TYPE_SUBMIT_INFO, &keyedMutexInfo, 0u, DE_NULL, DE_NULL, 1u, &commandBuffer, 0u, DE_NULL }; VK_CHECK(m_vkd.queueSubmit(queue, 1u, &submitInfo, DE_NULL)); } dx11Op->copyMemory(); { vk::VkDeviceMemory memory = resourceRead->getMemory(); deUint64 keyInternal = DX11Operation::KEYED_MUTEX_VK_VERIFY; deUint32 timeout = 0xFFFFFFFF; // INFINITE deUint64 keyExternal = DX11Operation::KEYED_MUTEX_DONE; vk::VkWin32KeyedMutexAcquireReleaseInfoKHR keyedMutexInfo = { vk::VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHR, DE_NULL, 1, &memory, &keyInternal, &timeout, 1, &memory, &keyExternal, }; const vk::VkCommandBuffer commandBuffer = *commandBufferRead; const vk::VkSubmitInfo submitInfo = { vk::VK_STRUCTURE_TYPE_SUBMIT_INFO, &keyedMutexInfo, 0u, DE_NULL, DE_NULL, 1u, &commandBuffer, 0u, DE_NULL }; VK_CHECK(m_vkd.queueSubmit(queue, 1u, &submitInfo, DE_NULL)); } VK_CHECK(m_vkd.queueWaitIdle(queue)); { const Data expected = writeOp->getData(); const Data actual = readOp->getData(); DE_ASSERT(expected.size == actual.size); if (0 != deMemCmp(expected.data, actual.data, expected.size)) { const size_t maxBytesLogged = 256; std::ostringstream expectedData; std::ostringstream actualData; size_t byteNdx = 0; // Find first byte difference for (; actual.data[byteNdx] == expected.data[byteNdx]; byteNdx++) { // Nothing } log << TestLog::Message << "First different byte at offset: " << byteNdx << TestLog::EndMessage; // Log 8 previous bytes before the first incorrect byte if (byteNdx > 8) { expectedData << "... "; actualData << "... "; byteNdx -= 8; } else byteNdx = 0; for (size_t i = 0; i < maxBytesLogged && byteNdx < expected.size; i++, byteNdx++) { expectedData << (i > 0 ? ", " : "") << (deUint32)expected.data[byteNdx]; actualData << (i > 0 ? ", " : "") << (deUint32)actual.data[byteNdx]; } if (expected.size > byteNdx) { expectedData << "..."; actualData << "..."; } log << TestLog::Message << "Expected data: (" << expectedData.str() << ")" << TestLog::EndMessage; log << TestLog::Message << "Actual data: (" << actualData.str() << ")" << TestLog::EndMessage; m_resultCollector.fail("Memory contents don't match"); } } } catch (const tcu::NotSupportedError& error) { log << TestLog::Message << "Not supported: " << error.getMessage() << TestLog::EndMessage; } catch (const tcu::TestError& error) { m_resultCollector.fail(std::string("Exception: ") + error.getMessage()); } // Collect possible validation errors. InstanceAndDevice::collectMessages(); // Move to next queue { m_queueNdx++; if (m_queueNdx >= m_queueFamilies.size()) { return tcu::TestStatus(m_resultCollector.getResult(), m_resultCollector.getMessage()); } else { return tcu::TestStatus::incomplete(); } } } struct Progs { void init (vk::SourceCollections& dst, TestConfig config) const { const de::UniquePtr readOp (makeOperationSupport(config.readOp, config.resource)); const de::UniquePtr writeOp (makeOperationSupport(config.writeOp, config.resource)); readOp->initPrograms(dst); writeOp->initPrograms(dst); } }; } // anonymous static void createTests (tcu::TestCaseGroup* group) { tcu::TestContext& testCtx = group->getTestContext(); const struct { vk::VkExternalMemoryHandleTypeFlagBits memoryHandleTypeBuffer; vk::VkExternalMemoryHandleTypeFlagBits memoryHandleTypeImage; const char* nameSuffix; } cases[] = { { (vk::VkExternalMemoryHandleTypeFlagBits)0u, // DX11 doesn't support buffers with an NT handle vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT, "_nt" }, { vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT, vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT, "_kmt" }, }; for (size_t writeOpNdx = 0; writeOpNdx < DE_LENGTH_OF_ARRAY(s_writeOps); ++writeOpNdx) for (size_t readOpNdx = 0; readOpNdx < DE_LENGTH_OF_ARRAY(s_readOps); ++readOpNdx) { const OperationName writeOp = s_writeOps[writeOpNdx]; const OperationName readOp = s_readOps[readOpNdx]; const std::string opGroupName = getOperationName(writeOp) + "_" + getOperationName(readOp); bool empty = true; de::MovePtr opGroup (new tcu::TestCaseGroup(testCtx, opGroupName.c_str(), "")); for (size_t resourceNdx = 0; resourceNdx < DE_LENGTH_OF_ARRAY(s_resourcesWin32KeyedMutex); ++resourceNdx) { const ResourceDescription& resource = s_resourcesWin32KeyedMutex[resourceNdx]; for (size_t caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); caseNdx++) { if (resource.type == RESOURCE_TYPE_BUFFER && !cases[caseNdx].memoryHandleTypeBuffer) continue; if (resource.type == RESOURCE_TYPE_IMAGE && !cases[caseNdx].memoryHandleTypeImage) continue; std::string name = getResourceName(resource) + cases[caseNdx].nameSuffix; if (isResourceSupported(writeOp, resource) && isResourceSupported(readOp, resource)) { const TestConfig config (resource, writeOp, readOp, cases[caseNdx].memoryHandleTypeBuffer, cases[caseNdx].memoryHandleTypeImage); opGroup->addChild(new InstanceFactory1(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, "", Progs(), config)); empty = false; } } } if (!empty) group->addChild(opGroup.release()); } } static void cleanupGroup (tcu::TestCaseGroup* group) { DE_UNREF(group); // Destroy singleton object InstanceAndDevice::destroy(); } tcu::TestCaseGroup* createWin32KeyedMutexTest (tcu::TestContext& testCtx) { return createTestGroup(testCtx, "win32_keyed_mutex", "", createTests, cleanupGroup); } } // synchronization } // vkt