blob: 1028d68dff436a5b54b36b59299d8ff502091971 [file] [log] [blame]
// Copyright (C) 2023 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 <string>
#include "GfxstreamEnd2EndTests.h"
#include "gfxstream/RutabagaLayerTestUtils.h"
namespace gfxstream {
namespace tests {
namespace {
using testing::Eq;
using testing::Ge;
using testing::IsEmpty;
using testing::IsNull;
using testing::Not;
using testing::NotNull;
class GfxstreamEnd2EndVkSnapshotImageTest : public GfxstreamEnd2EndTest {};
TEST_P(GfxstreamEnd2EndVkSnapshotImageTest, PreserveImageHandle) {
auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
VK_ASSERT(SetUpTypicalVkTestEnvironment());
const uint32_t width = 32;
const uint32_t height = 32;
const vkhpp::ImageCreateInfo imageCreateInfo = {
.pNext = nullptr,
.imageType = vkhpp::ImageType::e2D,
.extent.width = width,
.extent.height = height,
.extent.depth = 1,
.mipLevels = 1,
.arrayLayers = 1,
.format = vkhpp::Format::eR8G8B8A8Unorm,
.tiling = vkhpp::ImageTiling::eOptimal,
.initialLayout = vkhpp::ImageLayout::eUndefined,
.usage = vkhpp::ImageUsageFlagBits::eSampled | vkhpp::ImageUsageFlagBits::eTransferDst |
vkhpp::ImageUsageFlagBits::eTransferSrc,
.sharingMode = vkhpp::SharingMode::eExclusive,
.samples = vkhpp::SampleCountFlagBits::e1,
};
auto image = device->createImageUnique(imageCreateInfo).value;
vkhpp::MemoryRequirements imageMemoryRequirements{};
device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
const uint32_t imageMemoryIndex = GetMemoryType(physicalDevice, imageMemoryRequirements,
vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
.allocationSize = imageMemoryRequirements.size,
.memoryTypeIndex = imageMemoryIndex,
};
auto imageMemory = device->allocateMemoryUnique(imageMemoryAllocateInfo).value;
ASSERT_THAT(imageMemory, IsValidHandle());
SnapshotSaveAndLoad();
ASSERT_THAT(device->bindImageMemory(*image, *imageMemory, 0), IsVkSuccess());
}
TEST_P(GfxstreamEnd2EndVkSnapshotImageTest, ImageContent) {
static constexpr int kWidth = 256;
static constexpr int kHeight = 256;
static constexpr vkhpp::DeviceSize kSize = 4 * kWidth * kHeight;
std::vector<uint8_t> srcBufferContent(kSize);
for (size_t i = 0; i < kSize; i++) {
srcBufferContent[i] = static_cast<uint8_t>(i & 0xff);
}
auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
VK_ASSERT(SetUpTypicalVkTestEnvironment());
// Staging buffer
const vkhpp::BufferCreateInfo bufferCreateInfo = {
.size = static_cast<VkDeviceSize>(kSize),
.usage = vkhpp::BufferUsageFlagBits::eTransferSrc,
.sharingMode = vkhpp::SharingMode::eExclusive,
};
auto stagingBuffer = device->createBufferUnique(bufferCreateInfo).value;
ASSERT_THAT(stagingBuffer, IsValidHandle());
vkhpp::MemoryRequirements stagingBufferMemoryRequirements{};
device->getBufferMemoryRequirements(*stagingBuffer, &stagingBufferMemoryRequirements);
const auto stagingBufferMemoryType = GetMemoryType(
physicalDevice, stagingBufferMemoryRequirements,
vkhpp::MemoryPropertyFlagBits::eHostVisible | vkhpp::MemoryPropertyFlagBits::eHostCoherent);
// Staging memory
const vkhpp::MemoryAllocateInfo stagingBufferMemoryAllocateInfo = {
.allocationSize = stagingBufferMemoryRequirements.size,
.memoryTypeIndex = stagingBufferMemoryType,
};
auto stagingBufferMemory = device->allocateMemoryUnique(stagingBufferMemoryAllocateInfo).value;
ASSERT_THAT(stagingBufferMemory, IsValidHandle());
ASSERT_THAT(device->bindBufferMemory(*stagingBuffer, *stagingBufferMemory, 0), IsVkSuccess());
// Fill memory content
void* mapped = nullptr;
auto mapResult =
device->mapMemory(*stagingBufferMemory, 0, VK_WHOLE_SIZE, vkhpp::MemoryMapFlags{}, &mapped);
ASSERT_THAT(mapResult, IsVkSuccess());
ASSERT_THAT(mapped, NotNull());
auto* bytes = reinterpret_cast<uint8_t*>(mapped);
std::memcpy(bytes, srcBufferContent.data(), kSize);
const vkhpp::MappedMemoryRange range = {
.memory = *stagingBufferMemory,
.offset = 0,
.size = kSize,
};
device->unmapMemory(*stagingBufferMemory);
// Image
const vkhpp::ImageCreateInfo imageCreateInfo = {
.pNext = nullptr,
.imageType = vkhpp::ImageType::e2D,
.extent.width = kWidth,
.extent.height = kHeight,
.extent.depth = 1,
.mipLevels = 1,
.arrayLayers = 1,
.format = vkhpp::Format::eR8G8B8A8Unorm,
.tiling = vkhpp::ImageTiling::eOptimal,
.initialLayout = vkhpp::ImageLayout::eUndefined,
.usage = vkhpp::ImageUsageFlagBits::eTransferDst | vkhpp::ImageUsageFlagBits::eTransferSrc,
.sharingMode = vkhpp::SharingMode::eExclusive,
.samples = vkhpp::SampleCountFlagBits::e1,
};
auto image = device->createImageUnique(imageCreateInfo).value;
vkhpp::MemoryRequirements imageMemoryRequirements{};
device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
const uint32_t imageMemoryIndex = GetMemoryType(physicalDevice, imageMemoryRequirements,
vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
.allocationSize = imageMemoryRequirements.size,
.memoryTypeIndex = imageMemoryIndex,
};
auto imageMemory = device->allocateMemoryUnique(imageMemoryAllocateInfo).value;
ASSERT_THAT(imageMemory, IsValidHandle());
ASSERT_THAT(device->bindImageMemory(*image, *imageMemory, 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 commandBuffers = device->allocateCommandBuffersUnique(commandBufferAllocateInfo).value;
ASSERT_THAT(commandBuffers, Not(IsEmpty()));
auto commandBuffer = std::move(commandBuffers[0]);
ASSERT_THAT(commandBuffer, IsValidHandle());
const vkhpp::CommandBufferBeginInfo commandBufferBeginInfo = {
.flags = vkhpp::CommandBufferUsageFlagBits::eOneTimeSubmit,
};
commandBuffer->begin(commandBufferBeginInfo);
const vkhpp::ImageMemoryBarrier barrier{
.oldLayout = vkhpp::ImageLayout::eUndefined,
.newLayout = vkhpp::ImageLayout::eTransferDstOptimal,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *image,
.subresourceRange =
{
.aspectMask = vkhpp::ImageAspectFlagBits::eColor,
.levelCount = 1,
.layerCount = 1,
},
};
commandBuffer->pipelineBarrier(vkhpp::PipelineStageFlagBits::eAllCommands,
vkhpp::PipelineStageFlagBits::eAllCommands,
vkhpp::DependencyFlags(), nullptr, nullptr, barrier);
const vkhpp::BufferImageCopy bufferImageCopy = {
.imageSubresource =
{
.aspectMask = vkhpp::ImageAspectFlagBits::eColor,
.layerCount = 1,
},
.imageExtent =
{
.width = kWidth,
.height = kHeight,
.depth = 1,
},
};
commandBuffer->copyBufferToImage(*stagingBuffer, *image,
vkhpp::ImageLayout::eTransferDstOptimal, 1, &bufferImageCopy);
commandBuffer->end();
auto transferFence = device->createFenceUnique(vkhpp::FenceCreateInfo()).value;
ASSERT_THAT(transferFence, IsValidHandle());
// Execute the command to copy image
const vkhpp::SubmitInfo submitInfo = {
.commandBufferCount = 1,
.pCommandBuffers = &commandBuffer.get(),
};
queue.submit(submitInfo, *transferFence);
auto waitResult = device->waitForFences(*transferFence, VK_TRUE, 3000000000L);
ASSERT_THAT(waitResult, IsVkSuccess());
// Snapshot
SnapshotSaveAndLoad();
// Read-back buffer
const vkhpp::BufferCreateInfo readbackBufferCreateInfo = {
.size = static_cast<VkDeviceSize>(kSize),
.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());
auto readbackCommandBuffers =
device->allocateCommandBuffersUnique(commandBufferAllocateInfo).value;
ASSERT_THAT(readbackCommandBuffers, Not(IsEmpty()));
auto readbackCommandBuffer = std::move(readbackCommandBuffers[0]);
ASSERT_THAT(readbackCommandBuffer, IsValidHandle());
readbackCommandBuffer->begin(commandBufferBeginInfo);
const vkhpp::ImageMemoryBarrier readbackBarrier{
.oldLayout = vkhpp::ImageLayout::eTransferDstOptimal,
.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);
readbackCommandBuffer->copyImageToBuffer(*image, vkhpp::ImageLayout::eTransferSrcOptimal,
*readbackBuffer, 1, &bufferImageCopy);
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
mapResult = device->mapMemory(*readbackBufferMemory, 0, VK_WHOLE_SIZE, vkhpp::MemoryMapFlags{},
&mapped);
ASSERT_THAT(mapResult, IsVkSuccess());
ASSERT_THAT(mapped, NotNull());
bytes = reinterpret_cast<uint8_t*>(mapped);
for (uint32_t i = 0; i < kSize; ++i) {
ASSERT_THAT(bytes[i], Eq(srcBufferContent[i]));
}
device->unmapMemory(*readbackBufferMemory);
}
INSTANTIATE_TEST_CASE_P(GfxstreamEnd2EndTests, GfxstreamEnd2EndVkSnapshotImageTest,
::testing::ValuesIn({
TestParams{
.with_gl = false,
.with_vk = true,
.with_vk_snapshot = true,
},
}),
&GetTestName);
} // namespace
} // namespace tests
} // namespace gfxstream