Merge "Snapshot descriptor set contents" into main
diff --git a/common/end2end/GfxstreamEnd2EndVkSnapshotPipelineTests.cpp b/common/end2end/GfxstreamEnd2EndVkSnapshotPipelineTests.cpp
index b9cc69f..09943bf 100644
--- a/common/end2end/GfxstreamEnd2EndVkSnapshotPipelineTests.cpp
+++ b/common/end2end/GfxstreamEnd2EndVkSnapshotPipelineTests.cpp
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <android-base/expected.h>
+
#include <string>
#include "GfxstreamEnd2EndTestUtils.h"
@@ -46,12 +48,21 @@
vkhpp::UniqueImageView imageView;
};
+struct BufferInfo {
+ vkhpp::UniqueBuffer buffer;
+ vkhpp::UniqueDeviceMemory memory;
+};
+
class GfxstreamEnd2EndVkSnapshotPipelineTest : public GfxstreamEnd2EndTest {
protected:
vkhpp::UniqueRenderPass createRenderPass(vkhpp::Device device);
std::unique_ptr<ImageInfo> createColorAttachment(vkhpp::PhysicalDevice physicalDevice,
vkhpp::Device device);
std::unique_ptr<PipelineInfo> createPipeline(vkhpp::Device device);
+ VkExpected<BufferInfo> createAndPopulateBuffer(vkhpp::PhysicalDevice physicalDevice,
+ vkhpp::Device device,
+ vkhpp::BufferUsageFlags usage, const void* data,
+ uint64_t dataSize);
static const uint32_t kFbWidth = 32;
static const uint32_t kFbHeight = 32;
};
@@ -65,12 +76,56 @@
std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count());
}
-// A blue triangle
-const float kVertexData[] = {
- -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
- 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
+// Full screen blue rectangle
+const float kFullscreenBlueRectangleVertexData[] = {
+ // clang-format off
+ /*pos=*/ -1.0f, -1.0f, 0.0f, 1.0f, /*color=*/ 0.0f, 0.0f, 1.0f, 1.0f,
+ /*pos=*/ 1.0f, -1.0f, 0.0f, 1.0f, /*color=*/ 0.0f, 0.0f, 1.0f, 1.0f,
+ /*pos=*/ 1.0f, 1.0f, 0.0f, 1.0f, /*color=*/ 0.0f, 0.0f, 1.0f, 1.0f,
+ /*pos=*/ 1.0f, 1.0f, 0.0f, 1.0f, /*color=*/ 0.0f, 0.0f, 1.0f, 1.0f,
+ /*pos=*/ -1.0f, 1.0f, 0.0f, 1.0f, /*color=*/ 0.0f, 0.0f, 1.0f, 1.0f,
+ /*pos=*/ -1.0f, -1.0f, 0.0f, 1.0f, /*color=*/ 0.0f, 0.0f, 1.0f, 1.0f,
+ // clang-format on
};
+VkExpected<BufferInfo> GfxstreamEnd2EndVkSnapshotPipelineTest::createAndPopulateBuffer(
+ vkhpp::PhysicalDevice physicalDevice, vkhpp::Device device, vkhpp::BufferUsageFlags usage,
+ const void* data, uint64_t dataSize) {
+ const vkhpp::BufferCreateInfo vertexBufferCreateInfo = {
+ .size = dataSize,
+ .usage = usage,
+ .sharingMode = vkhpp::SharingMode::eExclusive,
+ };
+ auto vertexBuffer = VK_EXPECT_RV(device.createBufferUnique(vertexBufferCreateInfo));
+
+ vkhpp::MemoryRequirements vertexBufferMemoryRequirements{};
+ device.getBufferMemoryRequirements(*vertexBuffer, &vertexBufferMemoryRequirements);
+
+ const auto vertexBufferMemoryType = utils::getMemoryType(
+ physicalDevice, vertexBufferMemoryRequirements,
+ vkhpp::MemoryPropertyFlagBits::eHostVisible | vkhpp::MemoryPropertyFlagBits::eHostCoherent);
+ if (vertexBufferMemoryType == -1) {
+ return android::base::unexpected(vkhpp::Result::eErrorOutOfHostMemory);
+ }
+ // Vertex memory
+ const vkhpp::MemoryAllocateInfo vertexBufferMemoryAllocateInfo = {
+ .allocationSize = vertexBufferMemoryRequirements.size,
+ .memoryTypeIndex = vertexBufferMemoryType,
+ };
+ auto vertexBufferMemory =
+ VK_EXPECT_RV(device.allocateMemoryUnique(vertexBufferMemoryAllocateInfo));
+ device.bindBufferMemory(*vertexBuffer, *vertexBufferMemory, 0);
+ void* mapped;
+ device.mapMemory(*vertexBufferMemory, 0, VK_WHOLE_SIZE, vkhpp::MemoryMapFlags{}, &mapped);
+ memcpy(mapped, data, dataSize);
+ device.unmapMemory(*vertexBufferMemory);
+
+ BufferInfo res;
+ res.buffer = std::move(vertexBuffer);
+ res.memory = std::move(vertexBufferMemory);
+ return res;
+}
+
vkhpp::UniqueRenderPass GfxstreamEnd2EndVkSnapshotPipelineTest::createRenderPass(
vkhpp::Device device) {
vkhpp::AttachmentDescription colorAttachmentDescription = {
@@ -103,11 +158,26 @@
std::unique_ptr<PipelineInfo> res(new PipelineInfo);
res->renderPass = createRenderPass(device);
- vkhpp::DescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = {};
+ vkhpp::DescriptorSetLayoutBinding bindings[1] = {
+ {
+ .binding = 0,
+ .descriptorType = vkhpp::DescriptorType::eUniformBuffer,
+ .descriptorCount = 1,
+ .stageFlags = vkhpp::ShaderStageFlagBits::eFragment,
+ },
+ };
+ vkhpp::DescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = {
+ .bindingCount = 1,
+ .pBindings = bindings,
+ };
res->descriptorSetLayout =
device.createDescriptorSetLayoutUnique(descriptorSetLayoutInfo).value;
- res->pipelineLayout =
- device.createPipelineLayoutUnique(vkhpp::PipelineLayoutCreateInfo{}).value;
+ res->pipelineLayout = device
+ .createPipelineLayoutUnique(vkhpp::PipelineLayoutCreateInfo{
+ .setLayoutCount = 1,
+ .pSetLayouts = &res->descriptorSetLayout.get(),
+ })
+ .value;
vkhpp::ShaderModuleCreateInfo vertexShaderModuleCreateInfo = {
.codeSize = sizeof(kSimpleShaderVert),
@@ -361,11 +431,7 @@
TEST_P(GfxstreamEnd2EndVkSnapshotPipelineWithMultiSamplingTest, CanSubmitQueue) {
TypicalVkTestEnvironment testEnvironment = VK_ASSERT(SetUpTypicalVkTestEnvironment());
- auto& instance = testEnvironment.instance;
- auto& physicalDevice = testEnvironment.physicalDevice;
- auto& device = testEnvironment.device;
- auto& queue = testEnvironment.queue;
- auto queueFamilyIndex = testEnvironment.queueFamilyIndex;
+ auto& [instance, physicalDevice, device, queue, queueFamilyIndex] = testEnvironment;
auto pipelineInfo = createPipeline(device.get());
@@ -489,6 +555,10 @@
waitResult = device->waitForFences(*fence, VK_TRUE, 3000000000L);
ASSERT_THAT(waitResult, IsVkSuccess());
+ if (GetParam().samples != 1) {
+ return;
+ }
+
std::vector<uint32_t> dst(kFbWidth * kFbHeight);
utils::readImageData(*colorAttachmentInfo->image, kFbWidth, kFbHeight,
vkhpp::ImageLayout::eColorAttachmentOptimal, dst.data(),
@@ -498,6 +568,395 @@
}
}
+TEST_P(GfxstreamEnd2EndVkSnapshotPipelineTest, CanSnapshotDescriptors) {
+ TypicalVkTestEnvironment testEnvironment = VK_ASSERT(SetUpTypicalVkTestEnvironment());
+ auto& [instance, physicalDevice, device, queue, queueFamilyIndex] = testEnvironment;
+
+ auto pipelineInfo = createPipeline(device.get());
+ auto vertexBufferInfo = createAndPopulateBuffer(
+ physicalDevice, device.get(), vkhpp::BufferUsageFlagBits::eVertexBuffer,
+ kFullscreenBlueRectangleVertexData, sizeof(kFullscreenBlueRectangleVertexData));
+
+ auto colorAttachmentInfo = createColorAttachment(physicalDevice, device.get());
+ ASSERT_THAT(colorAttachmentInfo->image, IsValidHandle());
+ ASSERT_THAT(colorAttachmentInfo->memory, IsValidHandle());
+ ASSERT_THAT(colorAttachmentInfo->imageView, IsValidHandle());
+
+ // Descriptor
+ std::vector<vkhpp::DescriptorPoolSize> sizes = {
+ {
+ .type = vkhpp::DescriptorType::eUniformBuffer,
+ .descriptorCount = 10,
+ },
+ };
+ vkhpp::DescriptorPoolCreateInfo descriptorPoolCreateInfo = {
+ .maxSets = 10,
+ .poolSizeCount = static_cast<uint32_t>(sizes.size()),
+ .pPoolSizes = sizes.data(),
+ };
+ auto descriptorPool = device->createDescriptorPoolUnique(descriptorPoolCreateInfo).value;
+ ASSERT_THAT(descriptorPool, IsValidHandle());
+
+ const std::vector<vkhpp::DescriptorSetLayout> descriptorSetLayouts(
+ 1, *pipelineInfo->descriptorSetLayout);
+
+ vkhpp::DescriptorSetAllocateInfo descriptorSetAllocateInfo = {
+ .descriptorPool = *descriptorPool,
+ .descriptorSetCount = 1,
+ .pSetLayouts = descriptorSetLayouts.data(),
+ };
+ auto descriptorSets = device->allocateDescriptorSetsUnique(descriptorSetAllocateInfo);
+ EXPECT_THAT(descriptorSets.result, Eq(vkhpp::Result::eSuccess));
+ auto descriptorSet = *descriptorSets.value[0];
+
+ // A uniform for red color
+ float kColor1[] = {1.0f, 0.0f, 0.0f, 0.0f};
+ auto uniformBufferInfo = createAndPopulateBuffer(physicalDevice, device.get(),
+ vkhpp::BufferUsageFlagBits::eUniformBuffer,
+ kColor1, sizeof(kColor1));
+
+ std::vector<vkhpp::WriteDescriptorSet> writeDescriptorSets;
+ std::vector<vkhpp::DescriptorBufferInfo> bufferInfos;
+ bufferInfos.emplace_back(vkhpp::DescriptorBufferInfo{
+ .buffer = *uniformBufferInfo->buffer,
+ .offset = 0,
+ .range = VK_WHOLE_SIZE,
+ });
+ writeDescriptorSets.emplace_back(vkhpp::WriteDescriptorSet{
+ .dstSet = descriptorSet,
+ .dstBinding = 0,
+ .descriptorCount = 1,
+ .descriptorType = vkhpp::DescriptorType::eUniformBuffer,
+ .pBufferInfo = bufferInfos.data(),
+ });
+ device->updateDescriptorSets(writeDescriptorSets, nullptr);
+
+ const std::vector<vkhpp::ImageView> attachments(1, *colorAttachmentInfo->imageView);
+ vkhpp::FramebufferCreateInfo framebufferCreateInfo = {
+ .renderPass = *pipelineInfo->renderPass,
+ .attachmentCount = 1,
+ .pAttachments = attachments.data(),
+ .width = kFbWidth,
+ .height = kFbHeight,
+ .layers = 1,
+ };
+ auto framebuffer = device->createFramebufferUnique(framebufferCreateInfo).value;
+ ASSERT_THAT(framebuffer, IsValidHandle());
+
+ auto fence = device->createFenceUnique(vkhpp::FenceCreateInfo()).value;
+ ASSERT_THAT(fence, IsValidHandle());
+
+ const vkhpp::CommandPoolCreateInfo commandPoolCreateInfo = {
+ .flags = vkhpp::CommandPoolCreateFlagBits::eResetCommandBuffer,
+ .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());
+
+ vkhpp::ClearColorValue clearColor(std::array<float, 4>{0.0f, 0.0f, 0.0f, 1.0f});
+ vkhpp::ClearValue clearValue{
+ .color = clearColor,
+ };
+ vkhpp::RenderPassBeginInfo renderPassBeginInfo{
+ .renderPass = *pipelineInfo->renderPass,
+ .framebuffer = *framebuffer,
+ .renderArea = vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)),
+ .clearValueCount = 1,
+ .pClearValues = &clearValue,
+ };
+
+ // Descriptor updates are cached on the guest, for testing purpose we need to submit a queue to
+ // commit descriptor updates.
+
+ const vkhpp::CommandBufferBeginInfo commandBufferBeginInfo = {
+ .flags = vkhpp::CommandBufferUsageFlagBits::eOneTimeSubmit,
+ };
+
+ commandBuffer->begin(commandBufferBeginInfo);
+ const vkhpp::ImageMemoryBarrier colorAttachmentBarrier{
+ .oldLayout = vkhpp::ImageLayout::eUndefined,
+ .newLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
+ .dstAccessMask = vkhpp::AccessFlagBits::eColorAttachmentRead |
+ vkhpp::AccessFlagBits::eColorAttachmentWrite,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = *colorAttachmentInfo->image,
+ .subresourceRange =
+ {
+ .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
+ .levelCount = 1,
+ .layerCount = 1,
+ },
+ };
+
+ commandBuffer->pipelineBarrier(
+ vkhpp::PipelineStageFlagBits::eTopOfPipe | vkhpp::PipelineStageFlagBits::eTransfer,
+ vkhpp::PipelineStageFlagBits::eColorAttachmentOutput, vkhpp::DependencyFlags(), nullptr,
+ nullptr, colorAttachmentBarrier);
+
+ commandBuffer->beginRenderPass(renderPassBeginInfo, vkhpp::SubpassContents::eInline);
+ commandBuffer->bindPipeline(vkhpp::PipelineBindPoint::eGraphics, *pipelineInfo->pipeline);
+ commandBuffer->bindDescriptorSets(vkhpp::PipelineBindPoint::eGraphics,
+ *pipelineInfo->pipelineLayout, 0, descriptorSet, nullptr);
+ commandBuffer->bindVertexBuffers(0, {*vertexBufferInfo->buffer}, {0});
+ commandBuffer->setViewport(0, vkhpp::Viewport(0.0f, 0.0f, static_cast<float>(kFbWidth),
+ static_cast<float>(kFbHeight), 0.0f, 1.0f));
+ commandBuffer->setScissor(
+ 0, vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)));
+ commandBuffer->draw(6, 1, 0, 0);
+ commandBuffer->endRenderPass();
+ commandBuffer->end();
+
+ std::vector<vkhpp::CommandBuffer> commandBufferHandles;
+ commandBufferHandles.push_back(*commandBuffer);
+
+ const vkhpp::SubmitInfo submitInfo = {
+ .commandBufferCount = static_cast<uint32_t>(commandBufferHandles.size()),
+ .pCommandBuffers = commandBufferHandles.data(),
+ };
+ queue.submit(submitInfo, *fence);
+ auto waitResult = device->waitForFences(*fence, VK_TRUE, 3000000000L);
+ ASSERT_THAT(waitResult, IsVkSuccess());
+ commandBuffer->reset();
+
+ // Clear the rendering
+ commandBuffer->begin(commandBufferBeginInfo);
+ commandBuffer->beginRenderPass(renderPassBeginInfo, vkhpp::SubpassContents::eInline);
+ commandBuffer->bindPipeline(vkhpp::PipelineBindPoint::eGraphics, *pipelineInfo->pipeline);
+ vkhpp::ClearAttachment clearAttachment{
+ .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
+ .colorAttachment = 0,
+ .clearValue = clearValue,
+ };
+ vkhpp::ClearRect clearRect{
+ .rect = vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)),
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ };
+ commandBuffer->clearAttachments(1, &clearAttachment, 1, &clearRect);
+ commandBuffer->endRenderPass();
+ commandBuffer->end();
+
+ device->resetFences(1, &fence.get());
+ queue.submit(submitInfo, *fence);
+ waitResult = device->waitForFences(*fence, VK_TRUE, 3000000000L);
+ ASSERT_THAT(waitResult, IsVkSuccess());
+ commandBuffer->reset();
+
+ SnapshotSaveAndLoad();
+
+ // Redraw after snapshot, verify descriptors keep their value
+ // Command buffer snapshot is not implemented yet, so we need to re-make the command buffer.
+ commandBuffer->begin(commandBufferBeginInfo);
+ commandBuffer->beginRenderPass(renderPassBeginInfo, vkhpp::SubpassContents::eInline);
+ commandBuffer->bindPipeline(vkhpp::PipelineBindPoint::eGraphics, *pipelineInfo->pipeline);
+ commandBuffer->bindDescriptorSets(vkhpp::PipelineBindPoint::eGraphics,
+ *pipelineInfo->pipelineLayout, 0, descriptorSet, nullptr);
+ commandBuffer->bindVertexBuffers(0, {*vertexBufferInfo->buffer}, {0});
+ commandBuffer->setViewport(0, vkhpp::Viewport(0.0f, 0.0f, static_cast<float>(kFbWidth),
+ static_cast<float>(kFbHeight), 0.0f, 1.0f));
+ commandBuffer->setScissor(
+ 0, vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)));
+ commandBuffer->draw(6, 1, 0, 0);
+ commandBuffer->endRenderPass();
+ commandBuffer->end();
+
+ device->resetFences(1, &fence.get());
+ queue.submit(submitInfo, *fence);
+ waitResult = device->waitForFences(*fence, VK_TRUE, 3000000000L);
+ ASSERT_THAT(waitResult, IsVkSuccess());
+
+ std::vector<uint32_t> dst(kFbWidth * kFbHeight);
+ utils::readImageData(*colorAttachmentInfo->image, kFbWidth, kFbHeight,
+ vkhpp::ImageLayout::eColorAttachmentOptimal, dst.data(),
+ dst.size() * sizeof(uint32_t), testEnvironment);
+ for (int i = 0; i < dst.size(); i++) {
+ // The shader adds a blue color (from vertex buffer) with a red color (from uniform) and get
+ // purple.
+ ASSERT_THAT(dst[i], Eq(0xffff00ff));
+ }
+}
+
+TEST_P(GfxstreamEnd2EndVkSnapshotPipelineTest, DeleteBufferAfterWriteDescriptor) {
+ TypicalVkTestEnvironment testEnvironment = VK_ASSERT(SetUpTypicalVkTestEnvironment());
+ auto& [instance, physicalDevice, device, queue, queueFamilyIndex] = testEnvironment;
+
+ auto pipelineInfo = createPipeline(device.get());
+ auto vertexBufferInfo = createAndPopulateBuffer(
+ physicalDevice, device.get(), vkhpp::BufferUsageFlagBits::eVertexBuffer,
+ kFullscreenBlueRectangleVertexData, sizeof(kFullscreenBlueRectangleVertexData));
+
+ auto colorAttachmentInfo = createColorAttachment(physicalDevice, device.get());
+ ASSERT_THAT(colorAttachmentInfo->image, IsValidHandle());
+ ASSERT_THAT(colorAttachmentInfo->memory, IsValidHandle());
+ ASSERT_THAT(colorAttachmentInfo->imageView, IsValidHandle());
+
+ // Descriptor
+ std::vector<vkhpp::DescriptorPoolSize> sizes = {
+ {
+ .type = vkhpp::DescriptorType::eUniformBuffer,
+ .descriptorCount = 10,
+ },
+ };
+ vkhpp::DescriptorPoolCreateInfo descriptorPoolCreateInfo = {
+ .maxSets = 10,
+ .poolSizeCount = static_cast<uint32_t>(sizes.size()),
+ .pPoolSizes = sizes.data(),
+ };
+ auto descriptorPool = device->createDescriptorPoolUnique(descriptorPoolCreateInfo).value;
+ ASSERT_THAT(descriptorPool, IsValidHandle());
+
+ const std::vector<vkhpp::DescriptorSetLayout> descriptorSetLayouts(
+ 1, *pipelineInfo->descriptorSetLayout);
+
+ vkhpp::DescriptorSetAllocateInfo descriptorSetAllocateInfo = {
+ .descriptorPool = *descriptorPool,
+ .descriptorSetCount = 1,
+ .pSetLayouts = descriptorSetLayouts.data(),
+ };
+ auto descriptorSets = device->allocateDescriptorSetsUnique(descriptorSetAllocateInfo);
+ EXPECT_THAT(descriptorSets.result, Eq(vkhpp::Result::eSuccess));
+ auto descriptorSet = *descriptorSets.value[0];
+
+ // A uniform for red color
+ float kColor1[] = {1.0f, 0.0f, 0.0f, 0.0f};
+ auto uniformBufferInfo = createAndPopulateBuffer(physicalDevice, device.get(),
+ vkhpp::BufferUsageFlagBits::eUniformBuffer,
+ kColor1, sizeof(kColor1));
+
+ std::vector<vkhpp::WriteDescriptorSet> writeDescriptorSets;
+ std::vector<vkhpp::DescriptorBufferInfo> bufferInfos;
+ bufferInfos.emplace_back(vkhpp::DescriptorBufferInfo{
+ .buffer = *uniformBufferInfo->buffer,
+ .offset = 0,
+ .range = VK_WHOLE_SIZE,
+ });
+ writeDescriptorSets.emplace_back(vkhpp::WriteDescriptorSet{
+ .dstSet = descriptorSet,
+ .dstBinding = 0,
+ .descriptorCount = 1,
+ .descriptorType = vkhpp::DescriptorType::eUniformBuffer,
+ .pBufferInfo = bufferInfos.data(),
+ });
+ device->updateDescriptorSets(writeDescriptorSets, nullptr);
+
+ const std::vector<vkhpp::ImageView> attachments(1, *colorAttachmentInfo->imageView);
+ vkhpp::FramebufferCreateInfo framebufferCreateInfo = {
+ .renderPass = *pipelineInfo->renderPass,
+ .attachmentCount = 1,
+ .pAttachments = attachments.data(),
+ .width = kFbWidth,
+ .height = kFbHeight,
+ .layers = 1,
+ };
+ auto framebuffer = device->createFramebufferUnique(framebufferCreateInfo).value;
+ ASSERT_THAT(framebuffer, IsValidHandle());
+
+ auto fence = device->createFenceUnique(vkhpp::FenceCreateInfo()).value;
+ ASSERT_THAT(fence, IsValidHandle());
+
+ const vkhpp::CommandPoolCreateInfo commandPoolCreateInfo = {
+ .flags = vkhpp::CommandPoolCreateFlagBits::eResetCommandBuffer,
+ .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());
+
+ vkhpp::ClearColorValue clearColor(std::array<float, 4>{0.0f, 0.0f, 0.0f, 1.0f});
+ vkhpp::ClearValue clearValue{
+ .color = clearColor,
+ };
+ vkhpp::RenderPassBeginInfo renderPassBeginInfo{
+ .renderPass = *pipelineInfo->renderPass,
+ .framebuffer = *framebuffer,
+ .renderArea = vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)),
+ .clearValueCount = 1,
+ .pClearValues = &clearValue,
+ };
+
+ // Descriptor updates are cached on the guest, for testing purpose we need to submit a queue to
+ // commit descriptor updates.
+
+ const vkhpp::CommandBufferBeginInfo commandBufferBeginInfo = {
+ .flags = vkhpp::CommandBufferUsageFlagBits::eOneTimeSubmit,
+ };
+
+ commandBuffer->begin(commandBufferBeginInfo);
+ const vkhpp::ImageMemoryBarrier colorAttachmentBarrier{
+ .oldLayout = vkhpp::ImageLayout::eUndefined,
+ .newLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
+ .dstAccessMask = vkhpp::AccessFlagBits::eColorAttachmentRead |
+ vkhpp::AccessFlagBits::eColorAttachmentWrite,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = *colorAttachmentInfo->image,
+ .subresourceRange =
+ {
+ .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
+ .levelCount = 1,
+ .layerCount = 1,
+ },
+ };
+
+ commandBuffer->pipelineBarrier(
+ vkhpp::PipelineStageFlagBits::eTopOfPipe | vkhpp::PipelineStageFlagBits::eTransfer,
+ vkhpp::PipelineStageFlagBits::eColorAttachmentOutput, vkhpp::DependencyFlags(), nullptr,
+ nullptr, colorAttachmentBarrier);
+
+ commandBuffer->beginRenderPass(renderPassBeginInfo, vkhpp::SubpassContents::eInline);
+ commandBuffer->bindPipeline(vkhpp::PipelineBindPoint::eGraphics, *pipelineInfo->pipeline);
+ commandBuffer->bindDescriptorSets(vkhpp::PipelineBindPoint::eGraphics,
+ *pipelineInfo->pipelineLayout, 0, descriptorSet, nullptr);
+ commandBuffer->bindVertexBuffers(0, {*vertexBufferInfo->buffer}, {0});
+ commandBuffer->setViewport(0, vkhpp::Viewport(0.0f, 0.0f, static_cast<float>(kFbWidth),
+ static_cast<float>(kFbHeight), 0.0f, 1.0f));
+ commandBuffer->setScissor(
+ 0, vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)));
+ commandBuffer->draw(6, 1, 0, 0);
+ commandBuffer->endRenderPass();
+ commandBuffer->end();
+
+ std::vector<vkhpp::CommandBuffer> commandBufferHandles;
+ commandBufferHandles.push_back(*commandBuffer);
+
+ const vkhpp::SubmitInfo submitInfo = {
+ .commandBufferCount = static_cast<uint32_t>(commandBufferHandles.size()),
+ .pCommandBuffers = commandBufferHandles.data(),
+ };
+ queue.submit(submitInfo, *fence);
+ auto waitResult = device->waitForFences(*fence, VK_TRUE, 3000000000L);
+ ASSERT_THAT(waitResult, IsVkSuccess());
+ commandBuffer->reset();
+
+ vertexBufferInfo->buffer.reset();
+ // Descriptor snapshot should not crash after underlying buffer is deleted.
+ SnapshotSaveAndLoad();
+}
+
INSTANTIATE_TEST_CASE_P(GfxstreamEnd2EndTests, GfxstreamEnd2EndVkSnapshotPipelineTest,
::testing::ValuesIn({
TestParams{
diff --git a/common/end2end/simple_shader.frag b/common/end2end/simple_shader.frag
index b8b6f56..a3e42b1 100644
--- a/common/end2end/simple_shader.frag
+++ b/common/end2end/simple_shader.frag
@@ -14,11 +14,15 @@
#version 450
+layout (binding = 0) uniform UniformBuffer {
+ vec4 color1;
+};
+
layout (location = 0) in vec4 color;
layout (location = 0) out vec4 outColor;
void main()
{
- outColor = color;
+ outColor = color + color1;
}
\ No newline at end of file
diff --git a/common/end2end/simple_shader_frag.h b/common/end2end/simple_shader_frag.h
index cbb84f0..974d127 100644
--- a/common/end2end/simple_shader_frag.h
+++ b/common/end2end/simple_shader_frag.h
@@ -20,17 +20,26 @@
#include <stdint.h>
const uint32_t kSimpleShaderFrag[] = {
- 0x07230203, 0x00010000, 0x000d000b, 0x0000000d, 0x00000000, 0x00020011, 0x00000001, 0x0006000b,
+ 0x07230203, 0x00010000, 0x000d000b, 0x00000016, 0x00000000, 0x00020011, 0x00000001, 0x0006000b,
0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, 0x00000000, 0x0003000e, 0x00000000, 0x00000001,
0x0007000f, 0x00000004, 0x00000004, 0x6e69616d, 0x00000000, 0x00000009, 0x0000000b, 0x00030010,
0x00000004, 0x00000007, 0x00030003, 0x00000002, 0x000001c2, 0x000a0004, 0x475f4c47, 0x4c474f4f,
0x70635f45, 0x74735f70, 0x5f656c79, 0x656e696c, 0x7269645f, 0x69746365, 0x00006576, 0x00080004,
0x475f4c47, 0x4c474f4f, 0x6e695f45, 0x64756c63, 0x69645f65, 0x74636572, 0x00657669, 0x00040005,
0x00000004, 0x6e69616d, 0x00000000, 0x00050005, 0x00000009, 0x4374756f, 0x726f6c6f, 0x00000000,
- 0x00040005, 0x0000000b, 0x6f6c6f63, 0x00000072, 0x00040047, 0x00000009, 0x0000001e, 0x00000000,
- 0x00040047, 0x0000000b, 0x0000001e, 0x00000000, 0x00020013, 0x00000002, 0x00030021, 0x00000003,
- 0x00000002, 0x00030016, 0x00000006, 0x00000020, 0x00040017, 0x00000007, 0x00000006, 0x00000004,
- 0x00040020, 0x00000008, 0x00000003, 0x00000007, 0x0004003b, 0x00000008, 0x00000009, 0x00000003,
- 0x00040020, 0x0000000a, 0x00000001, 0x00000007, 0x0004003b, 0x0000000a, 0x0000000b, 0x00000001,
- 0x00050036, 0x00000002, 0x00000004, 0x00000000, 0x00000003, 0x000200f8, 0x00000005, 0x0004003d,
- 0x00000007, 0x0000000c, 0x0000000b, 0x0003003e, 0x00000009, 0x0000000c, 0x000100fd, 0x00010038};
\ No newline at end of file
+ 0x00040005, 0x0000000b, 0x6f6c6f63, 0x00000072, 0x00060005, 0x0000000d, 0x66696e55, 0x426d726f,
+ 0x65666675, 0x00000072, 0x00050006, 0x0000000d, 0x00000000, 0x6f6c6f63, 0x00003172, 0x00030005,
+ 0x0000000f, 0x00000000, 0x00040047, 0x00000009, 0x0000001e, 0x00000000, 0x00040047, 0x0000000b,
+ 0x0000001e, 0x00000000, 0x00050048, 0x0000000d, 0x00000000, 0x00000023, 0x00000000, 0x00030047,
+ 0x0000000d, 0x00000002, 0x00040047, 0x0000000f, 0x00000022, 0x00000000, 0x00040047, 0x0000000f,
+ 0x00000021, 0x00000000, 0x00020013, 0x00000002, 0x00030021, 0x00000003, 0x00000002, 0x00030016,
+ 0x00000006, 0x00000020, 0x00040017, 0x00000007, 0x00000006, 0x00000004, 0x00040020, 0x00000008,
+ 0x00000003, 0x00000007, 0x0004003b, 0x00000008, 0x00000009, 0x00000003, 0x00040020, 0x0000000a,
+ 0x00000001, 0x00000007, 0x0004003b, 0x0000000a, 0x0000000b, 0x00000001, 0x0003001e, 0x0000000d,
+ 0x00000007, 0x00040020, 0x0000000e, 0x00000002, 0x0000000d, 0x0004003b, 0x0000000e, 0x0000000f,
+ 0x00000002, 0x00040015, 0x00000010, 0x00000020, 0x00000001, 0x0004002b, 0x00000010, 0x00000011,
+ 0x00000000, 0x00040020, 0x00000012, 0x00000002, 0x00000007, 0x00050036, 0x00000002, 0x00000004,
+ 0x00000000, 0x00000003, 0x000200f8, 0x00000005, 0x0004003d, 0x00000007, 0x0000000c, 0x0000000b,
+ 0x00050041, 0x00000012, 0x00000013, 0x0000000f, 0x00000011, 0x0004003d, 0x00000007, 0x00000014,
+ 0x00000013, 0x00050081, 0x00000007, 0x00000015, 0x0000000c, 0x00000014, 0x0003003e, 0x00000009,
+ 0x00000015, 0x000100fd, 0x00010038};
\ No newline at end of file
diff --git a/host/vulkan/VkDecoderGlobalState.cpp b/host/vulkan/VkDecoderGlobalState.cpp
index bf18f23..785b1f4 100644
--- a/host/vulkan/VkDecoderGlobalState.cpp
+++ b/host/vulkan/VkDecoderGlobalState.cpp
@@ -528,6 +528,141 @@
releaseSnapshotStateBlock(&stateBlock);
}
+ // snapshot descriptors
+ std::vector<VkDescriptorPool> sortedBoxedDescriptorPools;
+ for (const auto& descriptorPoolIte : mDescriptorPoolInfo) {
+ auto boxed =
+ unboxed_to_boxed_non_dispatchable_VkDescriptorPool(descriptorPoolIte.first);
+ sortedBoxedDescriptorPools.push_back(boxed);
+ }
+ std::sort(sortedBoxedDescriptorPools.begin(), sortedBoxedDescriptorPools.end());
+ for (const auto& boxedDescriptorPool : sortedBoxedDescriptorPools) {
+ auto unboxedDescriptorPool = unbox_VkDescriptorPool(boxedDescriptorPool);
+ const DescriptorPoolInfo& poolInfo = mDescriptorPoolInfo[unboxedDescriptorPool];
+
+ for (uint64_t poolId : poolInfo.poolIds) {
+ DispatchableHandleInfo<uint64_t>* setHandleInfo = sBoxedHandleManager.get(poolId);
+ bool allocated = setHandleInfo->underlying != 0;
+ stream->putByte(allocated);
+ if (!allocated) {
+ continue;
+ }
+
+ const DescriptorSetInfo& descriptorSetInfo =
+ mDescriptorSetInfo[(VkDescriptorSet)setHandleInfo->underlying];
+ VkDescriptorSetLayout boxedLayout =
+ unboxed_to_boxed_non_dispatchable_VkDescriptorSetLayout(
+ descriptorSetInfo.unboxedLayout);
+ stream->putBe64((uint64_t)boxedLayout);
+ // Count all valid descriptors.
+ //
+ // There is a use case where user can create an image, write it to a descriptor,
+ // read/write the image by committing a command, then delete the image without
+ // unbinding the descriptor. For example:
+ //
+ // T1: create "vkimage1" (original)
+ // T2: update binding1 of vkdescriptorset1 with vkimage1
+ // T3: draw
+ // T4: delete "vkimage1" (original)
+ // T5: create "vkimage1" (recycled)
+ // T6: snapshot load
+ //
+ // At the point of the snapshot, the original vk image has been invalidated,
+ // thus we cannot call vkUpdateDescriptorSets for it, and need to remove it
+ // from the snapshot.
+ //
+ // The current implementation bases on smart pointers. A descriptor set info
+ // holds weak pointers to their underlying resources (image, image view, buffer).
+ // On snapshot load, we check if any of the smart pointers are invalidated.
+ //
+ // An alternative approach has been discussed by, instead of using smart
+ // pointers, checking valid handles on snapshot save. This approach has the
+ // advantage that it reduces number of smart pointer allocations. After discussion
+ // we concluded that there is at least one corner case that will break the
+ // alternative approach. That is when the user deletes a bound vkimage and creates
+ // a new vkimage. The driver is free to reuse released handles, thus we might
+ // end up having a new vkimage with the same handle as the old one (see T5 in the
+ // example), and think the binding is still valid. And if we bind the new image
+ // regardless, we might hit a Vulkan validation error because the new image might
+ // have the "usage" flag that is unsuitable to bind to descriptors.
+ std::vector<std::pair<int, int>> validWriteIndices;
+ for (int bindingIdx = 0; bindingIdx < descriptorSetInfo.allWrites.size();
+ bindingIdx++) {
+ for (int bindingElemIdx = 0;
+ bindingElemIdx < descriptorSetInfo.allWrites[bindingIdx].size();
+ bindingElemIdx++) {
+ const auto& entry = descriptorSetInfo.allWrites[bindingIdx][bindingElemIdx];
+ if (entry.writeType == DescriptorSetInfo::DescriptorWriteType::Empty) {
+ continue;
+ }
+ int dependencyObjCount =
+ descriptorDependencyObjectCount(entry.descriptorType);
+ if (entry.alives.size() < dependencyObjCount) {
+ continue;
+ }
+ bool isValid = true;
+ for (const auto& alive : entry.alives) {
+ isValid &= !alive.expired();
+ if (!isValid) {
+ break;
+ }
+ }
+ if (!isValid) {
+ continue;
+ }
+ validWriteIndices.push_back(std::make_pair(bindingIdx, bindingElemIdx));
+ }
+ }
+ stream->putBe64(validWriteIndices.size());
+ // Save all valid descriptors
+ for (const auto& idx : validWriteIndices) {
+ const auto& entry = descriptorSetInfo.allWrites[idx.first][idx.second];
+ stream->putBe32(idx.first);
+ stream->putBe32(idx.second);
+ stream->putBe32(entry.writeType);
+ // entry.descriptorType might be redundant.
+ stream->putBe32(entry.descriptorType);
+ switch (entry.writeType) {
+ case DescriptorSetInfo::DescriptorWriteType::ImageInfo: {
+ VkDescriptorImageInfo imageInfo = entry.imageInfo;
+ // Get the unboxed version
+ imageInfo.imageView =
+ descriptorTypeContainsImage(entry.descriptorType)
+ ? unboxed_to_boxed_non_dispatchable_VkImageView(
+ imageInfo.imageView)
+ : VK_NULL_HANDLE;
+ imageInfo.sampler =
+ descriptorTypeContainsSampler(entry.descriptorType)
+ ? unboxed_to_boxed_non_dispatchable_VkSampler(imageInfo.sampler)
+ : VK_NULL_HANDLE;
+ stream->write(&imageInfo, sizeof(imageInfo));
+ } break;
+ case DescriptorSetInfo::DescriptorWriteType::BufferInfo: {
+ VkDescriptorBufferInfo bufferInfo = entry.bufferInfo;
+ // Get the unboxed version
+ bufferInfo.buffer =
+ unboxed_to_boxed_non_dispatchable_VkBuffer(bufferInfo.buffer);
+ stream->write(&bufferInfo, sizeof(bufferInfo));
+ } break;
+ case DescriptorSetInfo::DescriptorWriteType::BufferView: {
+ // Get the unboxed version
+ VkBufferView bufferView =
+ unboxed_to_boxed_non_dispatchable_VkBufferView(entry.bufferView);
+ stream->write(&bufferView, sizeof(bufferView));
+ } break;
+ case DescriptorSetInfo::DescriptorWriteType::InlineUniformBlock:
+ case DescriptorSetInfo::DescriptorWriteType::AccelerationStructure:
+ // TODO
+ GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+ << "Encountered pending inline uniform block or acceleration "
+ "structure "
+ "desc write, abort (NYI)";
+ default:
+ break;
+ }
+ }
+ }
+ }
mSnapshotState = SnapshotState::Normal;
}
@@ -539,6 +674,7 @@
// destroy all current internal data structures
clear();
mSnapshotState = SnapshotState::Loading;
+ android::base::BumpPool bumpPool;
// this part will replay in the decoder
snapshot()->load(stream, gfxLogger, healthMonitor);
// load mapped memory
@@ -609,6 +745,105 @@
releaseSnapshotStateBlock(&stateBlock);
}
+ // snapshot descriptors
+ std::vector<VkDescriptorPool> sortedBoxedDescriptorPools;
+ for (const auto& descriptorPoolIte : mDescriptorPoolInfo) {
+ auto boxed =
+ unboxed_to_boxed_non_dispatchable_VkDescriptorPool(descriptorPoolIte.first);
+ sortedBoxedDescriptorPools.push_back(boxed);
+ }
+ sort(sortedBoxedDescriptorPools.begin(), sortedBoxedDescriptorPools.end());
+ for (const auto& boxedDescriptorPool : sortedBoxedDescriptorPools) {
+ auto unboxedDescriptorPool = unbox_VkDescriptorPool(boxedDescriptorPool);
+ const DescriptorPoolInfo& poolInfo = mDescriptorPoolInfo[unboxedDescriptorPool];
+
+ std::vector<VkDescriptorSetLayout> layouts;
+ std::vector<uint64_t> poolIds;
+ std::vector<VkWriteDescriptorSet> writeDescriptorSets;
+ std::vector<uint32_t> writeStartingIndices;
+
+ // Temporary structures for the pointers in VkWriteDescriptorSet.
+ // Use unique_ptr so that the pointers don't change when vector resizes.
+ std::vector<std::unique_ptr<VkDescriptorImageInfo>> tmpImageInfos;
+ std::vector<std::unique_ptr<VkDescriptorBufferInfo>> tmpBufferInfos;
+ std::vector<std::unique_ptr<VkBufferView>> tmpBufferViews;
+
+ for (uint64_t poolId : poolInfo.poolIds) {
+ bool allocated = stream->getByte();
+ if (!allocated) {
+ continue;
+ }
+ poolIds.push_back(poolId);
+ writeStartingIndices.push_back(writeDescriptorSets.size());
+ VkDescriptorSetLayout boxedLayout = (VkDescriptorSetLayout)stream->getBe64();
+ layouts.push_back(unbox_VkDescriptorSetLayout(boxedLayout));
+ uint64_t validWriteCount = stream->getBe64();
+ for (int write = 0; write < validWriteCount; write++) {
+ uint32_t binding = stream->getBe32();
+ uint32_t arrayElement = stream->getBe32();
+ DescriptorSetInfo::DescriptorWriteType writeType =
+ static_cast<DescriptorSetInfo::DescriptorWriteType>(stream->getBe32());
+ VkDescriptorType descriptorType =
+ static_cast<VkDescriptorType>(stream->getBe32());
+ VkWriteDescriptorSet writeDescriptorSet = {
+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ .dstSet = (VkDescriptorSet)poolId,
+ .dstBinding = binding,
+ .dstArrayElement = arrayElement,
+ .descriptorCount = 1,
+ .descriptorType = descriptorType,
+ };
+ switch (writeType) {
+ case DescriptorSetInfo::DescriptorWriteType::ImageInfo: {
+ tmpImageInfos.push_back(std::make_unique<VkDescriptorImageInfo>());
+ writeDescriptorSet.pImageInfo = tmpImageInfos.back().get();
+ VkDescriptorImageInfo& imageInfo = *tmpImageInfos.back();
+ stream->read(&imageInfo, sizeof(imageInfo));
+ imageInfo.imageView = descriptorTypeContainsImage(descriptorType)
+ ? unbox_VkImageView(imageInfo.imageView)
+ : 0;
+ imageInfo.sampler = descriptorTypeContainsSampler(descriptorType)
+ ? unbox_VkSampler(imageInfo.sampler)
+ : 0;
+ } break;
+ case DescriptorSetInfo::DescriptorWriteType::BufferInfo: {
+ tmpBufferInfos.push_back(std::make_unique<VkDescriptorBufferInfo>());
+ writeDescriptorSet.pBufferInfo = tmpBufferInfos.back().get();
+ VkDescriptorBufferInfo& bufferInfo = *tmpBufferInfos.back();
+ stream->read(&bufferInfo, sizeof(bufferInfo));
+ bufferInfo.buffer = unbox_VkBuffer(bufferInfo.buffer);
+ } break;
+ case DescriptorSetInfo::DescriptorWriteType::BufferView: {
+ tmpBufferViews.push_back(std::make_unique<VkBufferView>());
+ writeDescriptorSet.pTexelBufferView = tmpBufferViews.back().get();
+ VkBufferView& bufferView = *tmpBufferViews.back();
+ stream->read(&bufferView, sizeof(bufferView));
+ bufferView = unbox_VkBufferView(bufferView);
+ } break;
+ case DescriptorSetInfo::DescriptorWriteType::InlineUniformBlock:
+ case DescriptorSetInfo::DescriptorWriteType::AccelerationStructure:
+ // TODO
+ GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+ << "Encountered pending inline uniform block or acceleration "
+ "structure "
+ "desc write, abort (NYI)";
+ default:
+ break;
+ }
+ writeDescriptorSets.push_back(writeDescriptorSet);
+ }
+ }
+ std::vector<uint32_t> whichPool(poolIds.size(), 0);
+ std::vector<uint32_t> pendingAlloc(poolIds.size(), true);
+
+ const auto& device = poolInfo.device;
+ const auto& deviceInfo = android::base::find(mDeviceInfo, device);
+ VulkanDispatch* dvk = dispatch_VkDevice(deviceInfo->boxed);
+ on_vkQueueCommitDescriptorSetUpdatesGOOGLE(
+ &bumpPool, dvk, device, 1, &unboxedDescriptorPool, poolIds.size(), layouts.data(),
+ poolIds.data(), whichPool.data(), pendingAlloc.data(), writeStartingIndices.data(),
+ writeDescriptorSets.size(), writeDescriptorSets.data());
+ }
mSnapshotState = SnapshotState::Normal;
}
@@ -1668,6 +1903,13 @@
const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer) {
auto device = unbox_VkDevice(boxed_device);
auto vk = dispatch_VkDevice(boxed_device);
+ VkBufferCreateInfo localCreateInfo;
+ if (snapshotsEnabled()) {
+ localCreateInfo = *pCreateInfo;
+ localCreateInfo.usage |=
+ VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+ pCreateInfo = &localCreateInfo;
+ }
VkResult result = vk->vkCreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
@@ -2663,7 +2905,20 @@
auto& setInfo = mDescriptorSetInfo[descriptorSet];
setInfo.pool = pool;
+ setInfo.unboxedLayout = setLayout;
setInfo.bindings = setLayoutInfo->bindings;
+ for (size_t i = 0; i < setInfo.bindings.size(); i++) {
+ VkDescriptorSetLayoutBinding dslBinding = setInfo.bindings[i];
+ int bindingIdx = dslBinding.binding;
+ if (setInfo.allWrites.size() <= bindingIdx) {
+ setInfo.allWrites.resize(bindingIdx + 1);
+ }
+ setInfo.allWrites[bindingIdx].resize(dslBinding.descriptorCount);
+ for (auto& write : setInfo.allWrites[bindingIdx]) {
+ write.descriptorType = dslBinding.descriptorType;
+ write.dstArrayElement = 0;
+ }
+ }
poolInfo->allocedSetsToBoxed[descriptorSet] = (VkDescriptorSet)boxedDescriptorSet;
applyDescriptorSetAllocationLocked(*poolInfo, setInfo.bindings);
@@ -2759,6 +3014,117 @@
const VkWriteDescriptorSet* pDescriptorWrites,
uint32_t descriptorCopyCount,
const VkCopyDescriptorSet* pDescriptorCopies) {
+ if (snapshotsEnabled()) {
+ for (uint32_t writeIdx = 0; writeIdx < descriptorWriteCount; writeIdx++) {
+ const VkWriteDescriptorSet& descriptorWrite = pDescriptorWrites[writeIdx];
+ auto ite = mDescriptorSetInfo.find(descriptorWrite.dstSet);
+ if (ite == mDescriptorSetInfo.end()) {
+ continue;
+ }
+ DescriptorSetInfo& descriptorSetInfo = ite->second;
+ auto& table = descriptorSetInfo.allWrites;
+ VkDescriptorType descType = descriptorWrite.descriptorType;
+ uint32_t dstBinding = descriptorWrite.dstBinding;
+ uint32_t dstArrayElement = descriptorWrite.dstArrayElement;
+ uint32_t descriptorCount = descriptorWrite.descriptorCount;
+
+ uint32_t arrOffset = dstArrayElement;
+
+ if (isDescriptorTypeImageInfo(descType)) {
+ for (uint32_t writeElemIdx = 0; writeElemIdx < descriptorCount;
+ ++writeElemIdx, ++arrOffset) {
+ // Descriptor writes wrap to the next binding. See
+ // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkWriteDescriptorSet.html
+ if (arrOffset >= table[dstBinding].size()) {
+ ++dstBinding;
+ arrOffset = 0;
+ }
+ auto& entry = table[dstBinding][arrOffset];
+ entry.imageInfo = descriptorWrite.pImageInfo[writeElemIdx];
+ entry.writeType = DescriptorSetInfo::DescriptorWriteType::ImageInfo;
+ entry.descriptorType = descType;
+ entry.alives.clear();
+ if (descriptorTypeContainsImage(descType)) {
+ auto* imageViewInfo =
+ android::base::find(mImageViewInfo, entry.imageInfo.imageView);
+ entry.alives.push_back(imageViewInfo->alive);
+ }
+ if (descriptorTypeContainsSampler(descType)) {
+ auto* samplerInfo =
+ android::base::find(mSamplerInfo, entry.imageInfo.sampler);
+ entry.alives.push_back(samplerInfo->alive);
+ }
+ }
+ } else if (isDescriptorTypeBufferInfo(descType)) {
+ for (uint32_t writeElemIdx = 0; writeElemIdx < descriptorCount;
+ ++writeElemIdx, ++arrOffset) {
+ if (arrOffset >= table[dstBinding].size()) {
+ ++dstBinding;
+ arrOffset = 0;
+ }
+ auto& entry = table[dstBinding][arrOffset];
+ entry.bufferInfo = descriptorWrite.pBufferInfo[writeElemIdx];
+ entry.writeType = DescriptorSetInfo::DescriptorWriteType::BufferInfo;
+ entry.descriptorType = descType;
+ entry.alives.clear();
+ auto* bufferInfo =
+ android::base::find(mBufferInfo, entry.bufferInfo.buffer);
+ entry.alives.push_back(bufferInfo->alive);
+ }
+ } else if (isDescriptorTypeBufferView(descType)) {
+ for (uint32_t writeElemIdx = 0; writeElemIdx < descriptorCount;
+ ++writeElemIdx, ++arrOffset) {
+ if (arrOffset >= table[dstBinding].size()) {
+ ++dstBinding;
+ arrOffset = 0;
+ }
+ auto& entry = table[dstBinding][arrOffset];
+ entry.bufferView = descriptorWrite.pTexelBufferView[writeElemIdx];
+ entry.writeType = DescriptorSetInfo::DescriptorWriteType::BufferView;
+ entry.descriptorType = descType;
+ // TODO: check alive
+ ERR("%s: Snapshot for texel buffer view is incomplete.\n", __func__);
+ }
+ } else if (isDescriptorTypeInlineUniformBlock(descType)) {
+ const VkWriteDescriptorSetInlineUniformBlock* descInlineUniformBlock =
+ static_cast<const VkWriteDescriptorSetInlineUniformBlock*>(
+ descriptorWrite.pNext);
+ while (descInlineUniformBlock &&
+ descInlineUniformBlock->sType !=
+ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK) {
+ descInlineUniformBlock =
+ static_cast<const VkWriteDescriptorSetInlineUniformBlock*>(
+ descInlineUniformBlock->pNext);
+ }
+ if (!descInlineUniformBlock) {
+ GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+ << __func__ << ": did not find inline uniform block";
+ return;
+ }
+ auto& entry = table[dstBinding][0];
+ entry.inlineUniformBlock = *descInlineUniformBlock;
+ entry.inlineUniformBlockBuffer.assign(
+ static_cast<const uint8_t*>(descInlineUniformBlock->pData),
+ static_cast<const uint8_t*>(descInlineUniformBlock->pData) +
+ descInlineUniformBlock->dataSize);
+ entry.writeType = DescriptorSetInfo::DescriptorWriteType::InlineUniformBlock;
+ entry.descriptorType = descType;
+ entry.dstArrayElement = dstArrayElement;
+ } else if (isDescriptorTypeAccelerationStructure(descType)) {
+ // TODO
+ // Look for pNext inline uniform block or acceleration structure.
+ // Append new DescriptorWrite entry that holds the buffer
+ ERR("%s: Ignoring Snapshot for emulated write for descriptor type 0x%x\n",
+ __func__, descType);
+ }
+ }
+ // TODO: bookkeep pDescriptorCopies
+ // Our primary use case vkQueueCommitDescriptorSetUpdatesGOOGLE does not use
+ // pDescriptorCopies. Thus skip its implementation for now.
+ if (descriptorCopyCount) {
+ ERR("%s: Snapshot does not support descriptor copy yet\n");
+ }
+ }
bool needEmulateWriteDescriptor = false;
// c++ seems to allow for 0-size array allocation
std::unique_ptr<bool[]> descriptorWritesNeedDeepCopy(new bool[descriptorWriteCount]);
@@ -5733,8 +6099,7 @@
return (VkDescriptorSet)(setHandleInfo->underlying);
}
} else {
- // Snapshot doesn't really replay the commands to allocate those descriptors.
- if (pendingAlloc || snapshotsEnabled()) {
+ if (pendingAlloc) {
VkDescriptorSet allocedSet;
VkDescriptorSetAllocateInfo dsAi = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, 0, pool, 1, &setLayout,
@@ -5774,7 +6139,21 @@
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
<< "queue " << queue << "(boxed: " << boxed_queue << ") with no device registered";
}
+ on_vkQueueCommitDescriptorSetUpdatesGOOGLE(
+ pool, vk, device, descriptorPoolCount, pDescriptorPools, descriptorSetCount,
+ pDescriptorSetLayouts, pDescriptorSetPoolIds, pDescriptorSetWhichPool,
+ pDescriptorSetPendingAllocation, pDescriptorWriteStartingIndices,
+ pendingDescriptorWriteCount, pPendingDescriptorWrites);
+ }
+ void on_vkQueueCommitDescriptorSetUpdatesGOOGLE(
+ android::base::BumpPool* pool, VulkanDispatch* vk, VkDevice device,
+ uint32_t descriptorPoolCount, const VkDescriptorPool* pDescriptorPools,
+ uint32_t descriptorSetCount, const VkDescriptorSetLayout* pDescriptorSetLayouts,
+ const uint64_t* pDescriptorSetPoolIds, const uint32_t* pDescriptorSetWhichPool,
+ const uint32_t* pDescriptorSetPendingAllocation,
+ const uint32_t* pDescriptorWriteStartingIndices, uint32_t pendingDescriptorWriteCount,
+ const VkWriteDescriptorSet* pPendingDescriptorWrites) {
std::vector<VkDescriptorSet> setsToUpdate(descriptorSetCount, nullptr);
bool didAlloc = false;
@@ -5805,7 +6184,6 @@
} else {
writeEndIndex = pDescriptorWriteStartingIndices[i + 1];
}
-
for (uint32_t j = writeStartIndex; j < writeEndIndex; ++j) {
writeDescriptorSetsForHostDriver[j].dstSet = setsToUpdate[i];
}
@@ -6897,6 +7275,18 @@
(descType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT);
}
+ bool descriptorTypeContainsImage(VkDescriptorType descType) {
+ return (descType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) ||
+ (descType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE) ||
+ (descType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) ||
+ (descType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT);
+ }
+
+ bool descriptorTypeContainsSampler(VkDescriptorType descType) {
+ return (descType == VK_DESCRIPTOR_TYPE_SAMPLER) ||
+ (descType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
+ }
+
bool isDescriptorTypeBufferInfo(VkDescriptorType descType) {
return (descType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) ||
(descType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) ||
@@ -6909,6 +7299,34 @@
(descType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);
}
+ bool isDescriptorTypeInlineUniformBlock(VkDescriptorType descType) {
+ return descType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT;
+ }
+
+ bool isDescriptorTypeAccelerationStructure(VkDescriptorType descType) {
+ return descType == VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
+ }
+
+ int descriptorDependencyObjectCount(VkDescriptorType descType) {
+ switch (descType) {
+ case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+ return 2;
+ case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+ case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
+ case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
+ case VK_DESCRIPTOR_TYPE_SAMPLER:
+ case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
+ case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
+ case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
+ case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
+ case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
+ case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
+ return 1;
+ default:
+ return 0;
+ }
+ }
+
struct DescriptorUpdateTemplateInfo {
VkDescriptorUpdateTemplateCreateInfo createInfo;
std::vector<VkDescriptorUpdateTemplateEntry> linearizedTemplateEntries;
diff --git a/host/vulkan/VkDecoderInternalStructs.h b/host/vulkan/VkDecoderInternalStructs.h
index b81c50c..eab6656 100644
--- a/host/vulkan/VkDecoderInternalStructs.h
+++ b/host/vulkan/VkDecoderInternalStructs.h
@@ -231,6 +231,7 @@
VkDeviceMemory memory = 0;
VkDeviceSize memoryOffset = 0;
VkDeviceSize size;
+ std::shared_ptr<bool> alive{new bool(true)};
};
struct ImageInfo {
@@ -251,6 +252,7 @@
// Color buffer, provided via vkAllocateMemory().
std::optional<HandleType> boundColorBuffer;
+ std::shared_ptr<bool> alive{new bool(true)};
};
struct SamplerInfo {
@@ -271,6 +273,7 @@
SamplerInfo(const SamplerInfo& other) { *this = other; }
SamplerInfo(SamplerInfo&& other) = delete;
SamplerInfo& operator=(SamplerInfo&& other) = delete;
+ std::shared_ptr<bool> alive{new bool(true)};
};
struct FenceInfo {
@@ -331,7 +334,36 @@
};
struct DescriptorSetInfo {
+ enum DescriptorWriteType {
+ Empty = 0,
+ ImageInfo = 1,
+ BufferInfo = 2,
+ BufferView = 3,
+ InlineUniformBlock = 4,
+ AccelerationStructure = 5,
+ };
+
+ struct DescriptorWrite {
+ VkDescriptorType descriptorType;
+ DescriptorWriteType writeType = DescriptorWriteType::Empty;
+ uint32_t dstArrayElement; // Only used for inlineUniformBlock and accelerationStructure.
+
+ union {
+ VkDescriptorImageInfo imageInfo;
+ VkDescriptorBufferInfo bufferInfo;
+ VkBufferView bufferView;
+ VkWriteDescriptorSetInlineUniformBlockEXT inlineUniformBlock;
+ VkWriteDescriptorSetAccelerationStructureKHR accelerationStructure;
+ };
+
+ std::vector<uint8_t> inlineUniformBlockBuffer;
+ // Weak pointer(s) to detect if all objects on dependency chain are alive.
+ std::vector<std::weak_ptr<bool>> alives;
+ };
+
VkDescriptorPool pool;
+ VkDescriptorSetLayout unboxedLayout = 0;
+ std::vector<std::vector<DescriptorWrite>> allWrites;
std::vector<VkDescriptorSetLayoutBinding> bindings;
};
diff --git a/host/vulkan/VkDecoderSnapshotUtils.cpp b/host/vulkan/VkDecoderSnapshotUtils.cpp
index 921d075..8cdad81 100644
--- a/host/vulkan/VkDecoderSnapshotUtils.cpp
+++ b/host/vulkan/VkDecoderSnapshotUtils.cpp
@@ -584,7 +584,7 @@
VkBufferCreateInfo bufferCreateInfo = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = static_cast<VkDeviceSize>(bufferInfo->size),
- .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
+ .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
VkBuffer stagingBuffer;