| // Copyright (C) 2024 The Android Open Source Project |
| // |
| // 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. |
| |
| #include "GfxstreamEnd2EndTestUtils.h" |
| |
| namespace gfxstream { |
| namespace tests { |
| namespace utils { |
| |
| using testing::Eq; |
| using testing::Ge; |
| using testing::IsEmpty; |
| using testing::IsNull; |
| using testing::Not; |
| using testing::NotNull; |
| |
| uint32_t getMemoryType(const vkhpp::PhysicalDevice& physicalDevice, |
| const vkhpp::MemoryRequirements& memoryRequirements, |
| vkhpp::MemoryPropertyFlags memoryProperties) { |
| const auto props = physicalDevice.getMemoryProperties(); |
| for (uint32_t i = 0; i < props.memoryTypeCount; i++) { |
| if (!(memoryRequirements.memoryTypeBits & (1 << i))) { |
| continue; |
| } |
| if ((props.memoryTypes[i].propertyFlags & memoryProperties) != memoryProperties) { |
| continue; |
| } |
| return i; |
| } |
| return -1; |
| } |
| |
| void readImageData(vkhpp::Image image, uint32_t width, uint32_t height, |
| vkhpp::ImageLayout currentLayout, void* dst, uint64_t dstSize, |
| const GfxstreamEnd2EndTest::TypicalVkTestEnvironment& testEnvironment) { |
| auto& instance = testEnvironment.instance; |
| auto& physicalDevice = testEnvironment.physicalDevice; |
| auto& device = testEnvironment.device; |
| auto& queue = testEnvironment.queue; |
| auto queueFamilyIndex = testEnvironment.queueFamilyIndex; |
| |
| // Read-back buffer |
| const vkhpp::BufferCreateInfo readbackBufferCreateInfo = { |
| .size = static_cast<VkDeviceSize>(dstSize), |
| .usage = vkhpp::BufferUsageFlagBits::eTransferDst, |
| .sharingMode = vkhpp::SharingMode::eExclusive, |
| }; |
| auto readbackBuffer = device->createBufferUnique(readbackBufferCreateInfo).value; |
| ASSERT_THAT(readbackBuffer, IsValidHandle()); |
| |
| vkhpp::MemoryRequirements readbackBufferMemoryRequirements{}; |
| device->getBufferMemoryRequirements(*readbackBuffer, &readbackBufferMemoryRequirements); |
| |
| const auto readbackBufferMemoryType = getMemoryType( |
| physicalDevice, readbackBufferMemoryRequirements, |
| vkhpp::MemoryPropertyFlagBits::eHostVisible | vkhpp::MemoryPropertyFlagBits::eHostCoherent); |
| |
| // Read-back memory |
| const vkhpp::MemoryAllocateInfo readbackBufferMemoryAllocateInfo = { |
| .allocationSize = readbackBufferMemoryRequirements.size, |
| .memoryTypeIndex = readbackBufferMemoryType, |
| }; |
| auto readbackBufferMemory = |
| device->allocateMemoryUnique(readbackBufferMemoryAllocateInfo).value; |
| ASSERT_THAT(readbackBufferMemory, IsValidHandle()); |
| ASSERT_THAT(device->bindBufferMemory(*readbackBuffer, *readbackBufferMemory, 0), IsVkSuccess()); |
| |
| // Command buffer |
| const vkhpp::CommandPoolCreateInfo commandPoolCreateInfo = { |
| .queueFamilyIndex = queueFamilyIndex, |
| }; |
| |
| auto commandPool = device->createCommandPoolUnique(commandPoolCreateInfo).value; |
| ASSERT_THAT(commandPool, IsValidHandle()); |
| const vkhpp::CommandBufferAllocateInfo commandBufferAllocateInfo = { |
| .level = vkhpp::CommandBufferLevel::ePrimary, |
| .commandPool = *commandPool, |
| .commandBufferCount = 1, |
| }; |
| auto readbackCommandBuffers = |
| device->allocateCommandBuffersUnique(commandBufferAllocateInfo).value; |
| ASSERT_THAT(readbackCommandBuffers, Not(IsEmpty())); |
| auto readbackCommandBuffer = std::move(readbackCommandBuffers[0]); |
| ASSERT_THAT(readbackCommandBuffer, IsValidHandle()); |
| |
| const vkhpp::CommandBufferBeginInfo commandBufferBeginInfo = { |
| .flags = vkhpp::CommandBufferUsageFlagBits::eOneTimeSubmit, |
| }; |
| readbackCommandBuffer->begin(commandBufferBeginInfo); |
| const vkhpp::ImageMemoryBarrier readbackBarrier{ |
| .oldLayout = currentLayout, |
| .newLayout = vkhpp::ImageLayout::eTransferSrcOptimal, |
| .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, |
| .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, |
| .image = image, |
| .subresourceRange = |
| { |
| .aspectMask = vkhpp::ImageAspectFlagBits::eColor, |
| .levelCount = 1, |
| .layerCount = 1, |
| }, |
| }; |
| |
| readbackCommandBuffer->pipelineBarrier( |
| vkhpp::PipelineStageFlagBits::eAllCommands, vkhpp::PipelineStageFlagBits::eAllCommands, |
| vkhpp::DependencyFlags(), nullptr, nullptr, readbackBarrier); |
| |
| const vkhpp::BufferImageCopy bufferImageCopy = { |
| .imageSubresource = |
| { |
| .aspectMask = vkhpp::ImageAspectFlagBits::eColor, |
| .layerCount = 1, |
| }, |
| .imageExtent = |
| { |
| .width = width, |
| .height = height, |
| .depth = 1, |
| }, |
| }; |
| readbackCommandBuffer->copyImageToBuffer(image, vkhpp::ImageLayout::eTransferSrcOptimal, |
| *readbackBuffer, 1, &bufferImageCopy); |
| |
| const vkhpp::ImageMemoryBarrier restoreBarrier{ |
| .oldLayout = vkhpp::ImageLayout::eTransferSrcOptimal, |
| .newLayout = currentLayout, |
| .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, |
| .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, |
| .image = image, |
| .subresourceRange = |
| { |
| .aspectMask = vkhpp::ImageAspectFlagBits::eColor, |
| .levelCount = 1, |
| .layerCount = 1, |
| }, |
| }; |
| |
| readbackCommandBuffer->pipelineBarrier( |
| vkhpp::PipelineStageFlagBits::eAllCommands, vkhpp::PipelineStageFlagBits::eAllCommands, |
| vkhpp::DependencyFlags(), nullptr, nullptr, restoreBarrier); |
| |
| readbackCommandBuffer->end(); |
| |
| auto readbackFence = device->createFenceUnique(vkhpp::FenceCreateInfo()).value; |
| ASSERT_THAT(readbackCommandBuffer, IsValidHandle()); |
| |
| // Execute the command to copy image back to buffer |
| const vkhpp::SubmitInfo readbackSubmitInfo = { |
| .commandBufferCount = 1, |
| .pCommandBuffers = &readbackCommandBuffer.get(), |
| }; |
| queue.submit(readbackSubmitInfo, *readbackFence); |
| |
| auto readbackWaitResult = device->waitForFences(*readbackFence, VK_TRUE, 3000000000L); |
| ASSERT_THAT(readbackWaitResult, IsVkSuccess()); |
| |
| // Verify content |
| void* mapped; |
| auto mapResult = device->mapMemory(*readbackBufferMemory, 0, VK_WHOLE_SIZE, |
| vkhpp::MemoryMapFlags{}, &mapped); |
| ASSERT_THAT(mapResult, IsVkSuccess()); |
| ASSERT_THAT(mapped, NotNull()); |
| memcpy(dst, mapped, dstSize); |
| device->unmapMemory(*readbackBufferMemory); |
| } |
| |
| } // namespace utils |
| } // namespace tests |
| } // namespace gfxstream |