Decouple CompositorVk from DisplayVk
... so that Cuttlefish running without native swapchain support
but with ANGLE in the guest can still use CompositorVk for host
composition.
Bug: b/229145718
Test: `launch_cvd --gpu_mode=gfxstream`
Test: `launch_cvd --gpu_mode=gfxstream` with WIP ANGLE
Test: `cd device/generic/vulkan-cereal/build && ./Vulkan_unittests`
Test: `cd device/generic/vulkan-cereal/build && ninja test`
Test: `ctest --test-dir device/generic/vulkan-cereal/build`
Test: `atest --host gfxstream_compositorvk_test`
Change-Id: Idd124257a2f6b84a4c72bec52c179c1f09252692
diff --git a/stream-servers/Android.bp b/stream-servers/Android.bp
index 2318ef7..58e31fd 100644
--- a/stream-servers/Android.bp
+++ b/stream-servers/Android.bp
@@ -47,6 +47,9 @@
"gfxstream_lz4",
"gfxstream_compressedTextures",
],
+ export_static_lib_headers: [
+ "gfxstream_vulkan_cereal_host",
+ ],
shared_libs: [
"liblog", // gfxstream_base uses this via perfetto-libperfettobase
],
@@ -95,3 +98,65 @@
}
}
}
+
+cc_library {
+ name: "libgfxstream_test_image_utils",
+ defaults: [ "gfxstream_defaults" ],
+ shared_libs: [
+ "libbase",
+ ],
+ static_libs: [
+ "libgfxstream_stb",
+ ],
+ srcs: [
+ "tests/ImageUtils.cpp",
+ ],
+}
+
+// Run with `atest --host gfxstream_compositorvk_test`
+cc_test_host {
+ name: "gfxstream_compositorvk_test",
+ defaults: [ "gfxstream_defaults" ],
+ srcs: [
+ "tests/CompositorVk_unittest.cpp",
+ ],
+ data: [
+ "tests/testdata/256x256_android.png",
+ "tests/testdata/256x256_android_with_transparency.png",
+ "tests/testdata/256x256_golden_blend_premultiplied.png",
+ "tests/testdata/256x256_golden_crop.png",
+ "tests/testdata/256x256_golden_simple_composition.png",
+ "tests/testdata/256x256_golden_multiple_layers.png",
+ "tests/testdata/256x256_golden_multiple_targets_0.png",
+ "tests/testdata/256x256_golden_multiple_targets_1.png",
+ "tests/testdata/256x256_golden_multiple_targets_2.png",
+ "tests/testdata/256x256_golden_multiple_targets_3.png",
+ "tests/testdata/256x256_golden_multiple_targets_4.png",
+ "tests/testdata/256x256_golden_multiple_targets_5.png",
+ "tests/testdata/256x256_golden_multiple_targets_6.png",
+ "tests/testdata/256x256_golden_multiple_targets_7.png",
+ "tests/testdata/256x256_golden_multiple_targets_8.png",
+ "tests/testdata/256x256_golden_multiple_targets_9.png",
+ "tests/testdata/256x256_golden_transform_none.png",
+ "tests/testdata/256x256_golden_transform_fliph.png",
+ "tests/testdata/256x256_golden_transform_flipv.png",
+ "tests/testdata/256x256_golden_transform_rot90.png",
+ "tests/testdata/256x256_golden_transform_rot180.png",
+ "tests/testdata/256x256_golden_transform_rot270.png",
+ "tests/testdata/256x256_golden_transform_fliphrot90.png",
+ "tests/testdata/256x256_golden_transform_flipvrot90.png",
+ ],
+ shared_libs: [
+ "libbase",
+ "libgfxstream_backend",
+ "libgfxstream_test_image_utils",
+ ],
+ static_libs: [
+ "gfxstream_vulkan_server",
+ "libc++fs",
+ ],
+ test_options: {
+ // Disabled by default as requires Vulkan.
+ unit_test: false,
+ },
+}
diff --git a/stream-servers/CMakeLists.txt b/stream-servers/CMakeLists.txt
index 4e655d5..8f9e1c6 100644
--- a/stream-servers/CMakeLists.txt
+++ b/stream-servers/CMakeLists.txt
@@ -135,7 +135,8 @@
tests/ShaderUtils.cpp
tests/GLSnapshotTestDispatch.cpp
tests/GLSnapshotTestStateUtils.cpp
- tests/HelloTriangleImp.cpp)
+ tests/HelloTriangleImp.cpp
+ tests/ImageUtils.cpp)
target_include_directories(
stream-server-testing-support
PUBLIC
@@ -149,6 +150,7 @@
stream-server-testing-support
PUBLIC
gfxstream_backend_static
+ gfxstream_stb
OSWindow
gtest)
@@ -232,6 +234,16 @@
gtest
gtest_main
gmock)
-gtest_discover_tests(Vulkan_unittests)
+gtest_discover_tests(
+ Vulkan_unittests
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
+
+file(GLOB Vulkan_unittests_datafiles "tests/testdata/*.png")
+add_custom_command(TARGET Vulkan_unittests POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E make_directory
+ ${CMAKE_BINARY_DIR}/tests/testdata
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ ${Vulkan_unittests_datafiles}
+ ${CMAKE_BINARY_DIR}/tests/testdata)
set_test_include_files()
diff --git a/stream-servers/ColorBuffer.cpp b/stream-servers/ColorBuffer.cpp
index 0eba85a..06c272a 100644
--- a/stream-servers/ColorBuffer.cpp
+++ b/stream-servers/ColorBuffer.cpp
@@ -987,10 +987,8 @@
#else
int handle,
#endif
- uint64_t size, bool dedicated, bool linearTiling, bool vulkanOnly,
- std::shared_ptr<DisplayVk::DisplayBufferInfo> displayBufferVk) {
+ uint64_t size, bool dedicated, bool linearTiling, bool vulkanOnly) {
RecursiveScopedHelperContext context(m_helper);
- m_displayBufferVk = std::move(displayBufferVk);
s_gles2.glCreateMemoryObjectsEXT(1, &m_memoryObject);
if (dedicated) {
static const GLint DEDICATED_FLAG = GL_TRUE;
diff --git a/stream-servers/ColorBuffer.h b/stream-servers/ColorBuffer.h
index 6a2dc90..0e9388b 100644
--- a/stream-servers/ColorBuffer.h
+++ b/stream-servers/ColorBuffer.h
@@ -255,10 +255,6 @@
void postLayer(const ComposeLayer& l, int frameWidth, int frameHeight);
GLuint getTexture();
- const std::shared_ptr<DisplayVk::DisplayBufferInfo>& getDisplayBufferVk()
- const {
- return m_displayBufferVk;
- };
std::unique_ptr<BorrowedImageInfo> getBorrowedImageInfo();
// ColorBuffer backing change methods
@@ -271,8 +267,7 @@
#else
int handle,
#endif
- uint64_t size, bool dedicated, bool linearTiling, bool vulkanOnly,
- std::shared_ptr<DisplayVk::DisplayBufferInfo> displayBufferVk);
+ uint64_t size, bool dedicated, bool linearTiling, bool vulkanOnly);
// Change to EGL native pixmap
bool importEglNativePixmap(void* pixmap, bool preserveContent);
// Change to some other native EGL image. nativeEglImage must not have
@@ -350,9 +345,6 @@
GLuint m_buf = 0;
uint32_t m_displayId = 0;
bool m_BRSwizzle = false;
- // Won't share with others so that m_displayBufferVk lives shorter than this
- // ColorBuffer.
- std::shared_ptr<DisplayVk::DisplayBufferInfo> m_displayBufferVk;
};
typedef std::shared_ptr<ColorBuffer> ColorBufferPtr;
diff --git a/stream-servers/FrameBuffer.cpp b/stream-servers/FrameBuffer.cpp
index a3aaab1..8d4ed4b 100644
--- a/stream-servers/FrameBuffer.cpp
+++ b/stream-servers/FrameBuffer.cpp
@@ -16,11 +16,13 @@
#include "FrameBuffer.h"
-#include <iomanip>
#include <stdio.h>
#include <string.h>
#include <time.h>
+#include <iomanip>
+
+#include "CompositorGl.h"
#include "DispatchTables.h"
#include "GLESVersionDetector.h"
#include "Hwc2.h"
@@ -514,6 +516,9 @@
feature_is_enabled(
kFeature_GuestUsesAngle);
+ fb->m_useVulkanComposition = feature_is_enabled(kFeature_GuestUsesAngle) ||
+ feature_is_enabled(kFeature_VulkanNativeSwapchain);
+
std::unique_ptr<goldfish_vk::VkEmulationFeatures> vkEmulationFeatures =
std::make_unique<goldfish_vk::VkEmulationFeatures>(goldfish_vk::VkEmulationFeatures{
.glInteropSupported = false, // Set later.
@@ -524,6 +529,7 @@
android::base::getEnvironmentVariable(
"ANDROID_EMU_VK_DISABLE_USE_CREATE_RESOURCES_WITH_REQUIREMENTS")
.empty(),
+ .useVulkanComposition = fb->m_useVulkanComposition,
.useVulkanNativeSwapchain = feature_is_enabled(kFeature_VulkanNativeSwapchain),
.guestRenderDoc = std::move(renderDocMultipleVkInstances),
});
@@ -872,9 +878,18 @@
}
}
- GL_LOG("Performing composition using CompositorGl.");
- fb->m_compositorGl = std::make_unique<CompositorGl>();
- fb->m_compositor = fb->m_compositorGl.get();
+ if (fb->m_useVulkanComposition) {
+ if (!vkEmu->compositorVk) {
+ ERR("Failed to get CompositorVk from VkEmulation.");
+ return false;
+ }
+ GL_LOG("Performing composition using CompositorVk.");
+ fb->m_compositor = vkEmu->compositorVk.get();
+ } else {
+ GL_LOG("Performing composition using CompositorGl.");
+ fb->m_compositorGl = std::make_unique<CompositorGl>();
+ fb->m_compositor = fb->m_compositorGl.get();
+ }
INFO("Graphics Adapter Vendor %s", fb->m_graphicsAdapterVendor.c_str());
INFO("Graphics Adapter %s", fb->m_graphicsAdapterName.c_str());
@@ -919,17 +934,8 @@
ERR("FB: importMemoryToColorBuffer cb handle %#x not found", colorBufferHandle);
return false;
}
-
- std::shared_ptr<DisplayVk::DisplayBufferInfo> db = nullptr;
- if (m_displayVk) {
- db = m_displayVk->createDisplayBuffer(image, imageCi);
- if (!db) {
- ERR("Fail to create display buffer for ColorBuffer %" PRIu64 ".",
- static_cast<uint64_t>(colorBufferHandle));
- }
- }
return cb->importMemory(handle, size, dedicated, imageCi.tiling == VK_IMAGE_TILING_LINEAR,
- vulkanOnly, std::move(db));
+ vulkanOnly);
}
void FrameBuffer::setColorBufferInUse(
@@ -1490,14 +1496,9 @@
genHandle_locked());
}
-void FrameBuffer::createColorBufferWithHandle(
- int p_width,
- int p_height,
- GLenum p_internalFormat,
- FrameworkFormat p_frameworkFormat,
- HandleType handle) {
-
- HandleType resHandle;
+void FrameBuffer::createColorBufferWithHandle(int p_width, int p_height, GLenum p_internalFormat,
+ FrameworkFormat p_frameworkFormat,
+ HandleType handle) {
{
AutoLock mutex(m_lock);
AutoLock colorBufferMapLock(m_colorBufferMapLock);
@@ -1510,12 +1511,14 @@
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER));
}
- resHandle = createColorBufferWithHandleLocked(
- p_width, p_height, p_internalFormat, p_frameworkFormat,
- handle);
+ handle = createColorBufferWithHandleLocked(p_width, p_height, p_internalFormat,
+ p_frameworkFormat, handle);
+ if (!handle) {
+ return;
+ }
}
- if (m_displayVk && resHandle == handle) {
+ if (m_displayVk || m_guestUsesAngle) {
goldfish_vk::setupVkColorBuffer(
handle,
false /* not vulkan only */,
@@ -3574,13 +3577,6 @@
m_guestManagedColorBufferLifetime = guestManaged;
}
-VkImageLayout FrameBuffer::getVkImageLayoutForComposeLayer() const {
- if (m_displayVk) {
- return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
- }
- return VK_IMAGE_LAYOUT_GENERAL;
-}
-
bool FrameBuffer::platformImportResource(uint32_t handle, uint32_t info, void* resource) {
if (!resource) {
ERR("Error: resource was null");
@@ -3645,7 +3641,11 @@
}
std::unique_ptr<BorrowedImageInfo> FrameBuffer::borrowColorBufferForComposition(
- uint32_t colorBufferHandle) {
+ uint32_t colorBufferHandle, bool colorBufferIsTarget) {
+ if (m_useVulkanComposition) {
+ return goldfish_vk::borrowColorBufferForComposition(colorBufferHandle, colorBufferIsTarget);
+ }
+
ColorBufferPtr colorBufferPtr = findColorBuffer(colorBufferHandle);
if (!colorBufferPtr) {
ERR("Failed to get borrowed image info for ColorBuffer:%d", colorBufferHandle);
@@ -3656,6 +3656,10 @@
std::unique_ptr<BorrowedImageInfo> FrameBuffer::borrowColorBufferForDisplay(
uint32_t colorBufferHandle) {
+ if (m_useVulkanComposition) {
+ return goldfish_vk::borrowColorBufferForDisplay(colorBufferHandle);
+ }
+
ColorBufferPtr colorBufferPtr = findColorBuffer(colorBufferHandle);
if (!colorBufferPtr) {
ERR("Failed to get borrowed image info for ColorBuffer:%d", colorBufferHandle);
diff --git a/stream-servers/FrameBuffer.h b/stream-servers/FrameBuffer.h
index a2be6e8..41b46b8 100644
--- a/stream-servers/FrameBuffer.h
+++ b/stream-servers/FrameBuffer.h
@@ -592,9 +592,8 @@
void setGuestManagedColorBufferLifetime(bool guestManaged);
- VkImageLayout getVkImageLayoutForComposeLayer() const;
-
- std::unique_ptr<BorrowedImageInfo> borrowColorBufferForComposition(uint32_t colorBufferHandle);
+ std::unique_ptr<BorrowedImageInfo> borrowColorBufferForComposition(uint32_t colorBufferHandle,
+ bool colorBufferIsTarget);
std::unique_ptr<BorrowedImageInfo> borrowColorBufferForDisplay(uint32_t colorBufferHandle);
private:
@@ -796,6 +795,7 @@
// FrameBuffer owns the CompositorGl if used as there is no GlEmulation
// equivalent to VkEmulation,
std::unique_ptr<CompositorGl> m_compositorGl;
+ bool m_useVulkanComposition = false;
// The implementation for Vulkan native swapchain. Only initialized when useVulkan is set when
// calling FrameBuffer::initialize(). DisplayVk is actually owned by VkEmulation.
diff --git a/stream-servers/GfxStreamBackend.cpp b/stream-servers/GfxStreamBackend.cpp
index cd0326f..50ef988 100644
--- a/stream-servers/GfxStreamBackend.cpp
+++ b/stream-servers/GfxStreamBackend.cpp
@@ -308,7 +308,6 @@
}
}
-
// First we make some agents available.
diff --git a/stream-servers/PostWorker.cpp b/stream-servers/PostWorker.cpp
index 6eb1ff3..297ae5f 100644
--- a/stream-servers/PostWorker.cpp
+++ b/stream-servers/PostWorker.cpp
@@ -97,9 +97,8 @@
if (shouldSkip) {
return;
}
- goldfish_vk::acquireColorBuffersForHostComposing({}, cb->getHndl());
- auto [success, waitForGpu] = m_displayVk->post(cb->getDisplayBufferVk());
- goldfish_vk::releaseColorBufferFromHostComposing({cb->getHndl()});
+ const auto imageInfo = mFb->borrowColorBufferForDisplay(cb->getHndl());
+ auto [success, waitForGpu] = m_displayVk->post(imageInfo.get());
if (success) {
waitForGpu.wait();
} else {
@@ -264,60 +263,15 @@
}
}
- auto targetColorBufferPtr = mFb->findColorBuffer(composeRequest.targetHandle);
- if (m_displayVk) {
- auto targetColorBufferPtr = mFb->findColorBuffer(composeRequest.targetHandle);
- if (!targetColorBufferPtr) {
- GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) <<
- "Failed to retrieve the composition target buffer";
- }
- // We don't copy the render result to the targetHandle color buffer
- // when using the Vulkan native host swapchain, because we directly
- // render to the swapchain image instead of rendering onto a
- // ColorBuffer, and we don't readback from the ColorBuffer so far.
- std::vector<ColorBufferPtr> cbs; // Keep ColorBuffers alive
- cbs.emplace_back(targetColorBufferPtr);
- std::vector<std::shared_ptr<DisplayVk::DisplayBufferInfo>>
- composeBuffers;
- std::vector<uint32_t> layerColorBufferHandles;
- for (const ComposeLayer& layer : composeRequest.layers) {
- auto colorBufferPtr = mFb->findColorBuffer(layer.cbHandle);
- if (!colorBufferPtr) {
- composeBuffers.push_back(nullptr);
- continue;
- }
- auto db = colorBufferPtr->getDisplayBufferVk();
- composeBuffers.push_back(db);
- if (!db) {
- continue;
- }
- cbs.push_back(colorBufferPtr);
- layerColorBufferHandles.emplace_back(layer.cbHandle);
- }
- goldfish_vk::acquireColorBuffersForHostComposing(layerColorBufferHandles,
- composeRequest.targetHandle);
- auto [success, waitForGpu] = m_displayVk->compose(
- composeRequest.layers, composeBuffers, targetColorBufferPtr->getDisplayBufferVk());
- goldfish_vk::setColorBufferCurrentLayout(composeRequest.targetHandle,
- VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
- std::vector<uint32_t> colorBufferHandles(layerColorBufferHandles.begin(),
- layerColorBufferHandles.end());
- colorBufferHandles.emplace_back(composeRequest.targetHandle);
- goldfish_vk::releaseColorBufferFromHostComposing(colorBufferHandles);
- if (!success) {
- m_needsToRebindWindow = true;
- waitForGpu = completedFuture;
- }
- m_lastVkComposeColorBuffer = composeRequest.targetHandle;
- return waitForGpu;
- }
-
Compositor::CompositionRequest compositorRequest = {};
- compositorRequest.target = mFb->borrowColorBufferForComposition(composeRequest.targetHandle);
+ compositorRequest.target = mFb->borrowColorBufferForComposition(composeRequest.targetHandle,
+ /*colorBufferIsTarget=*/true);
for (const ComposeLayer& guestLayer : composeRequest.layers) {
auto& compositorLayer = compositorRequest.layers.emplace_back();
compositorLayer.props = guestLayer;
- compositorLayer.source = mFb->borrowColorBufferForComposition(guestLayer.cbHandle);
+ compositorLayer.source =
+ mFb->borrowColorBufferForComposition(guestLayer.cbHandle,
+ /*colorBufferIsTarget=*/false);
}
return m_compositor->compose(compositorRequest);
diff --git a/stream-servers/tests/CompositorVk_unittest.cpp b/stream-servers/tests/CompositorVk_unittest.cpp
index 8f623f9..a4c62b9 100644
--- a/stream-servers/tests/CompositorVk_unittest.cpp
+++ b/stream-servers/tests/CompositorVk_unittest.cpp
@@ -4,91 +4,70 @@
#include <algorithm>
#include <array>
+#include <filesystem>
#include <glm/gtx/matrix_transform_2d.hpp>
#include <memory>
#include <optional>
+#include "BorrowedImageVk.h"
#include "base/Lock.h"
+#include "tests/ImageUtils.h"
#include "tests/VkTestUtils.h"
#include "vulkan/VulkanDispatch.h"
#include "vulkan/vk_util.h"
+namespace {
+
+static constexpr const bool kDefaultSaveImageIfComparisonFailed = false;
+
+std::string GetTestDataPath(const std::string& basename) {
+ const std::filesystem::path currentPath = std::filesystem::current_path();
+ return currentPath / "tests" / "testdata" / basename;
+}
+
+static constexpr const uint32_t kColorBlack = 0xFF000000;
+static constexpr const uint32_t kColorRed = 0xFF0000FF;
+static constexpr const uint32_t kColorGreen = 0xFF00FF00;
+static constexpr const uint32_t kDefaultImageWidth = 256;
+static constexpr const uint32_t kDefaultImageHeight = 256;
+
class CompositorVkTest : public ::testing::Test {
protected:
- using RenderTarget = emugl::RenderResourceVk<VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
- VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT>;
- using RenderTexture = emugl::RenderTextureVk;
+ using TargetImage = emugl::RenderResourceVk<VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT>;
+ using SourceImage = emugl::RenderTextureVk;
static void SetUpTestCase() { k_vk = emugl::vkDispatch(false); }
- static constexpr uint32_t k_numOfRenderTargets = 10;
- static constexpr uint32_t k_renderTargetWidth = 255;
- static constexpr uint32_t k_renderTargetHeight = 255;
- static constexpr uint32_t k_renderTargetNumOfPixels =
- k_renderTargetWidth * k_renderTargetHeight;
-
void SetUp() override {
ASSERT_NE(k_vk, nullptr);
createInstance();
pickPhysicalDevice();
createLogicalDevice();
- VkFormatProperties formatProperties;
- k_vk->vkGetPhysicalDeviceFormatProperties(m_vkPhysicalDevice, RenderTarget::k_vkFormat,
- &formatProperties);
- if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) {
- GTEST_SKIP();
+ if (!supportsFeatures(TargetImage::k_vkFormat, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) {
+ GTEST_SKIP() << "Skipping test as format " << TargetImage::k_vkFormat
+ << " does not support VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT";
}
- k_vk->vkGetPhysicalDeviceFormatProperties(m_vkPhysicalDevice, RenderTexture::k_vkFormat,
- &formatProperties);
- if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
- GTEST_SKIP();
+ if (!supportsFeatures(SourceImage::k_vkFormat, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
+ GTEST_SKIP() << "Skipping test as format " << SourceImage::k_vkFormat
+ << " does not support VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT";
}
- VkCommandPoolCreateInfo commandPoolCi = {
+ const VkCommandPoolCreateInfo commandPoolCi = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
- .queueFamilyIndex = m_compositorQueueFamilyIndex};
+ .queueFamilyIndex = m_compositorQueueFamilyIndex,
+ };
ASSERT_EQ(k_vk->vkCreateCommandPool(m_vkDevice, &commandPoolCi, nullptr, &m_vkCommandPool),
VK_SUCCESS);
- VkCommandBufferAllocateInfo cmdBuffAllocInfo = {
- .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
- .commandPool = m_vkCommandPool,
- .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
- .commandBufferCount = k_numOfRenderTargets};
- m_vkCommandBuffers.resize(k_numOfRenderTargets);
- VK_CHECK(k_vk->vkAllocateCommandBuffers(m_vkDevice, &cmdBuffAllocInfo,
- m_vkCommandBuffers.data()));
-
k_vk->vkGetDeviceQueue(m_vkDevice, m_compositorQueueFamilyIndex, 0, &m_compositorVkQueue);
- ASSERT_TRUE(m_compositorVkQueue != VK_NULL_HANDLE);
+ ASSERT_NE(m_compositorVkQueue, VK_NULL_HANDLE);
m_compositorVkQueueLock = std::make_shared<android::base::Lock>();
-
- for (uint32_t i = 0; i < k_numOfRenderTargets; i++) {
- auto renderTarget =
- RenderTarget::create(*k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
- m_vkCommandPool, k_renderTargetHeight, k_renderTargetWidth);
- ASSERT_NE(renderTarget, nullptr);
- m_renderTargets.emplace_back(std::move(renderTarget));
- }
-
- m_renderTargetImageViews.resize(m_renderTargets.size());
- ASSERT_EQ(std::transform(m_renderTargets.begin(), m_renderTargets.end(),
- m_renderTargetImageViews.begin(),
- [](const std::unique_ptr<const RenderTarget> &renderTarget) {
- return renderTarget->m_vkImageView;
- }),
- m_renderTargetImageViews.end());
- setUpRGBASampler();
}
void TearDown() override {
- k_vk->vkDestroySampler(m_vkDevice, m_rgbaVkSampler, nullptr);
- k_vk->vkFreeCommandBuffers(m_vkDevice, m_vkCommandPool, m_vkCommandBuffers.size(),
- m_vkCommandBuffers.data());
- m_vkCommandBuffers.clear();
- m_renderTargets.clear();
k_vk->vkDestroyCommandPool(m_vkDevice, m_vkCommandPool, nullptr);
k_vk->vkDestroyDevice(m_vkDevice, nullptr);
m_vkDevice = VK_NULL_HANDLE;
@@ -97,40 +76,176 @@
}
std::unique_ptr<CompositorVk> createCompositor() {
- return CompositorVk::create(
- *k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue, m_compositorVkQueueLock,
- RenderTarget::k_vkFormat, RenderTarget::k_vkImageLayout, RenderTarget::k_vkImageLayout,
- m_renderTargetImageViews.size(), m_vkCommandPool, m_rgbaVkSampler);
+ return CompositorVk::create(*k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
+ m_compositorVkQueueLock, m_compositorQueueFamilyIndex,
+ /*maxFramesInFlight=*/3);
}
- std::vector<std::unique_ptr<CompositorVkRenderTarget>> createCompositorRenderTargets(
- CompositorVk &compositor) {
- std::vector<std::unique_ptr<CompositorVkRenderTarget>> res;
- for (VkImageView imageView : m_renderTargetImageViews) {
- res.emplace_back(compositor.createRenderTarget(imageView, k_renderTargetWidth,
- k_renderTargetHeight));
+ template <typename SourceOrTargetImage>
+ std::unique_ptr<const SourceOrTargetImage> createImageWithColor(uint32_t sourceWidth,
+ uint32_t sourceHeight,
+ uint32_t sourceColor) {
+ auto source =
+ SourceOrTargetImage::create(*k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
+ m_vkCommandPool, sourceWidth, sourceHeight);
+ if (source == nullptr) {
+ return nullptr;
}
- return res;
+
+ std::vector<uint32_t> sourcePixels(sourceWidth * sourceHeight, sourceColor);
+ if (!source->write(sourcePixels)) {
+ return nullptr;
+ }
+
+ return source;
}
- void setUpRGBASampler() {
- VkSamplerCreateInfo samplerCi = {.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
- .magFilter = VK_FILTER_NEAREST,
- .minFilter = VK_FILTER_NEAREST,
- .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
- .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
- .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
- .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
- .mipLodBias = 0.0f,
- .anisotropyEnable = VK_FALSE,
- .maxAnisotropy = 1.0f,
- .compareEnable = VK_FALSE,
- .compareOp = VK_COMPARE_OP_ALWAYS,
- .minLod = 0.0f,
- .maxLod = 0.0f,
- .borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK,
- .unnormalizedCoordinates = VK_FALSE};
- VK_CHECK(k_vk->vkCreateSampler(m_vkDevice, &samplerCi, nullptr, &m_rgbaVkSampler));
+ std::unique_ptr<const SourceImage> createSourceImageFromPng(const std::string& filename) {
+ uint32_t sourceWidth;
+ uint32_t sourceHeight;
+ std::vector<uint32_t> sourcePixels;
+ if (!LoadRGBAFromPng(filename, &sourceWidth, &sourceHeight, &sourcePixels)) {
+ return nullptr;
+ }
+
+ auto source =
+ SourceImage::create(*k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
+ m_vkCommandPool, sourceWidth, sourceHeight);
+ if (source == nullptr) {
+ return nullptr;
+ }
+
+ if (!source->write(sourcePixels)) {
+ return nullptr;
+ }
+
+ return source;
+ }
+
+ bool isRGBAPixelNear(uint32_t actualPixel, uint32_t expectedPixel) {
+ const uint8_t* actualRGBA = reinterpret_cast<const uint8_t*>(&actualPixel);
+ const uint8_t* expectedRGBA = reinterpret_cast<const uint8_t*>(&expectedPixel);
+
+ constexpr const uint32_t kRGBA8888Tolerance = 1;
+ for (uint32_t channel = 0; channel < 4; channel++) {
+ const uint8_t actualChannel = actualRGBA[channel];
+ const uint8_t expectedChannel = expectedRGBA[channel];
+
+ if ((std::max(actualChannel, expectedChannel) -
+ std::min(actualChannel, expectedChannel)) > kRGBA8888Tolerance) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool compareRGBAPixels(const uint32_t* actualPixels, const uint32_t* expectedPixels,
+ const uint32_t width, const uint32_t height) {
+ bool comparisonFailed = false;
+
+ uint32_t reportedIncorrectPixels = 0;
+ constexpr const uint32_t kMaxReportedIncorrectPixels = 10;
+
+ for (uint32_t y = 0; y < width; y++) {
+ for (uint32_t x = 0; x < height; x++) {
+ const uint32_t actualPixel = actualPixels[y * height + x];
+ const uint32_t expectedPixel = expectedPixels[y * width + x];
+ if (!isRGBAPixelNear(actualPixel, expectedPixel)) {
+ comparisonFailed = true;
+ if (reportedIncorrectPixels < kMaxReportedIncorrectPixels) {
+ reportedIncorrectPixels++;
+ const uint8_t* actualRGBA = reinterpret_cast<const uint8_t*>(&actualPixel);
+ const uint8_t* expectedRGBA =
+ reinterpret_cast<const uint8_t*>(&expectedPixel);
+ ADD_FAILURE()
+ << "Pixel comparison failed at (" << x << ", " << y << ") "
+ << " with actual "
+ << " r:" << static_cast<int>(actualRGBA[0])
+ << " g:" << static_cast<int>(actualRGBA[1])
+ << " b:" << static_cast<int>(actualRGBA[2])
+ << " a:" << static_cast<int>(actualRGBA[3]) << " but expected "
+ << " r:" << static_cast<int>(expectedRGBA[0])
+ << " g:" << static_cast<int>(expectedRGBA[1])
+ << " b:" << static_cast<int>(expectedRGBA[2])
+ << " a:" << static_cast<int>(expectedRGBA[3]);
+ }
+ }
+ }
+ }
+ return comparisonFailed;
+ }
+
+ void compareImageWithGoldenPng(const TargetImage* target, const std::string& filename,
+ const bool saveImageIfComparisonFailed) {
+ const uint32_t targetWidth = target->m_width;
+ const uint32_t targetHeight = target->m_height;
+ const auto targetPixelsOpt = target->read();
+ ASSERT_TRUE(targetPixelsOpt.has_value());
+ const auto& targetPixels = *targetPixelsOpt;
+
+ uint32_t goldenWidth;
+ uint32_t goldenHeight;
+ std::vector<uint32_t> goldenPixels;
+ const bool loadedGolden =
+ LoadRGBAFromPng(filename, &goldenWidth, &goldenHeight, &goldenPixels);
+ EXPECT_TRUE(loadedGolden) << "Failed to load golden image from " << filename;
+
+ bool comparisonFailed = !loadedGolden;
+
+ if (loadedGolden) {
+ EXPECT_EQ(target->m_width, goldenWidth)
+ << "Invalid width comparison with golden image from " << filename;
+ EXPECT_EQ(target->m_height, goldenHeight)
+ << "Invalid height comparison with golden image from " << filename;
+ if (targetWidth != goldenWidth || targetHeight != goldenHeight) {
+ comparisonFailed = true;
+ }
+ if (!comparisonFailed) {
+ comparisonFailed = compareRGBAPixels(targetPixels.data(), goldenPixels.data(),
+ goldenWidth, goldenHeight);
+ }
+ }
+
+ if (saveImageIfComparisonFailed && comparisonFailed) {
+ const std::string output =
+ std::filesystem::temp_directory_path() / std::filesystem::path(filename).filename();
+ SaveRGBAToPng(targetWidth, targetHeight, targetPixels.data(), output);
+ ADD_FAILURE() << "Saved composition result to " << output;
+ }
+ }
+
+ template <typename SourceOrTargetImage>
+ std::unique_ptr<BorrowedImageInfoVk> createBorrowedImageInfo(const SourceOrTargetImage* image) {
+ static int sImageId = 0;
+
+ auto ret = std::make_unique<BorrowedImageInfoVk>();
+ ret->id = sImageId++;
+ ret->width = image->m_width;
+ ret->height = image->m_height;
+ ret->image = image->m_vkImage;
+ ret->imageCreateInfo = image->m_vkImageCreateInfo;
+ ret->imageView = image->m_vkImageView;
+ ret->preBorrowLayout = SourceOrTargetImage::k_vkImageLayout;
+ ret->preBorrowQueueFamilyIndex = m_compositorQueueFamilyIndex;
+ ret->postBorrowLayout = SourceOrTargetImage::k_vkImageLayout;
+ ret->postBorrowQueueFamilyIndex = m_compositorQueueFamilyIndex;
+ return ret;
+ }
+
+ void checkImageFilledWith(const TargetImage* image, uint32_t expectedColor) {
+ auto actualPixelsOpt = image->read();
+ ASSERT_TRUE(actualPixelsOpt.has_value());
+ auto& actualPixels = *actualPixelsOpt;
+
+ const std::vector<uint32_t> expectedPixels(image->numOfPixels(), expectedColor);
+ compareRGBAPixels(actualPixels.data(), expectedPixels.data(), image->m_width,
+ image->m_height);
+ }
+
+ void fillImageWith(const TargetImage* image, uint32_t color) {
+ const std::vector<uint32_t> pixels(image->numOfPixels(), color);
+ ASSERT_TRUE(image->write(pixels)) << "Failed to fill image with color:" << color;
+ checkImageFilledWith(image, color);
}
static const goldfish_vk::VulkanDispatch *k_vk;
@@ -138,29 +253,29 @@
VkPhysicalDevice m_vkPhysicalDevice = VK_NULL_HANDLE;
uint32_t m_compositorQueueFamilyIndex = 0;
VkDevice m_vkDevice = VK_NULL_HANDLE;
- std::vector<std::unique_ptr<const RenderTarget>> m_renderTargets;
- std::vector<VkImageView> m_renderTargetImageViews;
VkCommandPool m_vkCommandPool = VK_NULL_HANDLE;
VkQueue m_compositorVkQueue = VK_NULL_HANDLE;
std::shared_ptr<android::base::Lock> m_compositorVkQueueLock;
- std::vector<VkCommandBuffer> m_vkCommandBuffers;
- VkSampler m_rgbaVkSampler = VK_NULL_HANDLE;
private:
void createInstance() {
- VkApplicationInfo appInfo = {.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
- .pNext = nullptr,
- .pApplicationName = "emulator CompositorVk unittest",
- .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
- .pEngineName = "No Engine",
- .engineVersion = VK_MAKE_VERSION(1, 0, 0),
- .apiVersion = VK_API_VERSION_1_1};
- VkInstanceCreateInfo instanceCi = {.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
- .pApplicationInfo = &appInfo,
- .enabledExtensionCount = 0,
- .ppEnabledExtensionNames = nullptr};
+ const VkApplicationInfo appInfo = {
+ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
+ .pNext = nullptr,
+ .pApplicationName = "emulator CompositorVk unittest",
+ .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
+ .pEngineName = "No Engine",
+ .engineVersion = VK_MAKE_VERSION(1, 0, 0),
+ .apiVersion = VK_API_VERSION_1_1,
+ };
+ const VkInstanceCreateInfo instanceCi = {
+ .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+ .pApplicationInfo = &appInfo,
+ .enabledExtensionCount = 0,
+ .ppEnabledExtensionNames = nullptr,
+ };
ASSERT_EQ(k_vk->vkCreateInstance(&instanceCi, nullptr, &m_vkInstance), VK_SUCCESS);
- ASSERT_TRUE(m_vkInstance != VK_NULL_HANDLE);
+ ASSERT_NE(m_vkInstance, VK_NULL_HANDLE);
}
void pickPhysicalDevice() {
@@ -181,7 +296,7 @@
queueFamilyProperties.data());
uint32_t queueFamilyIndex = 0;
for (; queueFamilyIndex < queueFamilyCount; queueFamilyIndex++) {
- if (CompositorVk::validateQueueFamilyProperties(
+ if (CompositorVk::queueSupportsComposition(
queueFamilyProperties[queueFamilyIndex])) {
break;
}
@@ -199,291 +314,499 @@
void createLogicalDevice() {
const float queuePriority = 1.0f;
- VkDeviceQueueCreateInfo queueCi = {.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
- .queueFamilyIndex = m_compositorQueueFamilyIndex,
- .queueCount = 1,
- .pQueuePriorities = &queuePriority};
- VkPhysicalDeviceFeatures2 features = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
- .pNext = nullptr};
- VkDeviceCreateInfo deviceCi = {.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
- .pNext = &features,
- .queueCreateInfoCount = 1,
- .pQueueCreateInfos = &queueCi,
- .enabledLayerCount = 0,
- .enabledExtensionCount = 0,
- .ppEnabledExtensionNames = nullptr};
+ const VkDeviceQueueCreateInfo queueCi = {
+ .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+ .queueFamilyIndex = m_compositorQueueFamilyIndex,
+ .queueCount = 1,
+ .pQueuePriorities = &queuePriority,
+ };
+ const VkPhysicalDeviceFeatures2 features = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
+ .pNext = nullptr,
+ };
+ const VkDeviceCreateInfo deviceCi = {
+ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+ .pNext = &features,
+ .queueCreateInfoCount = 1,
+ .pQueueCreateInfos = &queueCi,
+ .enabledLayerCount = 0,
+ .enabledExtensionCount = 0,
+ .ppEnabledExtensionNames = nullptr,
+ };
ASSERT_EQ(k_vk->vkCreateDevice(m_vkPhysicalDevice, &deviceCi, nullptr, &m_vkDevice),
VK_SUCCESS);
- ASSERT_TRUE(m_vkDevice != VK_NULL_HANDLE);
+ ASSERT_NE(m_vkDevice, VK_NULL_HANDLE);
+ }
+
+ bool supportsFeatures(VkFormat format, VkFormatFeatureFlags features) {
+ VkFormatProperties formatProperties = {};
+ k_vk->vkGetPhysicalDeviceFormatProperties(m_vkPhysicalDevice, format, &formatProperties);
+ return (formatProperties.optimalTilingFeatures & features) == features;
}
};
const goldfish_vk::VulkanDispatch *CompositorVkTest::k_vk = nullptr;
-TEST_F(CompositorVkTest, Init) { ASSERT_NE(createCompositor(), nullptr); }
-
-TEST_F(CompositorVkTest, ValidateQueueFamilyProperties) {
+TEST_F(CompositorVkTest, QueueSupportsComposition) {
VkQueueFamilyProperties properties = {};
+
properties.queueFlags &= ~VK_QUEUE_GRAPHICS_BIT;
- ASSERT_FALSE(CompositorVk::validateQueueFamilyProperties(properties));
+ ASSERT_FALSE(CompositorVk::queueSupportsComposition(properties));
+
properties.queueFlags |= VK_QUEUE_GRAPHICS_BIT;
- ASSERT_TRUE(CompositorVk::validateQueueFamilyProperties(properties));
+ ASSERT_TRUE(CompositorVk::queueSupportsComposition(properties));
}
-TEST_F(CompositorVkTest, EmptyCompositionShouldDrawABlackFrame) {
- std::vector<uint32_t> pixels(k_renderTargetNumOfPixels);
- for (uint32_t i = 0; i < k_renderTargetNumOfPixels; i++) {
- uint8_t v = static_cast<uint8_t>((i / 4) & 0xff);
- uint8_t *pixel = reinterpret_cast<uint8_t *>(&pixels[i]);
- pixel[0] = v;
- pixel[1] = v;
- pixel[2] = v;
- pixel[3] = 0xff;
- }
- for (uint32_t i = 0; i < k_numOfRenderTargets; i++) {
- ASSERT_TRUE(m_renderTargets[i]->write(pixels));
- auto maybeImageBytes = m_renderTargets[i]->read();
- ASSERT_TRUE(maybeImageBytes.has_value());
- for (uint32_t i = 0; i < k_renderTargetNumOfPixels; i++) {
- ASSERT_EQ(pixels[i], maybeImageBytes.value()[i]);
- }
- }
+TEST_F(CompositorVkTest, Init) { ASSERT_NE(createCompositor(), nullptr); }
+TEST_F(CompositorVkTest, EmptyCompositionShouldDrawABlackFrame) {
auto compositor = createCompositor();
- auto renderTargets = createCompositorRenderTargets(*compositor);
ASSERT_NE(compositor, nullptr);
- // render to render targets with event index
- std::vector<VkCommandBuffer> cmdBuffs = {};
- for (uint32_t i = 0; i < k_numOfRenderTargets; i++) {
- VkCommandBufferBeginInfo beginInfo = {
- .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
- .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
- };
- VK_CHECK(k_vk->vkBeginCommandBuffer(m_vkCommandBuffers[i], &beginInfo));
- compositor->recordCommandBuffers(i, m_vkCommandBuffers[i], *renderTargets[i]);
- VK_CHECK(k_vk->vkEndCommandBuffer(m_vkCommandBuffers[i]));
- if (i % 2 == 0) {
- cmdBuffs.emplace_back(m_vkCommandBuffers[i]);
- }
+ std::vector<std::unique_ptr<const TargetImage>> targets;
+ constexpr const uint32_t kNumImages = 10;
+ for (uint32_t i = 0; i < kNumImages; i++) {
+ auto target =
+ TargetImage::create(*k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
+ m_vkCommandPool, kDefaultImageWidth, kDefaultImageHeight);
+ ASSERT_NE(target, nullptr);
+ fillImageWith(target.get(), kColorRed);
+ targets.emplace_back(std::move(target));
}
- VkSubmitInfo submitInfo = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
- .commandBufferCount = static_cast<uint32_t>(cmdBuffs.size()),
- .pCommandBuffers = cmdBuffs.data()};
- ASSERT_EQ(k_vk->vkQueueSubmit(m_compositorVkQueue, 1, &submitInfo, VK_NULL_HANDLE), VK_SUCCESS);
- ASSERT_EQ(k_vk->vkQueueWaitIdle(m_compositorVkQueue), VK_SUCCESS);
- for (uint32_t i = 0; i < k_numOfRenderTargets; i++) {
- auto maybeImagePixels = m_renderTargets[i]->read();
- ASSERT_TRUE(maybeImagePixels.has_value());
- const auto &imagePixels = maybeImagePixels.value();
- for (uint32_t j = 0; j < k_renderTargetNumOfPixels; j++) {
- const auto pixel = reinterpret_cast<const uint8_t *>(&imagePixels[j]);
- // should only render to render targets with even index
- if (i % 2 == 0) {
- ASSERT_EQ(pixel[0], 0);
- ASSERT_EQ(pixel[1], 0);
- ASSERT_EQ(pixel[2], 0);
- ASSERT_EQ(pixel[3], 0xff);
- } else {
- ASSERT_EQ(pixels[j], imagePixels[j]);
- }
- }
+ for (uint32_t i = 0; i < kNumImages; i++) {
+ const Compositor::CompositionRequest compositionRequest = {
+ .target = createBorrowedImageInfo(targets[i].get()),
+ .layers = {}, // Note: this is empty!
+ };
+
+ auto compositionCompleteWaitable = compositor->compose(compositionRequest);
+ compositionCompleteWaitable.wait();
+ }
+
+ for (const auto& target : targets) {
+ checkImageFilledWith(target.get(), kColorBlack);
}
}
TEST_F(CompositorVkTest, SimpleComposition) {
- constexpr uint32_t textureLeft = 30;
- constexpr uint32_t textureRight = 50;
- constexpr uint32_t textureTop = 10;
- constexpr uint32_t textureBottom = 40;
- constexpr uint32_t textureWidth = textureRight - textureLeft;
- constexpr uint32_t textureHeight = textureBottom - textureTop;
- auto texture = RenderTexture::create(*k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
- m_vkCommandPool, textureWidth, textureHeight);
- uint32_t textureColor;
- uint8_t *textureColor_ = reinterpret_cast<uint8_t *>(&textureColor);
- textureColor_[0] = 0xff;
- textureColor_[1] = 0;
- textureColor_[2] = 0;
- textureColor_[3] = 0xff;
- std::vector<uint32_t> pixels(textureWidth * textureHeight, textureColor);
- ASSERT_TRUE(texture->write(pixels));
auto compositor = createCompositor();
ASSERT_NE(compositor, nullptr);
- ComposeLayer composeLayer = {
- 0 /* No color buffer handle */,
- HWC2_COMPOSITION_DEVICE,
- {
- /* frame in which the texture shows up on the display */
- textureLeft,
- textureTop,
- textureRight,
- textureBottom,
- },
- {
- /* how much of the texture itself to show */
- 0.0,
- 0.0,
- static_cast<float>(textureWidth),
- static_cast<float>(textureHeight),
- },
- HWC2_BLEND_MODE_PREMULTIPLIED /* blend mode */,
- 1.0 /* alpha */,
- {0, 0, 0, 0} /* color (N/A) */,
- HWC_TRANSFORM_NONE /* transform (no rotation */,
+ auto source = createSourceImageFromPng(GetTestDataPath("256x256_android.png"));
+ ASSERT_NE(source, nullptr);
+
+ auto target = TargetImage::create(*k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
+ m_vkCommandPool, source->m_width, source->m_height);
+ ASSERT_NE(target, nullptr);
+ fillImageWith(target.get(), kColorBlack);
+
+ Compositor::CompositionRequest compositionRequest = {
+ .target = createBorrowedImageInfo(target.get()),
};
+ compositionRequest.layers.emplace_back(Compositor::CompositionRequestLayer{
+ .source = createBorrowedImageInfo(source.get()),
+ .props =
+ {
+ .composeMode = HWC2_COMPOSITION_DEVICE,
+ .displayFrame =
+ {
+ .left = 64,
+ .top = 32,
+ .right = 128,
+ .bottom = 160,
+ },
+ .crop =
+ {
+ .left = 0,
+ .top = 0,
+ .right = static_cast<float>(source->m_width),
+ .bottom = static_cast<float>(source->m_height),
+ },
+ .blendMode = HWC2_BLEND_MODE_PREMULTIPLIED,
+ .alpha = 1.0,
+ .color =
+ {
+ .r = 0,
+ .g = 0,
+ .b = 0,
+ .a = 0,
+ },
+ .transform = HWC_TRANSFORM_NONE,
+ },
+ });
- std::unique_ptr<ComposeLayerVk> composeLayerVkPtr = ComposeLayerVk::createFromHwc2ComposeLayer(
- m_rgbaVkSampler, texture->m_vkImageView, composeLayer, textureWidth, textureHeight,
- k_renderTargetWidth, k_renderTargetHeight);
+ auto compositionCompleteWaitable = compositor->compose(compositionRequest);
+ compositionCompleteWaitable.wait();
- std::vector<std::unique_ptr<ComposeLayerVk>> layers;
- layers.emplace_back(std::move(composeLayerVkPtr));
-
- auto composition = std::make_unique<Composition>(std::move(layers));
- auto renderTargets = createCompositorRenderTargets(*compositor);
- compositor->setComposition(0, std::move(composition));
-
- VkCommandBuffer cmdBuff = m_vkCommandBuffers[0];
- VkCommandBufferBeginInfo beginInfo = {
- .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
- .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
- };
- VK_CHECK(k_vk->vkBeginCommandBuffer(cmdBuff, &beginInfo));
- compositor->recordCommandBuffers(0, cmdBuff, *renderTargets[0]);
- VK_CHECK(k_vk->vkEndCommandBuffer(cmdBuff));
-
- VkSubmitInfo submitInfo = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
- .commandBufferCount = 1,
- .pCommandBuffers = &cmdBuff};
- ASSERT_EQ(k_vk->vkQueueSubmit(m_compositorVkQueue, 1, &submitInfo, VK_NULL_HANDLE), VK_SUCCESS);
- ASSERT_EQ(k_vk->vkQueueWaitIdle(m_compositorVkQueue), VK_SUCCESS);
-
- auto maybeImagePixels = m_renderTargets[0]->read();
- ASSERT_TRUE(maybeImagePixels.has_value());
- const auto &imagePixels = maybeImagePixels.value();
-
- for (uint32_t i = 0; i < k_renderTargetHeight; i++) {
- for (uint32_t j = 0; j < k_renderTargetWidth; j++) {
- uint32_t offset = i * k_renderTargetWidth + j;
- const uint8_t *pixel = reinterpret_cast<const uint8_t *>(&imagePixels[offset]);
- EXPECT_EQ(pixel[1], 0);
- EXPECT_EQ(pixel[2], 0);
- EXPECT_EQ(pixel[3], 0xff);
- if (i >= textureTop && i < textureBottom && j >= textureLeft && j < textureRight) {
- EXPECT_EQ(pixel[0], 0xff);
- } else {
- EXPECT_EQ(pixel[0], 0);
- }
- }
- }
+ compareImageWithGoldenPng(target.get(),
+ GetTestDataPath("256x256_golden_simple_composition.png"),
+ kDefaultSaveImageIfComparisonFailed);
}
-TEST_F(CompositorVkTest, CompositingWithDifferentCompositionOnMultipleTargets) {
- constexpr uint32_t textureWidth = 20;
- constexpr uint32_t textureHeight = 30;
-
- ComposeLayer defaultComposeLayer = {
- 0 /* No color buffer handle */,
- HWC2_COMPOSITION_DEVICE,
- {
- /* display frame (to be replaced for each of k_numOfRenderTargets */
- 0,
- 0,
- 0,
- 0,
- },
- {
- /* how much of the texture itself to show */
- 0.0,
- 0.0,
- static_cast<float>(textureWidth),
- static_cast<float>(textureHeight),
- },
- HWC2_BLEND_MODE_PREMULTIPLIED /* blend mode */,
- 1.0 /* alpha */,
- {0, 0, 0, 0} /* color (N/A) */,
- HWC_TRANSFORM_NONE /* transform (no rotation */,
- };
-
- std::vector<ComposeLayer> composeLayers(k_numOfRenderTargets);
- for (int i = 0; i < k_numOfRenderTargets; i++) {
- composeLayers[i] = defaultComposeLayer;
- int left = (i * 30) % (k_renderTargetWidth - textureWidth);
- int top = (i * 20) % (k_renderTargetHeight - textureHeight);
- int right = left + textureWidth;
- int bottom = top + textureHeight;
- composeLayers[i].displayFrame = {
- left,
- top,
- right,
- bottom,
- };
- }
-
- auto texture = RenderTexture::create(*k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
- m_vkCommandPool, textureWidth, textureHeight);
- uint32_t textureColor;
- uint8_t *textureColor_ = reinterpret_cast<uint8_t *>(&textureColor);
- textureColor_[0] = 0xff;
- textureColor_[1] = 0;
- textureColor_[2] = 0;
- textureColor_[3] = 0xff;
- std::vector<uint32_t> pixels(textureWidth * textureHeight, textureColor);
- ASSERT_TRUE(texture->write(pixels));
+TEST_F(CompositorVkTest, BlendPremultiplied) {
auto compositor = createCompositor();
ASSERT_NE(compositor, nullptr);
- auto renderTargets = createCompositorRenderTargets(*compositor);
- for (int i = 0; i < k_numOfRenderTargets; i++) {
- std::unique_ptr<ComposeLayerVk> composeLayerVkPtr =
- ComposeLayerVk::createFromHwc2ComposeLayer(
- m_rgbaVkSampler, texture->m_vkImageView, composeLayers[i], textureWidth,
- textureHeight, k_renderTargetWidth, k_renderTargetHeight);
- std::vector<std::unique_ptr<ComposeLayerVk>> layers;
- layers.emplace_back(std::move(composeLayerVkPtr));
+ auto source =
+ createSourceImageFromPng(GetTestDataPath("256x256_android_with_transparency.png"));
+ ASSERT_NE(source, nullptr);
- auto composition = std::make_unique<Composition>(std::move(layers));
- compositor->setComposition(i, std::move(composition));
+ auto target = TargetImage::create(*k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
+ m_vkCommandPool, source->m_width, source->m_height);
+ ASSERT_NE(target, nullptr);
+ fillImageWith(target.get(), kColorBlack);
- VkCommandBuffer cmdBuff = m_vkCommandBuffers[i];
- VkCommandBufferBeginInfo beginInfo = {
- .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
- .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
- };
- VK_CHECK(k_vk->vkBeginCommandBuffer(cmdBuff, &beginInfo));
- compositor->recordCommandBuffers(i, cmdBuff, *renderTargets[i]);
- VK_CHECK(k_vk->vkEndCommandBuffer(cmdBuff));
+ Compositor::CompositionRequest compositionRequest = {
+ .target = createBorrowedImageInfo(target.get()),
+ };
+ compositionRequest.layers.emplace_back(Compositor::CompositionRequestLayer{
+ .source = createBorrowedImageInfo(source.get()),
+ .props =
+ {
+ .composeMode = HWC2_COMPOSITION_DEVICE,
+ .displayFrame =
+ {
+ .left = 0,
+ .top = 0,
+ .right = static_cast<int>(target->m_width),
+ .bottom = static_cast<int>(target->m_height),
+ },
+ .crop =
+ {
+ .left = 0,
+ .top = 0,
+ .right = static_cast<float>(source->m_width),
+ .bottom = static_cast<float>(source->m_height),
+ },
+ .blendMode = HWC2_BLEND_MODE_PREMULTIPLIED,
+ .alpha = 1.0,
+ .color =
+ {
+ .r = 0,
+ .g = 0,
+ .b = 0,
+ .a = 0,
+ },
+ .transform = HWC_TRANSFORM_NONE,
+ },
+ });
- VkSubmitInfo submitInfo = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
- .commandBufferCount = 1,
- .pCommandBuffers = &cmdBuff};
- ASSERT_EQ(k_vk->vkQueueSubmit(m_compositorVkQueue, 1, &submitInfo, VK_NULL_HANDLE),
- VK_SUCCESS);
- ASSERT_EQ(k_vk->vkQueueWaitIdle(m_compositorVkQueue), VK_SUCCESS);
+ auto compositionCompleteWaitable = compositor->compose(compositionRequest);
+ compositionCompleteWaitable.wait();
- auto maybeImagePixels = m_renderTargets[i]->read();
- ASSERT_TRUE(maybeImagePixels.has_value());
- const auto &imagePixels = maybeImagePixels.value();
+ compareImageWithGoldenPng(target.get(),
+ GetTestDataPath("256x256_golden_blend_premultiplied.png"),
+ kDefaultSaveImageIfComparisonFailed);
+}
- for (uint32_t j = 0; j < k_renderTargetHeight; j++) {
- for (uint32_t k = 0; k < k_renderTargetWidth; k++) {
- uint32_t offset = j * k_renderTargetWidth + k;
- const uint8_t *pixel = reinterpret_cast<const uint8_t *>(&imagePixels[offset]);
- EXPECT_EQ(pixel[1], 0);
- EXPECT_EQ(pixel[2], 0);
- EXPECT_EQ(pixel[3], 0xff);
- if (j >= composeLayers[i].displayFrame.top &&
- j < composeLayers[i].displayFrame.bottom &&
- k >= composeLayers[i].displayFrame.left &&
- k < composeLayers[i].displayFrame.right) {
- EXPECT_EQ(pixel[0], 0xff);
- } else {
- EXPECT_EQ(pixel[0], 0);
- }
- }
- }
+TEST_F(CompositorVkTest, Crop) {
+ auto compositor = createCompositor();
+ ASSERT_NE(compositor, nullptr);
+
+ auto source = createSourceImageFromPng(GetTestDataPath("256x256_android.png"));
+ ASSERT_NE(source, nullptr);
+
+ auto target = TargetImage::create(*k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
+ m_vkCommandPool, source->m_width, source->m_height);
+ ASSERT_NE(target, nullptr);
+ fillImageWith(target.get(), kColorBlack);
+
+ Compositor::CompositionRequest compositionRequest = {
+ .target = createBorrowedImageInfo(target.get()),
+ };
+ compositionRequest.layers.emplace_back(Compositor::CompositionRequestLayer{
+ .source = createBorrowedImageInfo(source.get()),
+ .props =
+ {
+ .composeMode = HWC2_COMPOSITION_DEVICE,
+ .displayFrame =
+ {
+ .left = 0,
+ .top = 0,
+ .right = static_cast<int>(target->m_width),
+ .bottom = static_cast<int>(target->m_height),
+ },
+ .crop =
+ {
+ .left = 0,
+ .top = 0,
+ .right = static_cast<float>(source->m_width) / 2.0f,
+ .bottom = static_cast<float>(source->m_height) / 2.0f,
+ },
+ .blendMode = HWC2_BLEND_MODE_PREMULTIPLIED,
+ .alpha = 1.0,
+ .color =
+ {
+ .r = 0,
+ .g = 0,
+ .b = 0,
+ .a = 0,
+ },
+ .transform = HWC_TRANSFORM_NONE,
+ },
+ });
+
+ auto compositionCompleteWaitable = compositor->compose(compositionRequest);
+ compositionCompleteWaitable.wait();
+
+ compareImageWithGoldenPng(target.get(), GetTestDataPath("256x256_golden_crop.png"),
+ kDefaultSaveImageIfComparisonFailed);
+}
+
+TEST_F(CompositorVkTest, Transformations) {
+ auto compositor = createCompositor();
+ ASSERT_NE(compositor, nullptr);
+
+ auto source = createSourceImageFromPng(GetTestDataPath("256x256_android.png"));
+ ASSERT_NE(source, nullptr);
+
+ Compositor::CompositionRequest compositionRequest;
+ compositionRequest.layers.emplace_back(Compositor::CompositionRequestLayer{
+ .source = createBorrowedImageInfo(source.get()),
+ .props =
+ {
+ .composeMode = HWC2_COMPOSITION_DEVICE,
+ .displayFrame =
+ {
+ .left = 32,
+ .top = 32,
+ .right = 224,
+ .bottom = 224,
+ },
+ .crop =
+ {
+ .left = 0,
+ .top = 0,
+ .right = static_cast<float>(source->m_width),
+ .bottom = static_cast<float>(source->m_height),
+ },
+ .blendMode = HWC2_BLEND_MODE_PREMULTIPLIED,
+ .alpha = 1.0,
+ .color =
+ {
+ .r = 0,
+ .g = 0,
+ .b = 0,
+ .a = 0,
+ },
+ .transform = HWC_TRANSFORM_NONE,
+ },
+ });
+
+ const std::unordered_map<hwc_transform_t, std::string> transformToGolden = {
+ {HWC_TRANSFORM_NONE, "256x256_golden_transform_none.png"},
+ {HWC_TRANSFORM_FLIP_H, "256x256_golden_transform_fliph.png"},
+ {HWC_TRANSFORM_FLIP_V, "256x256_golden_transform_flipv.png"},
+ {HWC_TRANSFORM_ROT_90, "256x256_golden_transform_rot90.png"},
+ {HWC_TRANSFORM_ROT_180, "256x256_golden_transform_rot180.png"},
+ {HWC_TRANSFORM_ROT_270, "256x256_golden_transform_rot270.png"},
+ {HWC_TRANSFORM_FLIP_H_ROT_90, "256x256_golden_transform_fliphrot90.png"},
+ {HWC_TRANSFORM_FLIP_V_ROT_90, "256x256_golden_transform_flipvrot90.png"},
+ };
+
+ for (const auto [transform, golden] : transformToGolden) {
+ auto target = TargetImage::create(*k_vk, m_vkDevice, m_vkPhysicalDevice,
+ m_compositorVkQueue, m_vkCommandPool, 256, 256);
+ ASSERT_NE(target, nullptr);
+ fillImageWith(target.get(), kColorBlack);
+
+ compositionRequest.target = createBorrowedImageInfo(target.get());
+ compositionRequest.layers[0].props.transform = transform;
+
+ auto compositionCompleteWaitable = compositor->compose(compositionRequest);
+ compositionCompleteWaitable.wait();
+
+ compareImageWithGoldenPng(target.get(), GetTestDataPath(golden),
+ kDefaultSaveImageIfComparisonFailed);
}
}
+
+TEST_F(CompositorVkTest, MultipleTargetsComposition) {
+ auto compositor = createCompositor();
+ ASSERT_NE(compositor, nullptr);
+
+ constexpr const uint32_t kNumCompostions = 10;
+
+ auto source = createImageWithColor<SourceImage>(256, 256, kColorGreen);
+ ASSERT_NE(source, nullptr);
+
+ std::vector<std::unique_ptr<const TargetImage>> targets;
+ for (uint32_t i = 0; i < kNumCompostions; i++) {
+ auto target = createImageWithColor<TargetImage>(256, 256, kColorBlack);
+ ASSERT_NE(target, nullptr);
+ targets.emplace_back(std::move(target));
+ }
+
+ Compositor::CompositionRequest compositionRequest = {};
+ compositionRequest.layers.emplace_back(Compositor::CompositionRequestLayer{
+ .source = createBorrowedImageInfo(source.get()),
+ .props =
+ {
+ .composeMode = HWC2_COMPOSITION_DEVICE,
+ .displayFrame =
+ {
+ .left = 0,
+ .top = 0,
+ .right = 0,
+ .bottom = static_cast<int>(source->m_height),
+ },
+ .crop =
+ {
+ .left = 0,
+ .top = 0,
+ .right = static_cast<float>(source->m_width),
+ .bottom = static_cast<float>(source->m_height),
+ },
+ .blendMode = HWC2_BLEND_MODE_PREMULTIPLIED,
+ .alpha = 1.0,
+ .color =
+ {
+ .r = 0,
+ .g = 0,
+ .b = 0,
+ .a = 0,
+ },
+ .transform = HWC_TRANSFORM_NONE,
+ },
+ });
+
+ const uint32_t displayFrameWidth = 256 / kNumCompostions;
+ for (uint32_t i = 0; i < kNumCompostions; i++) {
+ const auto& target = targets[i];
+
+ compositionRequest.target = createBorrowedImageInfo(target.get());
+ compositionRequest.layers[0].props.displayFrame.left = (i + 0) * displayFrameWidth;
+ compositionRequest.layers[0].props.displayFrame.right = (i + 1) * displayFrameWidth;
+
+ auto compositionCompleteWaitable = compositor->compose(compositionRequest);
+ compositionCompleteWaitable.wait();
+
+ compareImageWithGoldenPng(
+ target.get(),
+ GetTestDataPath("256x256_golden_multiple_targets_" + std::to_string(i) + ".png"),
+ kDefaultSaveImageIfComparisonFailed);
+ }
+}
+
+TEST_F(CompositorVkTest, MultipleLayers) {
+ auto compositor = createCompositor();
+ ASSERT_NE(compositor, nullptr);
+
+ auto source1 = createSourceImageFromPng(GetTestDataPath("256x256_android.png"));
+ ASSERT_NE(source1, nullptr);
+
+ auto source2 =
+ createSourceImageFromPng(GetTestDataPath("256x256_android_with_transparency.png"));
+ ASSERT_NE(source2, nullptr);
+
+ auto target = TargetImage::create(*k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
+ m_vkCommandPool, kDefaultImageWidth, kDefaultImageHeight);
+ ASSERT_NE(target, nullptr);
+ fillImageWith(target.get(), kColorBlack);
+
+ Compositor::CompositionRequest compositionRequest = {
+ .target = createBorrowedImageInfo(target.get()),
+ };
+ compositionRequest.layers.emplace_back(Compositor::CompositionRequestLayer{
+ .source = createBorrowedImageInfo(source1.get()),
+ .props =
+ {
+ .composeMode = HWC2_COMPOSITION_DEVICE,
+ .displayFrame =
+ {
+ .left = 0,
+ .top = 0,
+ .right = static_cast<int>(target->m_width),
+ .bottom = static_cast<int>(target->m_height),
+ },
+ .crop =
+ {
+ .left = 32.0,
+ .top = 32.0,
+ .right = static_cast<float>(source1->m_width) - 32.0f,
+ .bottom = static_cast<float>(source1->m_height) - 32.0f,
+ },
+ .blendMode = HWC2_BLEND_MODE_PREMULTIPLIED,
+ .alpha = 1.0,
+ .color =
+ {
+ .r = 0,
+ .g = 0,
+ .b = 0,
+ .a = 0,
+ },
+ .transform = HWC_TRANSFORM_NONE,
+ },
+ });
+ compositionRequest.layers.emplace_back(Compositor::CompositionRequestLayer{
+ .source = createBorrowedImageInfo(source2.get()),
+ .props =
+ {
+ .composeMode = HWC2_COMPOSITION_DEVICE,
+ .displayFrame =
+ {
+ .left = 0,
+ .top = 0,
+ .right = static_cast<int>(target->m_width),
+ .bottom = static_cast<int>(target->m_height),
+ },
+ .crop =
+ {
+ .left = 0,
+ .top = 0,
+ .right = static_cast<float>(source2->m_width) / 2.0f,
+ .bottom = static_cast<float>(source2->m_height) / 2.0f,
+ },
+ .blendMode = HWC2_BLEND_MODE_PREMULTIPLIED,
+ .alpha = 1.0,
+ .color =
+ {
+ .r = 0,
+ .g = 0,
+ .b = 0,
+ .a = 0,
+ },
+ .transform = HWC_TRANSFORM_ROT_90,
+ },
+ });
+ compositionRequest.layers.emplace_back(Compositor::CompositionRequestLayer{
+ .source = createBorrowedImageInfo(source2.get()),
+ .props =
+ {
+ .composeMode = HWC2_COMPOSITION_DEVICE,
+ .displayFrame =
+ {
+ .left = 0,
+ .top = 0,
+ .right = static_cast<int>(target->m_width) / 2,
+ .bottom = static_cast<int>(target->m_height),
+ },
+ .crop =
+ {
+ .left = 0,
+ .top = 0,
+ .right = static_cast<float>(source2->m_width),
+ .bottom = static_cast<float>(source2->m_height),
+ },
+ .blendMode = HWC2_BLEND_MODE_PREMULTIPLIED,
+ .alpha = 1.0,
+ .color =
+ {
+ .r = 0,
+ .g = 0,
+ .b = 0,
+ .a = 0,
+ },
+ .transform = HWC_TRANSFORM_FLIP_V_ROT_90,
+ },
+ });
+
+ auto compositionCompleteWaitable = compositor->compose(compositionRequest);
+ compositionCompleteWaitable.wait();
+
+ compareImageWithGoldenPng(target.get(), GetTestDataPath("256x256_golden_multiple_layers.png"),
+ kDefaultSaveImageIfComparisonFailed);
+}
+
+} // namespace
diff --git a/stream-servers/tests/DisplayVk_unittest.cpp b/stream-servers/tests/DisplayVk_unittest.cpp
index 7c36164..a70dc1f 100644
--- a/stream-servers/tests/DisplayVk_unittest.cpp
+++ b/stream-servers/tests/DisplayVk_unittest.cpp
@@ -1,7 +1,10 @@
+// Note: needs to be included before DisplayVk to avoid conflicts
+// between gtest and x11 headers.
#include <gtest/gtest.h>
#include "DisplayVk.h"
+#include "BorrowedImageVk.h"
#include "Standalone.h"
#include "base/Lock.h"
#include "tests/VkTestUtils.h"
@@ -9,6 +12,8 @@
class DisplayVkTest : public ::testing::Test {
protected:
+ using RenderTexture = emugl::RenderTextureVk;
+
static void SetUpTestCase() { k_vk = emugl::vkDispatch(false); }
void SetUp() override {
@@ -52,7 +57,22 @@
}
}
- using RenderTexture = emugl::RenderTextureVk;
+ std::unique_ptr<BorrowedImageInfoVk> createBorrowedImageInfo(
+ const std::unique_ptr<const RenderTexture>& texture) {
+ static uint32_t sTextureId = 0;
+
+ auto info = std::make_unique<BorrowedImageInfoVk>();
+ info->id = sTextureId++;
+ info->width = texture->m_vkImageCreateInfo.extent.width;
+ info->height = texture->m_vkImageCreateInfo.extent.height;
+ info->image = texture->m_vkImage;
+ info->imageCreateInfo = texture->m_vkImageCreateInfo;
+ info->preBorrowLayout = RenderTexture::k_vkImageLayout;
+ info->preBorrowQueueFamilyIndex = m_compositorQueueFamilyIndex;
+ info->postBorrowLayout = RenderTexture::k_vkImageLayout;
+ info->postBorrowQueueFamilyIndex = m_compositorQueueFamilyIndex;
+ return info;
+ }
static const goldfish_vk::VulkanDispatch *k_vk;
static const uint32_t k_width = 0x100;
@@ -133,7 +153,7 @@
maybeSwapChainQueueFamilyIndex = queueFamilyIndex;
}
if (!maybeCompositorQueueFamilyIndex.has_value() &&
- CompositorVk::validateQueueFamilyProperties(queueProps[queueFamilyIndex])) {
+ CompositorVk::queueSupportsComposition(queueProps[queueFamilyIndex])) {
maybeCompositorQueueFamilyIndex = queueFamilyIndex;
}
}
@@ -192,8 +212,8 @@
m_vkCommandPool, textureWidth, textureHeight);
std::vector<uint32_t> pixels(textureWidth * textureHeight, 0);
ASSERT_TRUE(texture->write(pixels));
- auto cbvk = displayVk.createDisplayBuffer(texture->m_vkImage, texture->m_vkImageCreateInfo);
- ASSERT_TRUE(std::get<0>(displayVk.post(cbvk)));
+ const auto imageInfo = createBorrowedImageInfo(texture);
+ ASSERT_TRUE(std::get<0>(displayVk.post(imageInfo.get())));
}
TEST_F(DisplayVkTest, SimplePost) {
@@ -212,10 +232,10 @@
}
}
ASSERT_TRUE(texture->write(pixels));
- auto cbvk = m_displayVk->createDisplayBuffer(texture->m_vkImage, texture->m_vkImageCreateInfo);
+ const auto imageInfo = createBorrowedImageInfo(texture);
std::vector<std::shared_future<void>> waitForGpuFutures;
for (uint32_t i = 0; i < 10; i++) {
- auto [success, waitForGpuFuture] = m_displayVk->post(cbvk);
+ auto [success, waitForGpuFuture] = m_displayVk->post(imageInfo.get());
ASSERT_TRUE(success);
waitForGpuFutures.emplace_back(std::move(waitForGpuFuture));
}
@@ -239,16 +259,14 @@
std::vector<uint32_t> greenPixels(textureWidth * textureHeight, green);
ASSERT_TRUE(redTexture->write(redPixels));
ASSERT_TRUE(greenTexture->write(greenPixels));
- auto redCbvk =
- m_displayVk->createDisplayBuffer(redTexture->m_vkImage, redTexture->m_vkImageCreateInfo);
- auto greenCbvk = m_displayVk->createDisplayBuffer(greenTexture->m_vkImage,
- greenTexture->m_vkImageCreateInfo);
+ const auto redImageInfo = createBorrowedImageInfo(redTexture);
+ const auto greenImageInfo = createBorrowedImageInfo(greenTexture);
std::vector<std::shared_future<void>> waitForGpuFutures;
for (uint32_t i = 0; i < 10; i++) {
- auto [success, waitForGpuFuture] = m_displayVk->post(redCbvk);
+ auto [success, waitForGpuFuture] = m_displayVk->post(redImageInfo.get());
ASSERT_TRUE(success);
waitForGpuFutures.emplace_back(std::move(waitForGpuFuture));
- std::tie(success, waitForGpuFuture) = m_displayVk->post(greenCbvk);
+ std::tie(success, waitForGpuFuture) = m_displayVk->post(greenImageInfo.get());
ASSERT_TRUE(success);
waitForGpuFutures.emplace_back(std::move(waitForGpuFuture));
}
diff --git a/stream-servers/tests/ImageUtils.cpp b/stream-servers/tests/ImageUtils.cpp
new file mode 100644
index 0000000..f9de13f
--- /dev/null
+++ b/stream-servers/tests/ImageUtils.cpp
@@ -0,0 +1,58 @@
+// Copyright (C) 2022 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 "ImageUtils.h"
+
+#include <stb/stb_image.h>
+#include <stb/stb_image_write.h>
+
+#include <cstring>
+
+bool LoadRGBAFromPng(const std::string& filename, uint32_t* outWidth, uint32_t* outHeight,
+ std::vector<uint32_t>* outPixels) {
+ *outWidth = 0;
+ *outHeight = 0;
+ outPixels->clear();
+
+ int decodedWidth;
+ int decodedHeight;
+ int decodedChannels;
+ unsigned char* decodedPixels =
+ stbi_load(filename.c_str(), &decodedWidth, &decodedHeight, &decodedChannels,
+ /*desiredChannels=*/4);
+ if (decodedPixels == nullptr) {
+ return false;
+ }
+
+ const std::size_t decodedSize = decodedWidth * decodedHeight;
+ const std::size_t decodedSizeBytes = decodedSize * 4;
+
+ *outWidth = static_cast<uint32_t>(decodedWidth);
+ *outHeight = static_cast<uint32_t>(decodedHeight);
+ outPixels->resize(decodedSize, 0);
+ std::memcpy(outPixels->data(), decodedPixels, decodedSizeBytes);
+
+ stbi_image_free(decodedPixels);
+
+ return true;
+}
+
+bool SaveRGBAToPng(uint32_t width, uint32_t height, const uint32_t* pixels,
+ const std::string& filename) {
+ if (stbi_write_png(filename.c_str(), width, height,
+ /*channels=*/4, pixels, width * 4) == 0) {
+ return false;
+ }
+ return true;
+}
diff --git a/stream-servers/tests/ImageUtils.h b/stream-servers/tests/ImageUtils.h
new file mode 100644
index 0000000..567741b
--- /dev/null
+++ b/stream-servers/tests/ImageUtils.h
@@ -0,0 +1,25 @@
+// Copyright (C) 2022 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.
+
+#pragma once
+
+#include <cctype>
+#include <string>
+#include <vector>
+
+bool LoadRGBAFromPng(const std::string& filename, uint32_t* outWidth, uint32_t* outHeight,
+ std::vector<uint32_t>* outPixels);
+
+bool SaveRGBAToPng(uint32_t width, uint32_t height, const uint32_t* pixels,
+ const std::string& filename);
diff --git a/stream-servers/tests/VkTestUtils.h b/stream-servers/tests/VkTestUtils.h
index 0a6e9af..b509135 100644
--- a/stream-servers/tests/VkTestUtils.h
+++ b/stream-servers/tests/VkTestUtils.h
@@ -48,7 +48,7 @@
template <VkImageLayout imageLayout, VkImageUsageFlags imageUsage>
struct RenderResourceVk : public RenderResourceVkBase {
public:
- static constexpr VkFormat k_vkFormat = VK_FORMAT_R8G8B8A8_SRGB;
+ static constexpr VkFormat k_vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
static constexpr uint32_t k_bpp = 4;
static constexpr VkImageLayout k_vkImageLayout = imageLayout;
diff --git a/stream-servers/tests/testdata/256x256_android.png b/stream-servers/tests/testdata/256x256_android.png
new file mode 100644
index 0000000..4dd93d7
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_android.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_android_with_transparency.png b/stream-servers/tests/testdata/256x256_android_with_transparency.png
new file mode 100644
index 0000000..1618bd6
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_android_with_transparency.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_blend_premultiplied.png b/stream-servers/tests/testdata/256x256_golden_blend_premultiplied.png
new file mode 100644
index 0000000..8cb1d16
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_blend_premultiplied.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_crop.png b/stream-servers/tests/testdata/256x256_golden_crop.png
new file mode 100644
index 0000000..59417c5
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_crop.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_multiple_layers.png b/stream-servers/tests/testdata/256x256_golden_multiple_layers.png
new file mode 100644
index 0000000..dab9f08
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_multiple_layers.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_multiple_targets_0.png b/stream-servers/tests/testdata/256x256_golden_multiple_targets_0.png
new file mode 100644
index 0000000..26fc138
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_multiple_targets_0.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_multiple_targets_1.png b/stream-servers/tests/testdata/256x256_golden_multiple_targets_1.png
new file mode 100644
index 0000000..8ff75c5
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_multiple_targets_1.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_multiple_targets_2.png b/stream-servers/tests/testdata/256x256_golden_multiple_targets_2.png
new file mode 100644
index 0000000..40f4a05
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_multiple_targets_2.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_multiple_targets_3.png b/stream-servers/tests/testdata/256x256_golden_multiple_targets_3.png
new file mode 100644
index 0000000..ea8489d
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_multiple_targets_3.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_multiple_targets_4.png b/stream-servers/tests/testdata/256x256_golden_multiple_targets_4.png
new file mode 100644
index 0000000..96f28ab
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_multiple_targets_4.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_multiple_targets_5.png b/stream-servers/tests/testdata/256x256_golden_multiple_targets_5.png
new file mode 100644
index 0000000..f94807c
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_multiple_targets_5.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_multiple_targets_6.png b/stream-servers/tests/testdata/256x256_golden_multiple_targets_6.png
new file mode 100644
index 0000000..3f228ec
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_multiple_targets_6.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_multiple_targets_7.png b/stream-servers/tests/testdata/256x256_golden_multiple_targets_7.png
new file mode 100644
index 0000000..e973b57
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_multiple_targets_7.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_multiple_targets_8.png b/stream-servers/tests/testdata/256x256_golden_multiple_targets_8.png
new file mode 100644
index 0000000..4cd7269
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_multiple_targets_8.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_multiple_targets_9.png b/stream-servers/tests/testdata/256x256_golden_multiple_targets_9.png
new file mode 100644
index 0000000..b3c8f0b
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_multiple_targets_9.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_simple_composition.png b/stream-servers/tests/testdata/256x256_golden_simple_composition.png
new file mode 100644
index 0000000..a2345d0
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_simple_composition.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_transform_fliph.png b/stream-servers/tests/testdata/256x256_golden_transform_fliph.png
new file mode 100644
index 0000000..96e8c11
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_transform_fliph.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_transform_fliphrot90.png b/stream-servers/tests/testdata/256x256_golden_transform_fliphrot90.png
new file mode 100644
index 0000000..ad77440
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_transform_fliphrot90.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_transform_flipv.png b/stream-servers/tests/testdata/256x256_golden_transform_flipv.png
new file mode 100644
index 0000000..1b42862
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_transform_flipv.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_transform_flipvrot90.png b/stream-servers/tests/testdata/256x256_golden_transform_flipvrot90.png
new file mode 100644
index 0000000..3b6c18a
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_transform_flipvrot90.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_transform_none.png b/stream-servers/tests/testdata/256x256_golden_transform_none.png
new file mode 100644
index 0000000..4055960
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_transform_none.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_transform_rot180.png b/stream-servers/tests/testdata/256x256_golden_transform_rot180.png
new file mode 100644
index 0000000..b7a30c6
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_transform_rot180.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_transform_rot270.png b/stream-servers/tests/testdata/256x256_golden_transform_rot270.png
new file mode 100644
index 0000000..db1dc9f
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_transform_rot270.png
Binary files differ
diff --git a/stream-servers/tests/testdata/256x256_golden_transform_rot90.png b/stream-servers/tests/testdata/256x256_golden_transform_rot90.png
new file mode 100644
index 0000000..6580bb3
--- /dev/null
+++ b/stream-servers/tests/testdata/256x256_golden_transform_rot90.png
Binary files differ
diff --git a/stream-servers/vulkan/Android.bp b/stream-servers/vulkan/Android.bp
index 4191120..41cf32e 100644
--- a/stream-servers/vulkan/Android.bp
+++ b/stream-servers/vulkan/Android.bp
@@ -30,6 +30,7 @@
"-Wno-unreachable-code-loop-increment",
],
srcs: [
+ "BorrowedImageVk.cpp",
"CompositorVk.cpp",
"DisplayVk.cpp",
"SwapChainStateVk.cpp",
diff --git a/stream-servers/vulkan/BorrowedImageVk.cpp b/stream-servers/vulkan/BorrowedImageVk.cpp
new file mode 100644
index 0000000..7f84547
--- /dev/null
+++ b/stream-servers/vulkan/BorrowedImageVk.cpp
@@ -0,0 +1,113 @@
+// Copyright 2022 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 expresso or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "BorrowedImageVk.h"
+
+void addNeededBarriersToUseBorrowedImage(
+ const BorrowedImageInfoVk& borrowedImageInfo, uint32_t usedQueueFamilyIndex,
+ VkImageLayout usedInitialImageLayout, VkImageLayout usedFinalImageLayout,
+ std::vector<VkImageMemoryBarrier>* preUseQueueTransferBarriers,
+ std::vector<VkImageMemoryBarrier>* preUseLayoutTransitionBarriers,
+ std::vector<VkImageMemoryBarrier>* postUseLayoutTransitionBarriers,
+ std::vector<VkImageMemoryBarrier>* postUseQueueTransferBarriers) {
+ if (borrowedImageInfo.preBorrowQueueFamilyIndex != usedQueueFamilyIndex) {
+ const VkImageMemoryBarrier queueTransferBarrier = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
+ .oldLayout = borrowedImageInfo.preBorrowLayout,
+ .newLayout = borrowedImageInfo.preBorrowLayout,
+ .srcQueueFamilyIndex = borrowedImageInfo.preBorrowQueueFamilyIndex,
+ .dstQueueFamilyIndex = usedQueueFamilyIndex,
+ .image = borrowedImageInfo.image,
+ .subresourceRange =
+ {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ };
+ preUseQueueTransferBarriers->emplace_back(queueTransferBarrier);
+ }
+ if (borrowedImageInfo.preBorrowLayout != usedInitialImageLayout &&
+ usedInitialImageLayout != VK_IMAGE_LAYOUT_UNDEFINED) {
+ const VkImageMemoryBarrier layoutTransitionBarrier = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
+ .oldLayout = borrowedImageInfo.preBorrowLayout,
+ .newLayout = usedInitialImageLayout,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = borrowedImageInfo.image,
+ .subresourceRange =
+ {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ };
+ preUseLayoutTransitionBarriers->emplace_back(layoutTransitionBarrier);
+ }
+ if (borrowedImageInfo.postBorrowLayout != usedFinalImageLayout) {
+ const VkImageMemoryBarrier layoutTransitionBarrier = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
+ .oldLayout = usedFinalImageLayout,
+ .newLayout = borrowedImageInfo.postBorrowLayout,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = borrowedImageInfo.image,
+ .subresourceRange =
+ {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ };
+ postUseLayoutTransitionBarriers->emplace_back(layoutTransitionBarrier);
+ }
+ if (borrowedImageInfo.postBorrowQueueFamilyIndex != usedQueueFamilyIndex) {
+ const VkImageMemoryBarrier queueTransferBarrier = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
+ .oldLayout = borrowedImageInfo.postBorrowLayout,
+ .newLayout = borrowedImageInfo.postBorrowLayout,
+ .srcQueueFamilyIndex = usedQueueFamilyIndex,
+ .dstQueueFamilyIndex = borrowedImageInfo.postBorrowQueueFamilyIndex,
+ .image = borrowedImageInfo.image,
+ .subresourceRange =
+ {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ };
+ postUseQueueTransferBarriers->emplace_back(queueTransferBarrier);
+ }
+}
diff --git a/stream-servers/BorrowedImageVk.h b/stream-servers/vulkan/BorrowedImageVk.h
similarity index 77%
rename from stream-servers/BorrowedImageVk.h
rename to stream-servers/vulkan/BorrowedImageVk.h
index 2ee18dd..87902d3 100644
--- a/stream-servers/BorrowedImageVk.h
+++ b/stream-servers/vulkan/BorrowedImageVk.h
@@ -14,6 +14,9 @@
#pragma once
+#include <cstdint>
+#include <vector>
+
#include "BorrowedImage.h"
#include "vulkan/cereal/common/goldfish_vk_dispatch.h"
@@ -45,3 +48,11 @@
// after composition.
uint32_t postBorrowQueueFamilyIndex = 0;
};
+
+void addNeededBarriersToUseBorrowedImage(
+ const BorrowedImageInfoVk& borrowedImageInfo, uint32_t usedQueueFamilyIndex,
+ VkImageLayout usedInitialImageLayout, VkImageLayout usedFinalImageLayout,
+ std::vector<VkImageMemoryBarrier>* preUseQueueTransferBarriers,
+ std::vector<VkImageMemoryBarrier>* preUseLayoutTransitionBarriers,
+ std::vector<VkImageMemoryBarrier>* postUseLayoutTransitionBarriers,
+ std::vector<VkImageMemoryBarrier>* postUseQueueTransferBarriers);
diff --git a/stream-servers/vulkan/CMakeLists.txt b/stream-servers/vulkan/CMakeLists.txt
index 0dc73bf..ef13d4c 100644
--- a/stream-servers/vulkan/CMakeLists.txt
+++ b/stream-servers/vulkan/CMakeLists.txt
@@ -1,6 +1,7 @@
add_subdirectory(cereal)
add_library(gfxstream-vulkan-server
+ BorrowedImageVk.cpp
CompositorVk.cpp
DisplayVk.cpp
SwapChainStateVk.cpp
diff --git a/stream-servers/vulkan/CompositorVk.cpp b/stream-servers/vulkan/CompositorVk.cpp
index 3f73086..685585f 100644
--- a/stream-servers/vulkan/CompositorVk.cpp
+++ b/stream-servers/vulkan/CompositorVk.cpp
@@ -7,7 +7,7 @@
#include <optional>
#include "host-common/logging.h"
-#include "vulkan/VkCommonOperations.h"
+#include "vulkan/vk_enum_string_helper.h"
#include "vulkan/vk_util.h"
using emugl::ABORT_REASON_OTHER;
@@ -18,150 +18,145 @@
#include "vulkan/CompositorVertexShader.h"
} // namespace CompositorVkShader
-#define COMPOSITOR_VK_ERROR(fmt, ...) \
- do { \
- fprintf(stderr, "%s(%s:%d): " fmt "\n", __func__, __FILE__, __LINE__, ##__VA_ARGS__); \
- fflush(stderr); \
- } while (0)
+namespace {
+
+constexpr const VkImageLayout kSourceImageInitialLayoutUsed =
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+constexpr const VkImageLayout kSourceImageFinalLayoutUsed =
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+constexpr const VkImageLayout kTargetImageInitialLayoutUsed = VK_IMAGE_LAYOUT_UNDEFINED;
+constexpr const VkImageLayout kTargetImageFinalLayoutUsed = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+
+const BorrowedImageInfoVk* getInfoOrAbort(const std::unique_ptr<BorrowedImageInfo>& info) {
+ auto imageVk = static_cast<const BorrowedImageInfoVk*>(info.get());
+ if (imageVk != nullptr) {
+ return imageVk;
+ }
+
+ GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+ << "CompositorVk did not find BorrowedImageInfoVk";
+}
+
+struct Vertex {
+ alignas(8) glm::vec2 pos;
+ alignas(8) glm::vec2 tex;
+
+ static VkVertexInputBindingDescription getBindingDescription() {
+ return VkVertexInputBindingDescription{
+ .binding = 0,
+ .stride = sizeof(struct Vertex),
+ .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
+ };
+ }
+
+ static std::array<VkVertexInputAttributeDescription, 2> getAttributeDescription() {
+ return {
+ VkVertexInputAttributeDescription{
+ .location = 0,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32_SFLOAT,
+ .offset = offsetof(struct Vertex, pos),
+ },
+ VkVertexInputAttributeDescription{
+ .location = 1,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32_SFLOAT,
+ .offset = offsetof(struct Vertex, tex),
+ },
+ };
+ }
+};
+
+static const std::vector<Vertex> k_vertices = {
+ // clang-format off
+ { .pos = {-1.0f, -1.0f}, .tex = {0.0f, 0.0f}},
+ { .pos = { 1.0f, -1.0f}, .tex = {1.0f, 0.0f}},
+ { .pos = { 1.0f, 1.0f}, .tex = {1.0f, 1.0f}},
+ { .pos = {-1.0f, 1.0f}, .tex = {0.0f, 1.0f}},
+ // clang-format on
+};
+
+static const std::vector<uint16_t> k_indices = {0, 1, 2, 2, 3, 0};
static VkShaderModule createShaderModule(const goldfish_vk::VulkanDispatch& vk, VkDevice device,
const std::vector<uint32_t>& code) {
- VkShaderModuleCreateInfo shaderModuleCi = {
+ const VkShaderModuleCreateInfo shaderModuleCi = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = static_cast<uint32_t>(code.size() * sizeof(uint32_t)),
- .pCode = code.data()};
+ .pCode = code.data(),
+ };
VkShaderModule res;
VK_CHECK(vk.vkCreateShaderModule(device, &shaderModuleCi, nullptr, &res));
return res;
}
-ComposeLayerVk::ComposeLayerVk(VkSampler vkSampler, VkImageView vkImageView,
- const LayerTransform& layerTransform)
- : m_vkSampler(vkSampler),
- m_vkImageView(vkImageView),
- m_layerTransform({.pos = layerTransform.pos, .texcoord = layerTransform.texcoord}) {}
+} // namespace
-std::unique_ptr<ComposeLayerVk> ComposeLayerVk::createFromHwc2ComposeLayer(
- VkSampler vkSampler, VkImageView vkImageView, const ComposeLayer& composeLayer,
- uint32_t cbWidth, uint32_t cbHeight, uint32_t dstWidth, uint32_t dstHeight) {
- // Calculate the posTransform and the texcoordTransform needed in the
- // uniform of the Compositor.vert shader. The posTransform should transform
- // the square(top = -1, bottom = 1, left = -1, right = 1) to the position
- // where the layer should be drawn in NDC space given the composeLayer.
- // texcoordTransform should transform the unit square(top = 0, bottom = 1,
- // left = 0, right = 1) to where we should sample the layer in the
- // normalized uv space given the composeLayer.
- const hwc_rect_t& posRect = composeLayer.displayFrame;
- const hwc_frect_t& texcoordRect = composeLayer.crop;
-
- int posWidth = posRect.right - posRect.left;
- int posHeight = posRect.bottom - posRect.top;
-
- float posScaleX = float(posWidth) / dstWidth;
- float posScaleY = float(posHeight) / dstHeight;
-
- float posTranslateX = -1.0f + posScaleX + 2.0f * float(posRect.left) / dstWidth;
- float posTranslateY = -1.0f + posScaleY + 2.0f * float(posRect.top) / dstHeight;
-
- float texcoordScalX = (texcoordRect.right - texcoordRect.left) / float(cbWidth);
- float texCoordScaleY = (texcoordRect.bottom - texcoordRect.top) / float(cbHeight);
-
- float texCoordTranslateX = texcoordRect.left / float(cbWidth);
- float texCoordTranslateY = texcoordRect.top / float(cbHeight);
-
- float texcoordRotation = 0.0f;
-
- const float pi = glm::pi<float>();
-
- switch (composeLayer.transform) {
- case HWC_TRANSFORM_ROT_90:
- texcoordRotation = pi * 0.5f;
- break;
- case HWC_TRANSFORM_ROT_180:
- texcoordRotation = pi;
- break;
- case HWC_TRANSFORM_ROT_270:
- texcoordRotation = pi * 1.5f;
- break;
- case HWC_TRANSFORM_FLIP_H:
- texcoordScalX *= -1.0f;
- break;
- case HWC_TRANSFORM_FLIP_V:
- texCoordScaleY *= -1.0f;
- break;
- case HWC_TRANSFORM_FLIP_H_ROT_90:
- texcoordRotation = pi * 0.5f;
- texcoordScalX *= -1.0f;
- break;
- case HWC_TRANSFORM_FLIP_V_ROT_90:
- texcoordRotation = pi * 0.5f;
- texCoordScaleY *= -1.0f;
- break;
- default:
- break;
+CompositorVk::RenderTarget::RenderTarget(const goldfish_vk::VulkanDispatch& vk, VkDevice vkDevice,
+ VkImage vkImage, VkImageView vkImageView, uint32_t width,
+ uint32_t height, VkRenderPass vkRenderPass)
+ : m_vk(vk),
+ m_vkDevice(vkDevice),
+ m_vkImage(vkImage),
+ m_vkFramebuffer(VK_NULL_HANDLE),
+ m_width(width),
+ m_height(height) {
+ if (vkImageView == VK_NULL_HANDLE) {
+ GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+ << "CompositorVk found empty image view handle when creating RenderTarget.";
}
- ComposeLayerVk::LayerTransform layerTransform = {
- .pos = glm::translate(glm::mat4(1.0f), glm::vec3(posTranslateX, posTranslateY, 0.0f)) *
- glm::scale(glm::mat4(1.0f), glm::vec3(posScaleX, posScaleY, 1.0f)),
- .texcoord = glm::translate(glm::mat4(1.0f),
- glm::vec3(texCoordTranslateX, texCoordTranslateY, 0.0f)) *
- glm::scale(glm::mat4(1.0f), glm::vec3(texcoordScalX, texCoordScaleY, 1.0f)) *
- glm::rotate(glm::mat4(1.0f), texcoordRotation, glm::vec3(0.0f, 0.0f, 1.0f)),
+ const VkFramebufferCreateInfo framebufferCi = {
+ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
+ .flags = 0,
+ .renderPass = vkRenderPass,
+ .attachmentCount = 1,
+ .pAttachments = &vkImageView,
+ .width = width,
+ .height = height,
+ .layers = 1,
};
-
- return std::unique_ptr<ComposeLayerVk>(
- new ComposeLayerVk(vkSampler, vkImageView, layerTransform));
+ VK_CHECK(m_vk.vkCreateFramebuffer(vkDevice, &framebufferCi, nullptr, &m_vkFramebuffer));
}
-Composition::Composition(std::vector<std::unique_ptr<ComposeLayerVk>> composeLayers)
- : m_composeLayers(std::move(composeLayers)) {}
-
-const std::vector<CompositorVk::Vertex> CompositorVk::k_vertices = {
- {{-1.0f, -1.0f}, {0.0f, 0.0f}},
- {{1.0f, -1.0f}, {1.0f, 0.0f}},
- {{1.0f, 1.0f}, {1.0f, 1.0f}},
- {{-1.0f, 1.0f}, {0.0f, 1.0f}},
-};
-
-const std::vector<uint16_t> CompositorVk::k_indices = {0, 1, 2, 2, 3, 0};
+CompositorVk::RenderTarget::~RenderTarget() {
+ if (m_vkFramebuffer != VK_NULL_HANDLE) {
+ m_vk.vkDestroyFramebuffer(m_vkDevice, m_vkFramebuffer, nullptr);
+ }
+}
std::unique_ptr<CompositorVk> CompositorVk::create(
const goldfish_vk::VulkanDispatch& vk, VkDevice vkDevice, VkPhysicalDevice vkPhysicalDevice,
- VkQueue vkQueue, std::shared_ptr<android::base::Lock> queueLock, VkFormat format,
- VkImageLayout initialLayout, VkImageLayout finalLayout, uint32_t maxFramesInFlight,
- VkCommandPool commandPool, VkSampler sampler) {
+ VkQueue vkQueue, std::shared_ptr<android::base::Lock> queueLock, uint32_t queueFamilyIndex,
+ uint32_t maxFramesInFlight) {
auto res = std::unique_ptr<CompositorVk>(new CompositorVk(
- vk, vkDevice, vkPhysicalDevice, vkQueue, queueLock, commandPool, maxFramesInFlight));
- res->setUpGraphicsPipeline(format, initialLayout, finalLayout, sampler);
- res->m_vkSampler = sampler;
+ vk, vkDevice, vkPhysicalDevice, vkQueue, queueLock, queueFamilyIndex, maxFramesInFlight));
+ res->setUpCommandPool();
+ res->setUpSampler();
+ res->setUpGraphicsPipeline();
res->setUpVertexBuffers();
res->setUpUniformBuffers();
res->setUpDescriptorSets();
- res->m_currentCompositions.resize(maxFramesInFlight);
- for (auto i = 0; i < maxFramesInFlight; i++) {
- std::vector<std::unique_ptr<ComposeLayerVk>> emptyCompositionLayers;
- res->setComposition(i, std::make_unique<Composition>(std::move(emptyCompositionLayers)));
- }
+ res->setUpFences();
+ res->setUpFrameResourceFutures();
return res;
}
CompositorVk::CompositorVk(const goldfish_vk::VulkanDispatch& vk, VkDevice vkDevice,
VkPhysicalDevice vkPhysicalDevice, VkQueue vkQueue,
std::shared_ptr<android::base::Lock> queueLock,
- VkCommandPool commandPool, uint32_t maxFramesInFlight)
- : CompositorVkBase(vk, vkDevice, vkPhysicalDevice, vkQueue, queueLock, commandPool),
+ uint32_t queueFamilyIndex, uint32_t maxFramesInFlight)
+ : CompositorVkBase(vk, vkDevice, vkPhysicalDevice, vkQueue, queueLock, queueFamilyIndex,
+ maxFramesInFlight),
m_maxFramesInFlight(maxFramesInFlight),
- m_vkSampler(VK_NULL_HANDLE),
- m_currentCompositions(0),
- m_uniformStorage({VK_NULL_HANDLE, VK_NULL_HANDLE, nullptr, 0}) {
- VkPhysicalDeviceProperties physicalDeviceProperties;
- m_vk.vkGetPhysicalDeviceProperties(m_vkPhysicalDevice, &physicalDeviceProperties);
- auto alignment = physicalDeviceProperties.limits.minUniformBufferOffsetAlignment;
- m_uniformStorage.m_stride = ((sizeof(UniformBufferObject) - 1) / alignment + 1) * alignment;
-}
+ m_renderTargetCache(k_renderTargetCacheSize) {}
CompositorVk::~CompositorVk() {
+ {
+ android::base::AutoLock lock(*m_vkQueueLock);
+ VK_CHECK(vk_util::waitForVkQueueIdleWithRetry(m_vk, m_vkQueue));
+ }
m_vk.vkDestroyDescriptorPool(m_vkDevice, m_vkDescriptorPool, nullptr);
if (m_uniformStorage.m_vkDeviceMemory != VK_NULL_HANDLE) {
m_vk.vkUnmapMemory(m_vkDevice, m_uniformStorage.m_vkDeviceMemory);
@@ -175,11 +170,15 @@
m_vk.vkDestroyPipeline(m_vkDevice, m_graphicsVkPipeline, nullptr);
m_vk.vkDestroyRenderPass(m_vkDevice, m_vkRenderPass, nullptr);
m_vk.vkDestroyPipelineLayout(m_vkDevice, m_vkPipelineLayout, nullptr);
+ m_vk.vkDestroySampler(m_vkDevice, m_vkSampler, nullptr);
m_vk.vkDestroyDescriptorSetLayout(m_vkDevice, m_vkDescriptorSetLayout, nullptr);
+ m_vk.vkDestroyCommandPool(m_vkDevice, m_vkCommandPool, nullptr);
+ for (PerFrameResources& frameResources : m_frameResources) {
+ m_vk.vkDestroyFence(m_vkDevice, frameResources.m_vkFence, nullptr);
+ }
}
-void CompositorVk::setUpGraphicsPipeline(VkFormat renderTargetFormat, VkImageLayout initialLayout,
- VkImageLayout finalLayout, VkSampler sampler) {
+void CompositorVk::setUpGraphicsPipeline() {
const std::vector<uint32_t> vertSpvBuff(CompositorVkShader::compositorVertexShader,
std::end(CompositorVkShader::compositorVertexShader));
const std::vector<uint32_t> fragSpvBuff(CompositorVkShader::compositorFragmentShader,
@@ -187,31 +186,37 @@
const auto vertShaderMod = createShaderModule(m_vk, m_vkDevice, vertSpvBuff);
const auto fragShaderMod = createShaderModule(m_vk, m_vkDevice, fragSpvBuff);
- VkPipelineShaderStageCreateInfo shaderStageCis[2] = {
- {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
- .stage = VK_SHADER_STAGE_VERTEX_BIT,
- .module = vertShaderMod,
- .pName = "main"},
- {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
- .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
- .module = fragShaderMod,
- .pName = "main"}};
+ const VkPipelineShaderStageCreateInfo shaderStageCis[2] = {
+ VkPipelineShaderStageCreateInfo{
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ .stage = VK_SHADER_STAGE_VERTEX_BIT,
+ .module = vertShaderMod,
+ .pName = "main",
+ },
+ VkPipelineShaderStageCreateInfo{
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
+ .module = fragShaderMod,
+ .pName = "main",
+ },
+ };
- auto bindingDescription = Vertex::getBindingDescription();
- auto attributeDescription = Vertex::getAttributeDescription();
- VkPipelineVertexInputStateCreateInfo vertexInputStateCi = {
+ const auto vertexAttributeDescription = Vertex::getAttributeDescription();
+ const auto vertexBindingDescription = Vertex::getBindingDescription();
+ const VkPipelineVertexInputStateCreateInfo vertexInputStateCi = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = 1,
- .pVertexBindingDescriptions = &bindingDescription,
- .vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescription.size()),
- .pVertexAttributeDescriptions = attributeDescription.data()};
- VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCi = {
+ .pVertexBindingDescriptions = &vertexBindingDescription,
+ .vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexAttributeDescription.size()),
+ .pVertexAttributeDescriptions = vertexAttributeDescription.data(),
+ };
+ const VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCi = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.primitiveRestartEnable = VK_FALSE,
};
- VkPipelineViewportStateCreateInfo viewportStateCi = {
+ const VkPipelineViewportStateCreateInfo viewportStateCi = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
// The viewport state is dynamic.
@@ -221,7 +226,7 @@
.pScissors = nullptr,
};
- VkPipelineRasterizationStateCreateInfo rasterizerStateCi = {
+ const VkPipelineRasterizationStateCreateInfo rasterizerStateCi = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.depthClampEnable = VK_FALSE,
.rasterizerDiscardEnable = VK_FALSE,
@@ -232,18 +237,20 @@
.depthBiasConstantFactor = 0.0f,
.depthBiasClamp = 0.0f,
.depthBiasSlopeFactor = 0.0f,
- .lineWidth = 1.0f};
+ .lineWidth = 1.0f,
+ };
- VkPipelineMultisampleStateCreateInfo multisampleStateCi = {
+ const VkPipelineMultisampleStateCreateInfo multisampleStateCi = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
.sampleShadingEnable = VK_FALSE,
.minSampleShading = 1.0f,
.pSampleMask = nullptr,
.alphaToCoverageEnable = VK_FALSE,
- .alphaToOneEnable = VK_FALSE};
+ .alphaToOneEnable = VK_FALSE,
+ };
- VkPipelineColorBlendAttachmentState colorBlendAttachment = {
+ const VkPipelineColorBlendAttachmentState colorBlendAttachment = {
.blendEnable = VK_TRUE,
.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
@@ -252,90 +259,110 @@
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.alphaBlendOp = VK_BLEND_OP_ADD,
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
- VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT};
+ VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
+ };
- VkPipelineColorBlendStateCreateInfo colorBlendStateCi = {
+ const VkPipelineColorBlendStateCreateInfo colorBlendStateCi = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = VK_FALSE,
.attachmentCount = 1,
- .pAttachments = &colorBlendAttachment};
+ .pAttachments = &colorBlendAttachment,
+ };
- VkDynamicState dynamicStates[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
- VkPipelineDynamicStateCreateInfo dynamicStateCi = {
+ const VkDynamicState dynamicStates[] = {
+ VK_DYNAMIC_STATE_VIEWPORT,
+ VK_DYNAMIC_STATE_SCISSOR,
+ };
+ const VkPipelineDynamicStateCreateInfo dynamicStateCi = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = std::size(dynamicStates),
.pDynamicStates = dynamicStates,
};
- VkDescriptorSetLayoutBinding layoutBindings[2] = {
- {.binding = 0,
- .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
- .descriptorCount = 1,
- .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
- .pImmutableSamplers = &sampler},
- {.binding = 1,
- .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
- .descriptorCount = 1,
- .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
- .pImmutableSamplers = nullptr}};
+ const VkDescriptorSetLayoutBinding layoutBindings[2] = {
+ VkDescriptorSetLayoutBinding{
+ .binding = 0,
+ .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .descriptorCount = 1,
+ .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
+ .pImmutableSamplers = &m_vkSampler,
+ },
+ VkDescriptorSetLayoutBinding{
+ .binding = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ .descriptorCount = 1,
+ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
+ .pImmutableSamplers = nullptr,
+ },
+ };
- VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCi = {
+ const VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCi = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.bindingCount = static_cast<uint32_t>(std::size(layoutBindings)),
- .pBindings = layoutBindings};
+ .pBindings = layoutBindings,
+ };
VK_CHECK(m_vk.vkCreateDescriptorSetLayout(m_vkDevice, &descriptorSetLayoutCi, nullptr,
&m_vkDescriptorSetLayout));
- VkPipelineLayoutCreateInfo pipelineLayoutCi = {
+ const VkPipelineLayoutCreateInfo pipelineLayoutCi = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 1,
.pSetLayouts = &m_vkDescriptorSetLayout,
- .pushConstantRangeCount = 0};
+ .pushConstantRangeCount = 0,
+ };
VK_CHECK(
m_vk.vkCreatePipelineLayout(m_vkDevice, &pipelineLayoutCi, nullptr, &m_vkPipelineLayout));
- VkAttachmentDescription colorAttachment = {.format = renderTargetFormat,
- .samples = VK_SAMPLE_COUNT_1_BIT,
- .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
- .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
- .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
- .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
- .initialLayout = initialLayout,
- .finalLayout = finalLayout};
+ const VkAttachmentDescription colorAttachment = {
+ .format = VK_FORMAT_R8G8B8A8_UNORM,
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
+ .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+ .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ .initialLayout = kTargetImageInitialLayoutUsed,
+ .finalLayout = kTargetImageFinalLayoutUsed,
+ };
- VkAttachmentReference colorAttachmentRef = {.attachment = 0,
- .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
+ const VkAttachmentReference colorAttachmentRef = {
+ .attachment = 0,
+ .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ };
- VkSubpassDescription subpass = {.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
- .colorAttachmentCount = 1,
- .pColorAttachments = &colorAttachmentRef};
+ const VkSubpassDescription subpass = {
+ .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
+ .colorAttachmentCount = 1,
+ .pColorAttachments = &colorAttachmentRef,
+ };
// TODO: to support multiple layer composition, we could run the same render
// pass for multiple time. In that case, we should use explicit
// VkImageMemoryBarriers to transform the image layout instead of relying on
// renderpass to do it.
- VkSubpassDependency subpassDependency = {
+ const VkSubpassDependency subpassDependency = {
.srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0,
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = 0,
- .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT};
+ .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ };
- VkRenderPassCreateInfo renderPassCi = {.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
- .attachmentCount = 1,
- .pAttachments = &colorAttachment,
- .subpassCount = 1,
- .pSubpasses = &subpass,
- .dependencyCount = 1,
- .pDependencies = &subpassDependency};
-
+ const VkRenderPassCreateInfo renderPassCi = {
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
+ .attachmentCount = 1,
+ .pAttachments = &colorAttachment,
+ .subpassCount = 1,
+ .pSubpasses = &subpass,
+ .dependencyCount = 1,
+ .pDependencies = &subpassDependency,
+ };
VK_CHECK(m_vk.vkCreateRenderPass(m_vkDevice, &renderPassCi, nullptr, &m_vkRenderPass));
- VkGraphicsPipelineCreateInfo graphicsPipelineCi = {
+ const VkGraphicsPipelineCreateInfo graphicsPipelineCi = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = static_cast<uint32_t>(std::size(shaderStageCis)),
.pStages = shaderStageCis,
@@ -351,8 +378,8 @@
.renderPass = m_vkRenderPass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
- .basePipelineIndex = -1};
-
+ .basePipelineIndex = -1,
+ };
VK_CHECK(m_vk.vkCreateGraphicsPipelines(m_vkDevice, VK_NULL_HANDLE, 1, &graphicsPipelineCi,
nullptr, &m_graphicsVkPipeline));
@@ -360,15 +387,211 @@
m_vk.vkDestroyShaderModule(m_vkDevice, fragShaderMod, nullptr);
}
+void CompositorVk::setUpVertexBuffers() {
+ const VkDeviceSize vertexBufferSize = sizeof(Vertex) * k_vertices.size();
+ std::tie(m_vertexVkBuffer, m_vertexVkDeviceMemory) =
+ createBuffer(vertexBufferSize,
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
+ .value();
+ auto [vertexStagingBuffer, vertexStagingBufferMemory] =
+ createStagingBufferWithData(k_vertices.data(), vertexBufferSize);
+ copyBuffer(vertexStagingBuffer, m_vertexVkBuffer, vertexBufferSize);
+ m_vk.vkDestroyBuffer(m_vkDevice, vertexStagingBuffer, nullptr);
+ m_vk.vkFreeMemory(m_vkDevice, vertexStagingBufferMemory, nullptr);
+
+ VkDeviceSize indexBufferSize = sizeof(k_indices[0]) * k_indices.size();
+ auto [indexStagingBuffer, indexStagingBufferMemory] =
+ createStagingBufferWithData(k_indices.data(), indexBufferSize);
+ std::tie(m_indexVkBuffer, m_indexVkDeviceMemory) =
+ createBuffer(indexBufferSize,
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
+ .value();
+
+ copyBuffer(indexStagingBuffer, m_indexVkBuffer, indexBufferSize);
+ m_vk.vkDestroyBuffer(m_vkDevice, indexStagingBuffer, nullptr);
+ m_vk.vkFreeMemory(m_vkDevice, indexStagingBufferMemory, nullptr);
+}
+
+void CompositorVk::setUpDescriptorSets() {
+ const uint32_t descriptorSetsPerFrame = kMaxLayersPerFrame;
+ const uint32_t descriptorSetsTotal = descriptorSetsPerFrame * m_maxFramesInFlight;
+
+ const uint32_t descriptorsOfEachTypePerSet = 1;
+ const uint32_t descriptorsOfEachTypePerFrame =
+ descriptorSetsPerFrame * descriptorsOfEachTypePerSet;
+ const uint32_t descriptorsOfEachTypeTotal = descriptorsOfEachTypePerFrame * m_maxFramesInFlight;
+
+ const VkDescriptorPoolSize descriptorPoolSizes[2] = {
+ VkDescriptorPoolSize{
+ .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .descriptorCount = descriptorsOfEachTypeTotal,
+ },
+ VkDescriptorPoolSize{
+ .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ .descriptorCount = descriptorsOfEachTypeTotal,
+ }};
+ const VkDescriptorPoolCreateInfo descriptorPoolCi = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+ .flags = 0,
+ .maxSets = descriptorSetsTotal,
+ .poolSizeCount = static_cast<uint32_t>(std::size(descriptorPoolSizes)),
+ .pPoolSizes = descriptorPoolSizes,
+ };
+ VK_CHECK(
+ m_vk.vkCreateDescriptorPool(m_vkDevice, &descriptorPoolCi, nullptr, &m_vkDescriptorPool));
+
+ const std::vector<VkDescriptorSetLayout> frameDescriptorSetLayouts(descriptorSetsPerFrame,
+ m_vkDescriptorSetLayout);
+ const VkDescriptorSetAllocateInfo frameDescriptorSetAllocInfo = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+ .descriptorPool = m_vkDescriptorPool,
+ .descriptorSetCount = descriptorSetsPerFrame,
+ .pSetLayouts = frameDescriptorSetLayouts.data(),
+ };
+
+ VkDeviceSize uniformBufferOffset = 0;
+ for (uint32_t frameIndex = 0; frameIndex < m_maxFramesInFlight; ++frameIndex) {
+ PerFrameResources& frameResources = m_frameResources[frameIndex];
+ frameResources.m_layerDescriptorSets.resize(descriptorSetsPerFrame);
+
+ VK_CHECK(m_vk.vkAllocateDescriptorSets(m_vkDevice, &frameDescriptorSetAllocInfo,
+ frameResources.m_layerDescriptorSets.data()));
+
+ for (uint32_t layerIndex = 0; layerIndex < kMaxLayersPerFrame; ++layerIndex) {
+ const VkDescriptorBufferInfo bufferInfo = {
+ .buffer = m_uniformStorage.m_vkBuffer,
+ .offset = uniformBufferOffset,
+ .range = sizeof(UniformBufferBinding),
+ };
+ const VkWriteDescriptorSet descriptorSetWrite = {
+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ .dstSet = frameResources.m_layerDescriptorSets[layerIndex],
+ .dstBinding = 1,
+ .dstArrayElement = 0,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ .pBufferInfo = &bufferInfo,
+ };
+ m_vk.vkUpdateDescriptorSets(m_vkDevice, 1, &descriptorSetWrite, 0, nullptr);
+
+ uniformBufferOffset += m_uniformStorage.m_stride;
+ }
+ }
+}
+
+void CompositorVk::setUpCommandPool() {
+ const VkCommandPoolCreateInfo commandPoolCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+ .flags = 0,
+ .queueFamilyIndex = m_queueFamilyIndex,
+ };
+
+ VkCommandPool commandPool = VK_NULL_HANDLE;
+ VK_CHECK(m_vk.vkCreateCommandPool(m_vkDevice, &commandPoolCreateInfo, nullptr, &commandPool));
+ m_vkCommandPool = commandPool;
+}
+
+void CompositorVk::setUpFences() {
+ for (uint32_t frameIndex = 0; frameIndex < m_maxFramesInFlight; ++frameIndex) {
+ PerFrameResources& frameResources = m_frameResources[frameIndex];
+
+ const VkFenceCreateInfo fenceCi = {
+ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+ };
+
+ VkFence fence;
+ VK_CHECK(m_vk.vkCreateFence(m_vkDevice, &fenceCi, nullptr, &fence));
+
+ frameResources.m_vkFence = fence;
+ }
+}
+
+void CompositorVk::setUpFrameResourceFutures() {
+ for (uint32_t frameIndex = 0; frameIndex < m_maxFramesInFlight; ++frameIndex) {
+ std::shared_future<PerFrameResources*> availableFrameResourceFuture =
+ std::async(std::launch::deferred, [this, frameIndex] {
+ return &m_frameResources[frameIndex];
+ }).share();
+
+ m_availableFrameResources.push_back(std::move(availableFrameResourceFuture));
+ }
+}
+
+void CompositorVk::setUpUniformBuffers() {
+ VkPhysicalDeviceProperties physicalDeviceProperties;
+ m_vk.vkGetPhysicalDeviceProperties(m_vkPhysicalDevice, &physicalDeviceProperties);
+ const VkDeviceSize alignment = physicalDeviceProperties.limits.minUniformBufferOffsetAlignment;
+ m_uniformStorage.m_stride = ((sizeof(UniformBufferBinding) - 1) / alignment + 1) * alignment;
+
+ VkDeviceSize size = m_uniformStorage.m_stride * m_maxFramesInFlight * kMaxLayersPerFrame;
+ auto maybeBuffer =
+ createBuffer(size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
+ VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
+ auto buffer = std::make_tuple<VkBuffer, VkDeviceMemory>(VK_NULL_HANDLE, VK_NULL_HANDLE);
+ if (maybeBuffer.has_value()) {
+ buffer = maybeBuffer.value();
+ } else {
+ buffer =
+ createBuffer(size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
+ .value();
+ }
+ std::tie(m_uniformStorage.m_vkBuffer, m_uniformStorage.m_vkDeviceMemory) = buffer;
+
+ void* mapped = nullptr;
+ VK_CHECK(m_vk.vkMapMemory(m_vkDevice, m_uniformStorage.m_vkDeviceMemory, 0, size, 0, &mapped));
+
+ uint8_t* data = reinterpret_cast<uint8_t*>(mapped);
+ for (uint32_t frameIndex = 0; frameIndex < m_maxFramesInFlight; ++frameIndex) {
+ PerFrameResources& frameResources = m_frameResources[frameIndex];
+ for (uint32_t layerIndex = 0; layerIndex < kMaxLayersPerFrame; ++layerIndex) {
+ auto* layerUboStorage = reinterpret_cast<UniformBufferBinding*>(data);
+ frameResources.m_layerUboStorages.push_back(layerUboStorage);
+ data += m_uniformStorage.m_stride;
+ }
+ }
+}
+
+void CompositorVk::setUpSampler() {
+ // The texture coordinate transformation matrices for flip/rotate/etc
+ // currently depends on this being repeat.
+ constexpr const VkSamplerAddressMode kSamplerMode = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+
+ const VkSamplerCreateInfo samplerCi = {
+ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
+ .magFilter = VK_FILTER_LINEAR,
+ .minFilter = VK_FILTER_LINEAR,
+ .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
+ .addressModeU = kSamplerMode,
+ .addressModeV = kSamplerMode,
+ .addressModeW = kSamplerMode,
+ .mipLodBias = 0.0f,
+ .anisotropyEnable = VK_FALSE,
+ .maxAnisotropy = 1.0f,
+ .compareEnable = VK_FALSE,
+ .compareOp = VK_COMPARE_OP_ALWAYS,
+ .minLod = 0.0f,
+ .maxLod = 0.0f,
+ .borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK,
+ .unnormalizedCoordinates = VK_FALSE,
+ };
+ VK_CHECK(m_vk.vkCreateSampler(m_vkDevice, &samplerCi, nullptr, &m_vkSampler));
+}
+
// Create a VkBuffer and a bound VkDeviceMemory. When the specified memory type
// can't be found, return std::nullopt. When Vulkan call fails, terminate the
// program.
std::optional<std::tuple<VkBuffer, VkDeviceMemory>> CompositorVk::createBuffer(
VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags memProperty) const {
- VkBufferCreateInfo bufferCi = {.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
- .size = size,
- .usage = usage,
- .sharingMode = VK_SHARING_MODE_EXCLUSIVE};
+ const VkBufferCreateInfo bufferCi = {
+ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ .size = size,
+ .usage = usage,
+ .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+ };
VkBuffer resBuffer;
VK_CHECK(m_vk.vkCreateBuffer(m_vkDevice, &bufferCi, nullptr, &resBuffer));
VkMemoryRequirements memRequirements;
@@ -377,12 +600,15 @@
m_vk.vkGetPhysicalDeviceMemoryProperties(m_vkPhysicalDevice, &physicalMemProperties);
auto maybeMemoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, memProperty);
if (!maybeMemoryTypeIndex.has_value()) {
+ ERR("Failed to find memory type for creating buffer.");
m_vk.vkDestroyBuffer(m_vkDevice, resBuffer, nullptr);
return std::nullopt;
}
- VkMemoryAllocateInfo memAllocInfo = {.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
- .allocationSize = memRequirements.size,
- .memoryTypeIndex = maybeMemoryTypeIndex.value()};
+ const VkMemoryAllocateInfo memAllocInfo = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ .allocationSize = memRequirements.size,
+ .memoryTypeIndex = maybeMemoryTypeIndex.value(),
+ };
VkDeviceMemory resMemory;
VK_CHECK(m_vk.vkAllocateMemory(m_vkDevice, &memAllocInfo, nullptr, &resMemory));
VK_CHECK(m_vk.vkBindBufferMemory(m_vkDevice, resBuffer, resMemory, 0));
@@ -412,236 +638,465 @@
});
}
-void CompositorVk::setUpVertexBuffers() {
- const VkDeviceSize vertexBufferSize = sizeof(k_vertices[0]) * k_vertices.size();
- std::tie(m_vertexVkBuffer, m_vertexVkDeviceMemory) =
- createBuffer(vertexBufferSize,
- VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
- VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
- .value();
- auto [vertexStagingBuffer, vertexStagingBufferMemory] =
- createStagingBufferWithData(k_vertices.data(), vertexBufferSize);
- copyBuffer(vertexStagingBuffer, m_vertexVkBuffer, vertexBufferSize);
- m_vk.vkDestroyBuffer(m_vkDevice, vertexStagingBuffer, nullptr);
- m_vk.vkFreeMemory(m_vkDevice, vertexStagingBufferMemory, nullptr);
-
- VkDeviceSize indexBufferSize = sizeof(k_indices[0]) * k_indices.size();
- auto [indexStagingBuffer, indexStagingBufferMemory] =
- createStagingBufferWithData(k_indices.data(), indexBufferSize);
- std::tie(m_indexVkBuffer, m_indexVkDeviceMemory) =
- createBuffer(indexBufferSize,
- VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
- VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
- .value();
- copyBuffer(indexStagingBuffer, m_indexVkBuffer, indexBufferSize);
- m_vk.vkDestroyBuffer(m_vkDevice, indexStagingBuffer, nullptr);
- m_vk.vkFreeMemory(m_vkDevice, indexStagingBufferMemory, nullptr);
+// TODO: move this to another common CRTP helper class in vk_util.h.
+VkFormatFeatureFlags CompositorVk::getFormatFeatures(VkFormat format, VkImageTiling tiling) {
+ auto i = m_vkFormatProperties.find(format);
+ if (i == m_vkFormatProperties.end()) {
+ VkFormatProperties formatProperties;
+ m_vk.vkGetPhysicalDeviceFormatProperties(m_vkPhysicalDevice, format, &formatProperties);
+ i = m_vkFormatProperties.emplace(format, formatProperties).first;
+ }
+ const VkFormatProperties& formatProperties = i->second;
+ VkFormatFeatureFlags formatFeatures = 0;
+ if (tiling == VK_IMAGE_TILING_LINEAR) {
+ formatFeatures = formatProperties.linearTilingFeatures;
+ } else if (tiling == VK_IMAGE_TILING_OPTIMAL) {
+ formatFeatures = formatProperties.optimalTilingFeatures;
+ } else {
+ ERR("Unknown tiling:%#" PRIx64 ".", static_cast<uint64_t>(tiling));
+ }
+ return formatFeatures;
}
-// We do see a composition requests with 12 layers. (b/222700096)
-// Inside hwc2, we will ask for surfaceflinger to
-// do the composition, if the layers more than 16.
-// If we see rendering error or significant time spent on updating
-// descriptors in setComposition, we should tune this number.
-static const uint32_t kMaxLayersPerFrame = 16;
+CompositorVk::RenderTarget* CompositorVk::getOrCreateRenderTargetInfo(
+ const BorrowedImageInfoVk& imageInfo) {
+ auto* renderTargetPtr = m_renderTargetCache.get(imageInfo.id);
+ if (renderTargetPtr != nullptr) {
+ return renderTargetPtr->get();
+ }
-void CompositorVk::setUpDescriptorSets() {
- uint32_t setsPerDescriptorType = m_maxFramesInFlight * kMaxLayersPerFrame;
+ auto* renderTarget = new RenderTarget(m_vk, m_vkDevice, imageInfo.image, imageInfo.imageView,
+ imageInfo.imageCreateInfo.extent.width,
+ imageInfo.imageCreateInfo.extent.height, m_vkRenderPass);
- VkDescriptorPoolSize descriptorPoolSizes[2] = {
- {.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
- .descriptorCount = setsPerDescriptorType},
- {.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = setsPerDescriptorType}};
+ m_renderTargetCache.set(imageInfo.id, std::unique_ptr<RenderTarget>(renderTarget));
- VkDescriptorPoolCreateInfo descriptorPoolCi = {
- .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
- .flags = 0,
- .maxSets = static_cast<uint32_t>(setsPerDescriptorType),
- .poolSizeCount = static_cast<uint32_t>(std::size(descriptorPoolSizes)),
- .pPoolSizes = descriptorPoolSizes};
- VK_CHECK(
- m_vk.vkCreateDescriptorPool(m_vkDevice, &descriptorPoolCi, nullptr, &m_vkDescriptorPool));
- std::vector<VkDescriptorSetLayout> layouts(setsPerDescriptorType, m_vkDescriptorSetLayout);
- VkDescriptorSetAllocateInfo descriptorSetAllocInfo = {
- .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
- .descriptorPool = m_vkDescriptorPool,
- .descriptorSetCount = setsPerDescriptorType,
- .pSetLayouts = layouts.data()};
- m_vkDescriptorSets.resize(setsPerDescriptorType);
- VK_CHECK(m_vk.vkAllocateDescriptorSets(m_vkDevice, &descriptorSetAllocInfo,
- m_vkDescriptorSets.data()));
- for (size_t i = 0; i < setsPerDescriptorType; i++) {
- VkDescriptorBufferInfo bufferInfo = {.buffer = m_uniformStorage.m_vkBuffer,
- .offset = i * m_uniformStorage.m_stride,
- .range = sizeof(UniformBufferObject)};
- VkWriteDescriptorSet descriptorSetWrite = {
- .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
- .dstSet = m_vkDescriptorSets[i],
- .dstBinding = 1,
- .dstArrayElement = 0,
- .descriptorCount = 1,
- .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
- .pBufferInfo = &bufferInfo};
- m_vk.vkUpdateDescriptorSets(m_vkDevice, 1, &descriptorSetWrite, 0, nullptr);
+ return renderTarget;
+}
+
+bool CompositorVk::canCompositeFrom(const VkImageCreateInfo& imageCi) {
+ VkFormatFeatureFlags formatFeatures = getFormatFeatures(imageCi.format, imageCi.tiling);
+ if (!(formatFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
+ ERR("The format, %s, with tiling, %s, doesn't support the "
+ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT feature. All supported features are %s.",
+ string_VkFormat(imageCi.format), string_VkImageTiling(imageCi.tiling),
+ string_VkFormatFeatureFlags(formatFeatures).c_str());
+ return false;
+ }
+ return true;
+}
+
+bool CompositorVk::canCompositeTo(const VkImageCreateInfo& imageCi) {
+ VkFormatFeatureFlags formatFeatures = getFormatFeatures(imageCi.format, imageCi.tiling);
+ if (!(formatFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) {
+ ERR("The format, %s, with tiling, %s, doesn't support the "
+ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT feature. All supported features are %s.",
+ string_VkFormat(imageCi.format), string_VkImageTiling(imageCi.tiling),
+ string_VkFormatFeatureFlags(formatFeatures).c_str());
+ return false;
+ }
+ if (!(imageCi.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) {
+ ERR("The VkImage is not created with the VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT usage flag. "
+ "The usage flags are %s.",
+ string_VkImageUsageFlags(imageCi.usage).c_str());
+ return false;
+ }
+ if (imageCi.format != k_renderTargetFormat) {
+ ERR("The format of the image, %s, is not supported by the CompositorVk as the render "
+ "target.",
+ string_VkFormat(imageCi.format));
+ return false;
+ }
+ return true;
+}
+
+void CompositorVk::buildCompositionVk(const CompositionRequest& compositionRequest,
+ CompositionVk* compositionVk) {
+ const BorrowedImageInfoVk* targetImage = getInfoOrAbort(compositionRequest.target);
+ RenderTarget* targetImageRenderTarget = getOrCreateRenderTargetInfo(*targetImage);
+
+ const uint32_t targetWidth = targetImage->width;
+ const uint32_t targetHeight = targetImage->height;
+
+ compositionVk->targetImage = targetImage;
+ compositionVk->targetFramebuffer = targetImageRenderTarget->m_vkFramebuffer;
+
+ for (const CompositionRequestLayer& layer : compositionRequest.layers) {
+ const BorrowedImageInfoVk* sourceImage = getInfoOrAbort(layer.source);
+ if (!canCompositeFrom(sourceImage->imageCreateInfo)) {
+ continue;
+ }
+
+ const uint32_t sourceImageWidth = sourceImage->width;
+ const uint32_t sourceImageHeight = sourceImage->height;
+
+ // Calculate the posTransform and the texcoordTransform needed in the
+ // uniform of the Compositor.vert shader. The posTransform should transform
+ // the square(top = -1, bottom = 1, left = -1, right = 1) to the position
+ // where the layer should be drawn in NDC space given the layer.
+ // texcoordTransform should transform the unit square(top = 0, bottom = 1,
+ // left = 0, right = 1) to where we should sample the layer in the
+ // normalized uv space given the composeLayer.
+ const hwc_rect_t& posRect = layer.props.displayFrame;
+ const hwc_frect_t& texcoordRect = layer.props.crop;
+
+ const int posWidth = posRect.right - posRect.left;
+ const int posHeight = posRect.bottom - posRect.top;
+
+ const float posScaleX = float(posWidth) / targetWidth;
+ const float posScaleY = float(posHeight) / targetHeight;
+
+ const float posTranslateX = -1.0f + posScaleX + 2.0f * float(posRect.left) / targetWidth;
+ const float posTranslateY = -1.0f + posScaleY + 2.0f * float(posRect.top) / targetHeight;
+
+ float texCoordScaleX = (texcoordRect.right - texcoordRect.left) / float(sourceImageWidth);
+ float texCoordScaleY = (texcoordRect.bottom - texcoordRect.top) / float(sourceImageHeight);
+
+ const float texCoordTranslateX = texcoordRect.left / float(sourceImageWidth);
+ const float texCoordTranslateY = texcoordRect.top / float(sourceImageHeight);
+
+ float texcoordRotation = 0.0f;
+
+ const float pi = glm::pi<float>();
+
+ switch (layer.props.transform) {
+ case HWC_TRANSFORM_NONE:
+ break;
+ case HWC_TRANSFORM_ROT_90:
+ texcoordRotation = pi * 0.5f;
+ break;
+ case HWC_TRANSFORM_ROT_180:
+ texcoordRotation = pi;
+ break;
+ case HWC_TRANSFORM_ROT_270:
+ texcoordRotation = pi * 1.5f;
+ break;
+ case HWC_TRANSFORM_FLIP_H:
+ texCoordScaleX *= -1.0f;
+ break;
+ case HWC_TRANSFORM_FLIP_V:
+ texCoordScaleY *= -1.0f;
+ break;
+ case HWC_TRANSFORM_FLIP_H_ROT_90:
+ texcoordRotation = pi * 0.5f;
+ texCoordScaleX *= -1.0f;
+ break;
+ case HWC_TRANSFORM_FLIP_V_ROT_90:
+ texcoordRotation = pi * 0.5f;
+ texCoordScaleY *= -1.0f;
+ break;
+ default:
+ ERR("Unknown transform:%d", static_cast<int>(layer.props.transform));
+ break;
+ }
+
+ const DescriptorSetContents descriptorSetContents = {
+ .binding0 =
+ {
+ .sampledImageView = sourceImage->imageView,
+ },
+ .binding1 = {
+ .positionTransform =
+ glm::translate(glm::mat4(1.0f), glm::vec3(posTranslateX, posTranslateY, 0.0f)) *
+ glm::scale(glm::mat4(1.0f), glm::vec3(posScaleX, posScaleY, 1.0f)),
+ .texCoordTransform =
+ glm::translate(glm::mat4(1.0f),
+ glm::vec3(texCoordTranslateX, texCoordTranslateY, 0.0f)) *
+ glm::scale(glm::mat4(1.0f), glm::vec3(texCoordScaleX, texCoordScaleY, 1.0f)) *
+ glm::rotate(glm::mat4(1.0f), texcoordRotation, glm::vec3(0.0f, 0.0f, 1.0f)),
+ }};
+ compositionVk->layersDescriptorSets.descriptorSets.emplace_back(descriptorSetContents);
+ compositionVk->layersSourceImages.emplace_back(sourceImage);
}
}
-void CompositorVk::recordCommandBuffers(uint32_t renderTargetIndex, VkCommandBuffer cmdBuffer,
- const CompositorVkRenderTarget& renderTarget) {
- VkClearValue clearColor = {.color = {.float32 = {0.0f, 0.0f, 0.0f, 1.0f}}};
- VkRenderPassBeginInfo renderPassBeginInfo = {
- .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
- .renderPass = m_vkRenderPass,
- .framebuffer = renderTarget.m_vkFramebuffer,
- .renderArea = {.offset = {0, 0}, .extent = {renderTarget.m_width, renderTarget.m_height}},
- .clearValueCount = 1,
- .pClearValues = &clearColor};
- m_vk.vkCmdBeginRenderPass(cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
- m_vk.vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_graphicsVkPipeline);
- VkRect2D scissor = {
- .offset = {0, 0},
- .extent =
+CompositorVk::CompositionFinishedWaitable CompositorVk::compose(
+ const CompositionRequest& compositionRequest) {
+ CompositionVk compositionVk;
+ buildCompositionVk(compositionRequest, &compositionVk);
+
+ // Grab and wait for the next available resources.
+ if (m_availableFrameResources.empty()) {
+ GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+ << "CompositorVk failed to get PerFrameResources.";
+ }
+ auto frameResourceFuture = std::move(m_availableFrameResources.front());
+ m_availableFrameResources.pop_front();
+ PerFrameResources* frameResources = frameResourceFuture.get();
+
+ updateDescriptorSetsIfChanged(compositionVk.layersDescriptorSets, frameResources);
+
+ std::vector<VkImageMemoryBarrier> preCompositionQueueTransferBarriers;
+ std::vector<VkImageMemoryBarrier> preCompositionLayoutTransitionBarriers;
+ std::vector<VkImageMemoryBarrier> postCompositionLayoutTransitionBarriers;
+ std::vector<VkImageMemoryBarrier> postCompositionQueueTransferBarriers;
+ addNeededBarriersToUseBorrowedImage(
+ *compositionVk.targetImage, m_queueFamilyIndex, kTargetImageInitialLayoutUsed,
+ kTargetImageFinalLayoutUsed, &preCompositionQueueTransferBarriers,
+ &preCompositionLayoutTransitionBarriers, &postCompositionLayoutTransitionBarriers,
+ &postCompositionQueueTransferBarriers);
+ for (const BorrowedImageInfoVk* sourceImage : compositionVk.layersSourceImages) {
+ addNeededBarriersToUseBorrowedImage(
+ *sourceImage, m_queueFamilyIndex, kSourceImageInitialLayoutUsed,
+ kSourceImageFinalLayoutUsed, &preCompositionQueueTransferBarriers,
+ &preCompositionLayoutTransitionBarriers, &postCompositionLayoutTransitionBarriers,
+ &postCompositionQueueTransferBarriers);
+ }
+
+ VkCommandBuffer& commandBuffer = frameResources->m_vkCommandBuffer;
+ if (commandBuffer != VK_NULL_HANDLE) {
+ m_vk.vkFreeCommandBuffers(m_vkDevice, m_vkCommandPool, 1, &commandBuffer);
+ }
+
+ const VkCommandBufferAllocateInfo commandBufferAllocInfo = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+ .commandPool = m_vkCommandPool,
+ .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+ .commandBufferCount = 1,
+ };
+ VK_CHECK(m_vk.vkAllocateCommandBuffers(m_vkDevice, &commandBufferAllocInfo, &commandBuffer));
+
+ const VkCommandBufferBeginInfo beginInfo = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+ .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
+ };
+ VK_CHECK(m_vk.vkBeginCommandBuffer(commandBuffer, &beginInfo));
+
+ if (!preCompositionQueueTransferBarriers.empty()) {
+ m_vk.vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr,
+ static_cast<uint32_t>(preCompositionQueueTransferBarriers.size()),
+ preCompositionQueueTransferBarriers.data());
+ }
+ if (!preCompositionLayoutTransitionBarriers.empty()) {
+ m_vk.vkCmdPipelineBarrier(
+ commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ 0, 0, nullptr, 0, nullptr,
+ static_cast<uint32_t>(preCompositionLayoutTransitionBarriers.size()),
+ preCompositionLayoutTransitionBarriers.data());
+ }
+
+ const VkClearValue renderTargetClearColor = {
+ .color =
{
- .width = renderTarget.m_width,
- .height = renderTarget.m_height,
+ .float32 = {0.0f, 0.0f, 0.0f, 1.0f},
},
};
- m_vk.vkCmdSetScissor(cmdBuffer, 0, 1, &scissor);
- VkViewport viewport = {
+ const VkRenderPassBeginInfo renderPassBeginInfo = {
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+ .renderPass = m_vkRenderPass,
+ .framebuffer = compositionVk.targetFramebuffer,
+ .renderArea =
+ {
+ .offset =
+ {
+ .x = 0,
+ .y = 0,
+ },
+ .extent =
+ {
+ .width = compositionVk.targetImage->imageCreateInfo.extent.width,
+ .height = compositionVk.targetImage->imageCreateInfo.extent.height,
+ },
+ },
+ .clearValueCount = 1,
+ .pClearValues = &renderTargetClearColor,
+ };
+ m_vk.vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+
+ m_vk.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_graphicsVkPipeline);
+
+ const VkRect2D scissor = {
+ .offset =
+ {
+ .x = 0,
+ .y = 0,
+ },
+ .extent =
+ {
+ .width = compositionVk.targetImage->imageCreateInfo.extent.width,
+ .height = compositionVk.targetImage->imageCreateInfo.extent.height,
+ },
+ };
+ m_vk.vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
+
+ const VkViewport viewport = {
.x = 0.0f,
.y = 0.0f,
- .width = static_cast<float>(renderTarget.m_width),
- .height = static_cast<float>(renderTarget.m_height),
+ .width = static_cast<float>(compositionVk.targetImage->imageCreateInfo.extent.width),
+ .height = static_cast<float>(compositionVk.targetImage->imageCreateInfo.extent.height),
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
- m_vk.vkCmdSetViewport(cmdBuffer, 0, 1, &viewport);
- VkDeviceSize offsets[] = {0};
- m_vk.vkCmdBindVertexBuffers(cmdBuffer, 0, 1, &m_vertexVkBuffer, offsets);
- m_vk.vkCmdBindIndexBuffer(cmdBuffer, m_indexVkBuffer, 0, VK_INDEX_TYPE_UINT16);
- for (uint32_t j = 0; j < m_currentCompositions[renderTargetIndex]->m_composeLayers.size();
- ++j) {
- m_vk.vkCmdBindDescriptorSets(
- cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_vkPipelineLayout, 0, 1,
- &m_vkDescriptorSets[renderTargetIndex * kMaxLayersPerFrame + j], 0, nullptr);
- m_vk.vkCmdDrawIndexed(cmdBuffer, static_cast<uint32_t>(k_indices.size()), 1, 0, 0, 0);
+ m_vk.vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
+
+ const VkDeviceSize offsets[] = {0};
+ m_vk.vkCmdBindVertexBuffers(commandBuffer, 0, 1, &m_vertexVkBuffer, offsets);
+
+ m_vk.vkCmdBindIndexBuffer(commandBuffer, m_indexVkBuffer, 0, VK_INDEX_TYPE_UINT16);
+
+ for (int layerIndex = 0; layerIndex < compositionVk.layersSourceImages.size(); ++layerIndex) {
+ VkDescriptorSet layerDescriptorSet = frameResources->m_layerDescriptorSets[layerIndex];
+
+ m_vk.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ m_vkPipelineLayout,
+ /*firstSet=*/0,
+ /*descriptorSetCount=*/1, &layerDescriptorSet,
+ /*dynamicOffsetCount=*/0,
+ /*pDynamicOffsets=*/nullptr);
+
+ m_vk.vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(k_indices.size()), 1, 0, 0, 0);
}
- m_vk.vkCmdEndRenderPass(cmdBuffer);
-}
-void CompositorVk::setUpUniformBuffers() {
- VkDeviceSize size = m_uniformStorage.m_stride * m_maxFramesInFlight * kMaxLayersPerFrame;
- auto maybeBuffer =
- createBuffer(size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
- VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
- VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
- auto buffer = std::make_tuple<VkBuffer, VkDeviceMemory>(VK_NULL_HANDLE, VK_NULL_HANDLE);
- if (maybeBuffer.has_value()) {
- buffer = maybeBuffer.value();
- } else {
- buffer =
- createBuffer(size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
- VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
- .value();
+ m_vk.vkCmdEndRenderPass(commandBuffer);
+
+ // Insert a VkImageMemoryBarrier so that the vkCmdBlitImage in post will wait for the rendering
+ // to the render target to complete.
+ const VkImageMemoryBarrier renderTargetBarrier = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = compositionVk.targetImage->image,
+ .subresourceRange =
+ {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ };
+ m_vk.vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ /*dependencyFlags=*/0,
+ /*memoryBarrierCount=*/0,
+ /*pMemoryBarriers=*/nullptr,
+ /*bufferMemoryBarrierCount=*/0,
+ /*pBufferMemoryBarriers=*/nullptr, 1, &renderTargetBarrier);
+
+ if (!postCompositionLayoutTransitionBarriers.empty()) {
+ m_vk.vkCmdPipelineBarrier(
+ commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ 0, 0, nullptr, 0, nullptr,
+ static_cast<uint32_t>(postCompositionLayoutTransitionBarriers.size()),
+ postCompositionLayoutTransitionBarriers.data());
}
- std::tie(m_uniformStorage.m_vkBuffer, m_uniformStorage.m_vkDeviceMemory) = buffer;
- VK_CHECK(m_vk.vkMapMemory(m_vkDevice, m_uniformStorage.m_vkDeviceMemory, 0, size, 0,
- reinterpret_cast<void**>(&m_uniformStorage.m_data)));
+ if (!postCompositionQueueTransferBarriers.empty()) {
+ m_vk.vkCmdPipelineBarrier(
+ commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ 0, 0, nullptr, 0, nullptr,
+ static_cast<uint32_t>(postCompositionQueueTransferBarriers.size()),
+ postCompositionQueueTransferBarriers.data());
+ }
+
+ VK_CHECK(m_vk.vkEndCommandBuffer(commandBuffer));
+
+ VkFence composeCompleteFence = frameResources->m_vkFence;
+ VK_CHECK(m_vk.vkResetFences(m_vkDevice, 1, &composeCompleteFence));
+
+ const VkPipelineStageFlags submitWaitStages[] = {
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ };
+ const VkSubmitInfo submitInfo = {
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .waitSemaphoreCount = 0,
+ .pWaitSemaphores = nullptr,
+ .pWaitDstStageMask = submitWaitStages,
+ .commandBufferCount = 1,
+ .pCommandBuffers = &commandBuffer,
+ .signalSemaphoreCount = 0,
+ .pSignalSemaphores = nullptr,
+ };
+
+ {
+ android::base::AutoLock lock(*m_vkQueueLock);
+ VK_CHECK(m_vk.vkQueueSubmit(m_vkQueue, 1, &submitInfo, composeCompleteFence));
+ }
+
+ // Create a future that will return the PerFrameResources to the next
+ // iteration of CompostiorVk::compose() once this current composition
+ // completes.
+ std::shared_future<PerFrameResources*> composeCompleteFutureForResources =
+ std::async(std::launch::deferred, [composeCompleteFence, frameResources, this]() mutable {
+ VK_CHECK(
+ m_vk.vkWaitForFences(m_vkDevice, 1, &composeCompleteFence, VK_TRUE, UINT64_MAX));
+ return frameResources;
+ }).share();
+ m_availableFrameResources.push_back(composeCompleteFutureForResources);
+
+ // Create a future that will return once this current composition
+ // completes that can be shared outside of CompositorVk.
+ std::shared_future<void> composeCompleteFuture =
+ std::async(std::launch::deferred, [composeCompleteFutureForResources]() {
+ composeCompleteFutureForResources.get();
+ }).share();
+
+ return composeCompleteFuture;
}
-bool CompositorVk::validateQueueFamilyProperties(const VkQueueFamilyProperties& properties) {
- return properties.queueFlags & VK_QUEUE_GRAPHICS_BIT;
+void CompositorVk::onImageDestroyed(uint32_t imageId) { m_renderTargetCache.remove(imageId); }
+
+bool operator==(const CompositorVkBase::DescriptorSetContents& lhs,
+ const CompositorVkBase::DescriptorSetContents& rhs) {
+ return std::tie(lhs.binding0.sampledImageView, lhs.binding1.positionTransform,
+ lhs.binding1.texCoordTransform) == std::tie(rhs.binding0.sampledImageView,
+ rhs.binding1.positionTransform,
+ rhs.binding1.texCoordTransform);
}
-void CompositorVk::setComposition(uint32_t rtIndex, std::unique_ptr<Composition>&& composition) {
- m_currentCompositions[rtIndex] = std::move(composition);
- const auto& currentComposition = *m_currentCompositions[rtIndex];
- if (currentComposition.m_composeLayers.size() > kMaxLayersPerFrame) {
+bool operator==(const CompositorVkBase::FrameDescriptorSetsContents& lhs,
+ const CompositorVkBase::FrameDescriptorSetsContents& rhs) {
+ return lhs.descriptorSets == rhs.descriptorSets;
+}
+
+void CompositorVk::updateDescriptorSetsIfChanged(
+ const FrameDescriptorSetsContents& descriptorSetsContents, PerFrameResources* frameResources) {
+ if (frameResources->m_vkDescriptorSetsContents == descriptorSetsContents) {
+ return;
+ }
+
+ const uint32_t numRequestedLayers =
+ static_cast<uint32_t>(descriptorSetsContents.descriptorSets.size());
+ if (numRequestedLayers > kMaxLayersPerFrame) {
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
<< "CompositorVk can't compose more than " << kMaxLayersPerFrame
- << " layers. layers asked: "
- << static_cast<uint32_t>(currentComposition.m_composeLayers.size());
+ << " layers. layers asked: " << numRequestedLayers;
+ return;
}
- memset(reinterpret_cast<uint8_t*>(m_uniformStorage.m_data) +
- (rtIndex * kMaxLayersPerFrame + 0) * m_uniformStorage.m_stride,
- 0, sizeof(ComposeLayerVk::LayerTransform) * kMaxLayersPerFrame);
-
- std::vector<VkDescriptorImageInfo> imageInfos(currentComposition.m_composeLayers.size());
+ std::vector<VkDescriptorImageInfo> descriptorImageInfos(numRequestedLayers);
std::vector<VkWriteDescriptorSet> descriptorWrites;
- for (size_t i = 0; i < currentComposition.m_composeLayers.size(); ++i) {
- const auto& layer = currentComposition.m_composeLayers[i];
- if (m_vkSampler != layer->m_vkSampler) {
- GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
- << "Unsupported sampler(" << reinterpret_cast<uintptr_t>(layer->m_vkSampler)
- << ").";
- }
- imageInfos[i] =
- VkDescriptorImageInfo({.sampler = VK_NULL_HANDLE,
- .imageView = layer->m_vkImageView,
- .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL});
- const VkDescriptorImageInfo& imageInfo = imageInfos[i];
- descriptorWrites.emplace_back(
- VkWriteDescriptorSet{.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
- .dstSet = m_vkDescriptorSets[rtIndex * kMaxLayersPerFrame + i],
- .dstBinding = 0,
- .dstArrayElement = 0,
- .descriptorCount = 1,
- .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
- .pImageInfo = &imageInfo});
- memcpy(reinterpret_cast<uint8_t*>(m_uniformStorage.m_data) +
- (rtIndex * kMaxLayersPerFrame + i) * m_uniformStorage.m_stride,
- &layer->m_layerTransform, sizeof(ComposeLayerVk::LayerTransform));
+ for (uint32_t layerIndex = 0; layerIndex < numRequestedLayers; ++layerIndex) {
+ const DescriptorSetContents& layerDescriptorSetContents =
+ descriptorSetsContents.descriptorSets[layerIndex];
+
+ descriptorImageInfos[layerIndex] = VkDescriptorImageInfo{
+ // Empty as we only use immutable samplers.
+ .sampler = VK_NULL_HANDLE,
+ .imageView = layerDescriptorSetContents.binding0.sampledImageView,
+ .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ };
+
+ descriptorWrites.emplace_back(VkWriteDescriptorSet{
+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ .dstSet = frameResources->m_layerDescriptorSets[layerIndex],
+ .dstBinding = 0,
+ .dstArrayElement = 0,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .pImageInfo = &descriptorImageInfos[layerIndex],
+ });
+
+ UniformBufferBinding* layerUboStorage = frameResources->m_layerUboStorages[layerIndex];
+ *layerUboStorage = layerDescriptorSetContents.binding1;
}
+
m_vk.vkUpdateDescriptorSets(m_vkDevice, descriptorWrites.size(), descriptorWrites.data(), 0,
nullptr);
-}
-std::unique_ptr<CompositorVkRenderTarget> CompositorVk::createRenderTarget(VkImageView vkImageView,
- uint32_t width,
- uint32_t height) {
- return std::unique_ptr<CompositorVkRenderTarget>(
- new CompositorVkRenderTarget(m_vk, m_vkDevice, vkImageView, width, height, m_vkRenderPass));
+ frameResources->m_vkDescriptorSetsContents = descriptorSetsContents;
}
-
-VkVertexInputBindingDescription CompositorVk::Vertex::getBindingDescription() {
- return {
- .binding = 0, .stride = sizeof(struct Vertex), .inputRate = VK_VERTEX_INPUT_RATE_VERTEX};
-}
-
-std::array<VkVertexInputAttributeDescription, 2> CompositorVk::Vertex::getAttributeDescription() {
- return {VkVertexInputAttributeDescription{.location = 0,
- .binding = 0,
- .format = VK_FORMAT_R32G32_SFLOAT,
- .offset = offsetof(struct Vertex, pos)},
- VkVertexInputAttributeDescription{.location = 1,
- .binding = 0,
- .format = VK_FORMAT_R32G32_SFLOAT,
- .offset = offsetof(struct Vertex, texPos)}};
-}
-
-CompositorVkRenderTarget::CompositorVkRenderTarget(const goldfish_vk::VulkanDispatch& vk,
- VkDevice vkDevice, VkImageView vkImageView,
- uint32_t width, uint32_t height,
- VkRenderPass vkRenderPass)
- : m_vk(vk),
- m_vkDevice(vkDevice),
- m_vkFramebuffer(VK_NULL_HANDLE),
- m_width(width),
- m_height(height) {
- VkFramebufferCreateInfo framebufferCi = {
- .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
- .flags = 0,
- .renderPass = vkRenderPass,
- .attachmentCount = 1,
- .pAttachments = &vkImageView,
- .width = width,
- .height = height,
- .layers = 1,
- };
- VK_CHECK(m_vk.vkCreateFramebuffer(vkDevice, &framebufferCi, nullptr, &m_vkFramebuffer));
-}
-
-CompositorVkRenderTarget::~CompositorVkRenderTarget() {
- m_vk.vkDestroyFramebuffer(m_vkDevice, m_vkFramebuffer, nullptr);
-}
\ No newline at end of file
diff --git a/stream-servers/vulkan/CompositorVk.h b/stream-servers/vulkan/CompositorVk.h
index 2216c61..182076f 100644
--- a/stream-servers/vulkan/CompositorVk.h
+++ b/stream-servers/vulkan/CompositorVk.h
@@ -2,57 +2,41 @@
#define COMPOSITOR_VK_H
#include <array>
+#include <deque>
+#include <future>
#include <glm/glm.hpp>
+#include <list>
#include <memory>
#include <optional>
#include <tuple>
-#include <variant>
+#include <unordered_map>
#include <vector>
+#include "BorrowedImage.h"
+#include "BorrowedImageVk.h"
+#include "Compositor.h"
#include "Hwc2.h"
#include "base/Lock.h"
+#include "base/LruCache.h"
#include "vulkan/cereal/common/goldfish_vk_dispatch.h"
#include "vulkan/vk_util.h"
-class ComposeLayerVk {
- public:
- VkSampler m_vkSampler;
- VkImageView m_vkImageView;
- struct LayerTransform {
- glm::mat4 pos;
- glm::mat4 texcoord;
- } m_layerTransform;
+// We do see a composition requests with 12 layers. (b/222700096)
+// Inside hwc2, we will ask for surfaceflinger to
+// do the composition, if the layers more than 16.
+// If we see rendering error or significant time spent on updating
+// descriptors in setComposition, we should tune this number.
+static constexpr const uint32_t kMaxLayersPerFrame = 16;
- static std::unique_ptr<ComposeLayerVk> createFromHwc2ComposeLayer(
- VkSampler, VkImageView, const ComposeLayer&, uint32_t cbWidth, uint32_t cbHeight,
- uint32_t dstWidth, uint32_t dstHeight);
-
- private:
- ComposeLayerVk() = delete;
- explicit ComposeLayerVk(VkSampler, VkImageView, const LayerTransform&);
-};
-
-// If we want to apply transform to all layers to rotate/clip/position the
-// virtual display, we should add that functionality here.
-class Composition {
- public:
- std::vector<std::unique_ptr<ComposeLayerVk>> m_composeLayers;
-
- Composition() = delete;
- explicit Composition(std::vector<std::unique_ptr<ComposeLayerVk>> composeLayers);
-};
-
-class CompositorVkRenderTarget;
-
+// Base used to grant visibility to members to the vk_util::* helper classes.
struct CompositorVkBase
- : public vk_util::RunSingleTimeCommand<
- CompositorVkBase,
- vk_util::FindMemoryType<CompositorVkBase,
- vk_util::RecordImageLayoutTransformCommands<CompositorVkBase>>> {
+ : public vk_util::RunSingleTimeCommand<CompositorVkBase,
+ vk_util::FindMemoryType<CompositorVkBase>> {
const goldfish_vk::VulkanDispatch& m_vk;
const VkDevice m_vkDevice;
const VkPhysicalDevice m_vkPhysicalDevice;
const VkQueue m_vkQueue;
+ const uint32_t m_queueFamilyIndex;
std::shared_ptr<android::base::Lock> m_vkQueueLock;
VkDescriptorSetLayout m_vkDescriptorSetLayout;
VkPipelineLayout m_vkPipelineLayout;
@@ -63,19 +47,66 @@
VkBuffer m_indexVkBuffer;
VkDeviceMemory m_indexVkDeviceMemory;
VkDescriptorPool m_vkDescriptorPool;
- std::vector<VkDescriptorSet> m_vkDescriptorSets;
-
VkCommandPool m_vkCommandPool;
+ // TODO: create additional VkSampler-s for YCbCr layers.
+ VkSampler m_vkSampler;
+
+ // The underlying storage for all of the uniform buffer objects.
+ struct UniformBufferStorage {
+ VkBuffer m_vkBuffer = VK_NULL_HANDLE;
+ VkDeviceMemory m_vkDeviceMemory = VK_NULL_HANDLE;
+ VkDeviceSize m_stride = 0;
+ } m_uniformStorage;
+
+ // Keep in sync with vulkan/Compositor.frag.
+ struct SamplerBinding {
+ VkImageView sampledImageView = VK_NULL_HANDLE;
+ };
+
+ // Keep in sync with vulkan/Compositor.vert.
+ struct UniformBufferBinding {
+ alignas(16) glm::mat4 positionTransform;
+ alignas(16) glm::mat4 texCoordTransform;
+ };
+
+ // The cached contents of a given descriptor set.
+ struct DescriptorSetContents {
+ SamplerBinding binding0;
+ UniformBufferBinding binding1;
+ };
+
+ // The cached contents of all descriptors sets of a given frame.
+ struct FrameDescriptorSetsContents {
+ std::vector<DescriptorSetContents> descriptorSets;
+ };
+
+ friend bool operator==(const DescriptorSetContents& lhs, const DescriptorSetContents& rhs);
+
+ friend bool operator==(const FrameDescriptorSetsContents& lhs,
+ const FrameDescriptorSetsContents& rhs);
+
+ struct PerFrameResources {
+ VkFence m_vkFence = VK_NULL_HANDLE;
+ VkCommandBuffer m_vkCommandBuffer = VK_NULL_HANDLE;
+ std::vector<VkDescriptorSet> m_layerDescriptorSets;
+ // Pointers into the underlying uniform buffer storage for the uniform
+ // buffer of part of each descriptor set for each layer.
+ std::vector<UniformBufferBinding*> m_layerUboStorages;
+ std::optional<FrameDescriptorSetsContents> m_vkDescriptorSetsContents;
+ };
+ std::vector<PerFrameResources> m_frameResources;
+ std::deque<std::shared_future<PerFrameResources*>> m_availableFrameResources;
explicit CompositorVkBase(const goldfish_vk::VulkanDispatch& vk, VkDevice device,
VkPhysicalDevice physicalDevice, VkQueue queue,
std::shared_ptr<android::base::Lock> queueLock,
- VkCommandPool commandPool)
+ uint32_t queueFamilyIndex, uint32_t maxFramesInFlight)
: m_vk(vk),
m_vkDevice(device),
m_vkPhysicalDevice(physicalDevice),
m_vkQueue(queue),
m_vkQueueLock(queueLock),
+ m_queueFamilyIndex(queueFamilyIndex),
m_vkDescriptorSetLayout(VK_NULL_HANDLE),
m_vkPipelineLayout(VK_NULL_HANDLE),
m_vkRenderPass(VK_NULL_HANDLE),
@@ -85,35 +116,43 @@
m_indexVkBuffer(VK_NULL_HANDLE),
m_indexVkDeviceMemory(VK_NULL_HANDLE),
m_vkDescriptorPool(VK_NULL_HANDLE),
- m_vkDescriptorSets(0),
- m_vkCommandPool(commandPool) {}
+ m_vkCommandPool(VK_NULL_HANDLE),
+ m_vkSampler(VK_NULL_HANDLE),
+ m_frameResources(maxFramesInFlight) {}
};
-class CompositorVk : protected CompositorVkBase {
+class CompositorVk : protected CompositorVkBase, public Compositor {
public:
- static std::unique_ptr<CompositorVk> create(
- const goldfish_vk::VulkanDispatch& vk, VkDevice, VkPhysicalDevice, VkQueue,
- std::shared_ptr<android::base::Lock> queueLock, VkFormat, VkImageLayout initialLayout,
- VkImageLayout finalLayout, uint32_t maxFramesInFlight, VkCommandPool, VkSampler);
- static bool validateQueueFamilyProperties(const VkQueueFamilyProperties& properties);
+ static std::unique_ptr<CompositorVk> create(const goldfish_vk::VulkanDispatch& vk,
+ VkDevice vkDevice,
+ VkPhysicalDevice vkPhysicalDevice, VkQueue vkQueue,
+ std::shared_ptr<android::base::Lock> queueLock,
+ uint32_t queueFamilyIndex,
+ uint32_t maxFramesInFlight);
~CompositorVk();
- void recordCommandBuffers(uint32_t renderTargetIndex, VkCommandBuffer,
- const CompositorVkRenderTarget&);
- void setComposition(uint32_t i, std::unique_ptr<Composition>&& composition);
- std::unique_ptr<CompositorVkRenderTarget> createRenderTarget(VkImageView, uint32_t width,
- uint32_t height);
+
+ CompositionFinishedWaitable compose(const CompositionRequest& compositionRequest) override;
+
+ void onImageDestroyed(uint32_t imageId) override;
+
+ static bool queueSupportsComposition(const VkQueueFamilyProperties& properties) {
+ return properties.queueFlags & VK_QUEUE_GRAPHICS_BIT;
+ }
private:
explicit CompositorVk(const goldfish_vk::VulkanDispatch&, VkDevice, VkPhysicalDevice, VkQueue,
- std::shared_ptr<android::base::Lock> queueLock, VkCommandPool,
+ std::shared_ptr<android::base::Lock> queueLock, uint32_t queueFamilyIndex,
uint32_t maxFramesInFlight);
- void setUpGraphicsPipeline(VkFormat renderTargetFormat, VkImageLayout initialLayout,
- VkImageLayout finalLayout, VkSampler);
+
+ void setUpGraphicsPipeline();
void setUpVertexBuffers();
+ void setUpSampler();
void setUpDescriptorSets();
- void setUpEmptyComposition(VkFormat);
void setUpUniformBuffers();
+ void setUpCommandPool();
+ void setUpFences();
+ void setUpFrameResourceFutures();
std::optional<std::tuple<VkBuffer, VkDeviceMemory>> createBuffer(VkDeviceSize,
VkBufferUsageFlags,
@@ -122,47 +161,73 @@
VkDeviceSize size) const;
void copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize) const;
- struct UniformBufferObject {
- alignas(16) glm::mat4 pos_transform;
- alignas(16) glm::mat4 texcoord_transform;
+ VkFormatFeatureFlags getFormatFeatures(VkFormat format, VkImageTiling tiling);
+
+ // Check if the ColorBuffer can be used as a compose layer to be sampled from.
+ bool canCompositeFrom(const VkImageCreateInfo& info);
+
+ // Check if the ColorBuffer can be used as a render target of a composition.
+ bool canCompositeTo(const VkImageCreateInfo& info);
+
+ // A consolidated view of a `Compositor::CompositionRequestLayer` with only
+ // the Vulkan components needed for command recording and submission.
+ struct CompositionLayerVk {
+ VkImage image = VK_NULL_HANDLE;
+ VkImageView imageView = VK_NULL_HANDLE;
+ VkImageLayout preCompositionLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ uint32_t preCompositionQueueFamilyIndex = 0;
+ VkImageLayout postCompositionLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ uint32_t postCompositionQueueFamilyIndex = 0;
};
- struct Vertex {
- alignas(8) glm::vec2 pos;
- alignas(8) glm::vec2 texPos;
+ // A consolidated view of a `Compositor::CompositionRequest` with only
+ // the Vulkan components needed for command recording and submission.
+ struct CompositionVk {
+ const BorrowedImageInfoVk* targetImage = nullptr;
+ VkFramebuffer targetFramebuffer = VK_NULL_HANDLE;
+ std::vector<const BorrowedImageInfoVk*> layersSourceImages;
+ FrameDescriptorSetsContents layersDescriptorSets;
+ };
+ void buildCompositionVk(const CompositionRequest& compositionRequest,
+ CompositionVk* compositionVk);
- static VkVertexInputBindingDescription getBindingDescription();
- static std::array<VkVertexInputAttributeDescription, 2> getAttributeDescription();
+ void updateDescriptorSetsIfChanged(const FrameDescriptorSetsContents& contents,
+ PerFrameResources* frameResources);
+
+ class RenderTarget {
+ public:
+ ~RenderTarget();
+
+ DISALLOW_COPY_ASSIGN_AND_MOVE(RenderTarget);
+
+ private:
+ friend class CompositorVk;
+ RenderTarget(const goldfish_vk::VulkanDispatch& vk, VkDevice vkDevice, VkImage vkImage,
+ VkImageView vkImageView, uint32_t width, uint32_t height,
+ VkRenderPass vkRenderPass);
+
+ const goldfish_vk::VulkanDispatch& m_vk;
+ VkDevice m_vkDevice;
+ VkImage m_vkImage;
+ VkFramebuffer m_vkFramebuffer;
+ uint32_t m_width;
+ uint32_t m_height;
};
- static const std::vector<Vertex> k_vertices;
- static const std::vector<uint16_t> k_indices;
+ // Gets the RenderTarget used for composing into the given image if it already exists,
+ // otherwise creates it.
+ RenderTarget* getOrCreateRenderTargetInfo(const BorrowedImageInfoVk& info);
- uint32_t m_maxFramesInFlight;
- VkSampler m_vkSampler;
+ // Cached format properties used for checking if composition is supported with a given
+ // format.
+ std::unordered_map<VkFormat, VkFormatProperties> m_vkFormatProperties;
- std::vector<std::unique_ptr<Composition>> m_currentCompositions;
- struct UniformStorage {
- VkBuffer m_vkBuffer;
- VkDeviceMemory m_vkDeviceMemory;
- void* m_data;
- VkDeviceSize m_stride;
- } m_uniformStorage;
-};
+ uint32_t m_maxFramesInFlight = 0;
-class CompositorVkRenderTarget {
- public:
- ~CompositorVkRenderTarget();
-
- private:
- const goldfish_vk::VulkanDispatch& m_vk;
- VkDevice m_vkDevice;
- VkFramebuffer m_vkFramebuffer;
- uint32_t m_width;
- uint32_t m_height;
- CompositorVkRenderTarget(const goldfish_vk::VulkanDispatch&, VkDevice, VkImageView,
- uint32_t width, uint32_t height, VkRenderPass);
- friend class CompositorVk;
+ static constexpr const VkFormat k_renderTargetFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ static constexpr const uint32_t k_renderTargetCacheSize = 128;
+ // Maps from borrowed image ids to render target info.
+ android::base::LruCache<uint32_t, std::unique_ptr<RenderTarget>> m_renderTargetCache;
};
#endif /* COMPOSITOR_VK_H */
diff --git a/stream-servers/vulkan/DisplayVk.cpp b/stream-servers/vulkan/DisplayVk.cpp
index 0550c08..5c240b1 100644
--- a/stream-servers/vulkan/DisplayVk.cpp
+++ b/stream-servers/vulkan/DisplayVk.cpp
@@ -6,7 +6,6 @@
#include "host-common/GfxstreamFatalError.h"
#include "host-common/logging.h"
-#include "vulkan/VkCommonOperations.h"
#include "vulkan/VkFormatUtils.h"
#include "vulkan/vk_enum_string_helper.h"
@@ -63,7 +62,6 @@
m_swapChainVkQueueLock(swapChainVkQueueLock),
m_vkCommandPool(VK_NULL_HANDLE),
m_swapChainStateVk(nullptr),
- m_compositorVk(nullptr),
m_surfaceState(nullptr) {
// TODO(kaiyili): validate the capabilites of the passed in Vulkan
// components.
@@ -73,24 +71,6 @@
.queueFamilyIndex = m_compositorQueueFamilyIndex,
};
VK_CHECK(m_vk.vkCreateCommandPool(m_vkDevice, &commandPoolCi, nullptr, &m_vkCommandPool));
-
- VkSamplerCreateInfo samplerCi = {.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
- .magFilter = VK_FILTER_LINEAR,
- .minFilter = VK_FILTER_LINEAR,
- .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
- .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
- .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
- .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
- .mipLodBias = 0.0f,
- .anisotropyEnable = VK_FALSE,
- .maxAnisotropy = 1.0f,
- .compareEnable = VK_FALSE,
- .compareOp = VK_COMPARE_OP_ALWAYS,
- .minLod = 0.0f,
- .maxLod = 0.0f,
- .borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK,
- .unnormalizedCoordinates = VK_FALSE};
- VK_CHECK(m_vk.vkCreateSampler(m_vkDevice, &samplerCi, nullptr, &m_compositionVkSampler));
}
DisplayVk::~DisplayVk() {
@@ -103,11 +83,7 @@
VK_CHECK(vk_util::waitForVkQueueIdleWithRetry(m_vk, m_compositorVkQueue));
}
m_postResourceFuture = std::nullopt;
- m_composeResourceFuture = std::nullopt;
- m_compositorVkRenderTargets.clear();
- m_vk.vkDestroySampler(m_vkDevice, m_compositionVkSampler, nullptr);
m_surfaceState.reset();
- m_compositorVk.reset();
m_swapChainStateVk.reset();
m_vk.vkDestroyCommandPool(m_vkDevice, m_vkCommandPool, nullptr);
}
@@ -122,10 +98,6 @@
VK_CHECK(vk_util::waitForVkQueueIdleWithRetry(m_vk, m_swapChainVkQueue));
}
m_postResourceFuture = std::nullopt;
- m_composeResourceFuture = std::nullopt;
- m_compositorVkRenderTargets = std::deque<std::shared_ptr<CompositorVkRenderTarget>>(
- k_compositorVkRenderTargetCacheSize, nullptr);
- m_compositorVk.reset();
m_swapChainStateVk.reset();
if (!SwapChainStateVk::validateQueueFamilyProperties(m_vk, m_vkPhysicalDevice, surface,
@@ -150,11 +122,6 @@
}
m_swapChainStateVk =
std::make_unique<SwapChainStateVk>(m_vk, m_vkDevice, swapChainCi->mCreateInfo);
- m_compositorVk = CompositorVk::create(
- m_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue, m_compositorVkQueueLock,
- k_compositorVkRenderTargetFormat, VK_IMAGE_LAYOUT_UNDEFINED,
- VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_swapChainStateVk->getVkImageViews().size(),
- m_vkCommandPool, m_compositionVkSampler);
int numSwapChainImages = m_swapChainStateVk->getVkImages().size();
@@ -165,35 +132,24 @@
m_inFlightFrameIndex = 0;
- m_composeResourceFuture = std::async(std::launch::deferred, [this] {
- return ComposeResource::create(m_vk, m_vkDevice, m_vkCommandPool);
- });
- m_composeResourceFuture.value().wait();
auto surfaceState = std::make_unique<SurfaceState>();
surfaceState->m_height = height;
surfaceState->m_width = width;
m_surfaceState = std::move(surfaceState);
}
-std::shared_ptr<DisplayVk::DisplayBufferInfo> DisplayVk::createDisplayBuffer(
- VkImage image, const VkImageCreateInfo& vkImageCreateInfo) {
- return std::shared_ptr<DisplayBufferInfo>(
- new DisplayBufferInfo(m_vk, m_vkDevice, vkImageCreateInfo, image));
-}
-
std::tuple<bool, std::shared_future<void>> DisplayVk::post(
- std::shared_ptr<DisplayBufferInfo> displayBufferPtr) {
+ const BorrowedImageInfo* sourceImageInfo) {
auto completedFuture = std::async(std::launch::deferred, [] {}).share();
completedFuture.wait();
- if (!displayBufferPtr) {
- fprintf(stderr, "%s: warning: null ptr passed to post buffer\n", __func__);
- return std::make_tuple(true, std::move(completedFuture));
- }
+
if (!m_swapChainStateVk || !m_surfaceState) {
DISPLAY_VK_ERROR("Haven't bound to a surface, can't post ColorBuffer.");
return std::make_tuple(true, std::move(completedFuture));
}
- if (!canPost(displayBufferPtr->m_vkImageCreateInfo)) {
+
+ const auto* sourceImageInfoVk = static_cast<const BorrowedImageInfoVk*>(sourceImageInfo);
+ if (!canPost(sourceImageInfoVk->imageCreateInfo)) {
DISPLAY_VK_ERROR("Can't post ColorBuffer.");
return std::make_tuple(true, std::move(completedFuture));
}
@@ -212,37 +168,81 @@
VkCommandBuffer cmdBuff = postResource->m_vkCommandBuffer;
VK_CHECK(m_vk.vkResetCommandBuffer(cmdBuff, 0));
- VkCommandBufferBeginInfo beginInfo = {
+
+ const VkCommandBufferBeginInfo beginInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
VK_CHECK(m_vk.vkBeginCommandBuffer(cmdBuff, &beginInfo));
- VkImageMemoryBarrier presentToXferDstBarrier = {
+
+ std::vector<VkImageMemoryBarrier> preBlitQueueTransferBarriers;
+ std::vector<VkImageMemoryBarrier> preBlitLayoutTransitionBarriers;
+ std::vector<VkImageMemoryBarrier> postBlitLayoutTransitionBarriers;
+ std::vector<VkImageMemoryBarrier> postBlitQueueTransferBarriers;
+ addNeededBarriersToUseBorrowedImage(
+ *sourceImageInfoVk, m_compositorQueueFamilyIndex,
+ /*usedInitialImageLayout=*/VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ /*usedFinalImageLayout=*/VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ &preBlitQueueTransferBarriers, &preBlitLayoutTransitionBarriers,
+ &postBlitLayoutTransitionBarriers, &postBlitQueueTransferBarriers);
+ preBlitLayoutTransitionBarriers.push_back(
+ VkImageMemoryBarrier{.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = m_swapChainStateVk->getVkImages()[imageIndex],
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ }});
+ postBlitLayoutTransitionBarriers.push_back(VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
- .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
- .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
- .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = m_swapChainStateVk->getVkImages()[imageIndex],
- .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
- .baseMipLevel = 0,
- .levelCount = 1,
- .baseArrayLayer = 0,
- .layerCount = 1}};
- m_vk.vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT,
- VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1,
- &presentToXferDstBarrier);
- VkImageBlit region = {
+ .subresourceRange =
+ {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ });
+
+ if (!preBlitQueueTransferBarriers.empty()) {
+ m_vk.vkCmdPipelineBarrier(
+ cmdBuff, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr,
+ static_cast<uint32_t>(preBlitQueueTransferBarriers.size()),
+ preBlitQueueTransferBarriers.data());
+ }
+ if (!preBlitLayoutTransitionBarriers.empty()) {
+ m_vk.vkCmdPipelineBarrier(
+ cmdBuff, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr,
+ static_cast<uint32_t>(preBlitLayoutTransitionBarriers.size()),
+ preBlitLayoutTransitionBarriers.data());
+ }
+
+ const VkImageBlit region = {
.srcSubresource = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1},
.srcOffsets = {{0, 0, 0},
- {static_cast<int32_t>(displayBufferPtr->m_vkImageCreateInfo.extent.width),
- static_cast<int32_t>(displayBufferPtr->m_vkImageCreateInfo.extent.height),
- 1}},
+ {static_cast<int32_t>(sourceImageInfoVk->imageCreateInfo.extent.width),
+ static_cast<int32_t>(sourceImageInfoVk->imageCreateInfo.extent.height), 1}},
.dstSubresource = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
@@ -251,8 +251,8 @@
{static_cast<int32_t>(m_surfaceState->m_width),
static_cast<int32_t>(m_surfaceState->m_height), 1}},
};
- VkFormat displayBufferFormat = displayBufferPtr->m_vkImageCreateInfo.format;
- VkImageTiling displayBufferTiling = displayBufferPtr->m_vkImageCreateInfo.tiling;
+ VkFormat displayBufferFormat = sourceImageInfoVk->imageCreateInfo.format;
+ VkImageTiling displayBufferTiling = sourceImageInfoVk->imageCreateInfo.tiling;
VkFilter filter = VK_FILTER_NEAREST;
VkFormatFeatureFlags displayBufferFormatFeatures =
getFormatFeatures(displayBufferFormat, displayBufferTiling);
@@ -274,26 +274,23 @@
} else {
filter = VK_FILTER_LINEAR;
}
- m_vk.vkCmdBlitImage(cmdBuff, displayBufferPtr->m_vkImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ m_vk.vkCmdBlitImage(cmdBuff, sourceImageInfoVk->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
m_swapChainStateVk->getVkImages()[imageIndex],
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion, filter);
- VkImageMemoryBarrier xferDstToPresentBarrier = {
- .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
- .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
- .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
- .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
- .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
- .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .image = m_swapChainStateVk->getVkImages()[imageIndex],
- .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
- .baseMipLevel = 0,
- .levelCount = 1,
- .baseArrayLayer = 0,
- .layerCount = 1}};
- m_vk.vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT,
- VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1,
- &xferDstToPresentBarrier);
+
+ if (!postBlitLayoutTransitionBarriers.empty()) {
+ m_vk.vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr,
+ static_cast<uint32_t>(postBlitLayoutTransitionBarriers.size()),
+ postBlitLayoutTransitionBarriers.data());
+ }
+ if (!postBlitQueueTransferBarriers.empty()) {
+ m_vk.vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr,
+ static_cast<uint32_t>(postBlitQueueTransferBarriers.size()),
+ postBlitQueueTransferBarriers.data());
+ }
+
VK_CHECK(m_vk.vkEndCommandBuffer(cmdBuff));
VkFence postCompleteFence = postResource->m_swapchainImageReleaseFence;
@@ -313,13 +310,8 @@
VK_CHECK(m_vk.vkQueueSubmit(m_compositorVkQueue, 1, &submitInfo, postCompleteFence));
}
std::shared_future<std::shared_ptr<PostResource>> postResourceFuture =
- std::async(std::launch::deferred, [postCompleteFence, postResource, displayBufferPtr,
- this]() mutable {
+ std::async(std::launch::deferred, [postCompleteFence, postResource, this]() mutable {
VK_CHECK(m_vk.vkWaitForFences(m_vkDevice, 1, &postCompleteFence, VK_TRUE, UINT64_MAX));
- // Explicitly reset displayBufferPtr here to make sure the lambda actually capture
- // displayBufferPtr to correctly extend the lifetime of displayBufferPtr until the
- // rendering completes.
- displayBufferPtr.reset();
return postResource;
}).share();
m_postResourceFuture = postResourceFuture;
@@ -351,152 +343,6 @@
}).share());
}
-std::tuple<bool, std::shared_future<void>> DisplayVk::compose(
- const std::vector<ComposeLayer>& composeLayers,
- std::vector<std::shared_ptr<DisplayBufferInfo>> composeBuffers,
- std::shared_ptr<DisplayBufferInfo> targetBuffer) {
- std::shared_future<void> completedFuture = std::async(std::launch::deferred, [] {}).share();
- completedFuture.wait();
-
- if (!m_swapChainStateVk || !m_compositorVk) {
- DISPLAY_VK_ERROR("Haven't bound to a surface, can't compose color buffer.");
- // The surface hasn't been created yet, hence we don't return
- // std::nullopt to request rebinding.
- return std::make_tuple(true, std::move(completedFuture));
- }
-
- std::vector<std::unique_ptr<ComposeLayerVk>> composeLayerVks;
- for (int i = 0; i < composeLayers.size(); ++i) {
- if (composeLayers[i].cbHandle == 0) {
- // When ColorBuffer handle is 0, it's expected that no ColorBuffer
- // is not found.
- continue;
- }
- if (!composeBuffers[i]) {
- DISPLAY_VK_ERROR("warning: null ptr passed to compose buffer for layer %d.", i);
- continue;
- }
- const auto& db = *composeBuffers[i];
- if (!canCompositeFrom(db.m_vkImageCreateInfo)) {
- DISPLAY_VK_ERROR("Can't composite from a display buffer. Skip the layer.");
- continue;
- }
- auto composeLayerVk = ComposeLayerVk::createFromHwc2ComposeLayer(
- m_compositionVkSampler, composeBuffers[i]->m_vkImageView, composeLayers[i],
- composeBuffers[i]->m_vkImageCreateInfo.extent.width,
- composeBuffers[i]->m_vkImageCreateInfo.extent.height,
- targetBuffer->m_vkImageCreateInfo.extent.width,
- targetBuffer->m_vkImageCreateInfo.extent.height);
- composeLayerVks.emplace_back(std::move(composeLayerVk));
- }
-
- if (composeLayerVks.empty()) {
- return std::make_tuple(true, std::move(completedFuture));
- }
-
- if (!targetBuffer) {
- DISPLAY_VK_ERROR("warning null ptr passed to compose target.");
- return std::make_tuple(true, std::move(completedFuture));
- }
- if (!canCompositeTo(targetBuffer->m_vkImageCreateInfo)) {
- DISPLAY_VK_ERROR("Can't write the result of the composition to the display buffer.");
- return std::make_tuple(true, std::move(completedFuture));
- }
-
- std::shared_ptr<CompositorVkRenderTarget> compositorVkRenderTarget =
- targetBuffer->m_compositorVkRenderTarget.lock();
- if (!compositorVkRenderTarget) {
- compositorVkRenderTarget = m_compositorVk->createRenderTarget(
- targetBuffer->m_vkImageView, targetBuffer->m_vkImageCreateInfo.extent.width,
- targetBuffer->m_vkImageCreateInfo.extent.height);
- if (!compositorVkRenderTarget) {
- GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
- << "Failed to create CompositorVkRenderTarget for the target display buffer.";
- }
- m_compositorVkRenderTargets.pop_back();
- m_compositorVkRenderTargets.push_front(compositorVkRenderTarget);
- targetBuffer->m_compositorVkRenderTarget = compositorVkRenderTarget;
- }
-
- std::future<std::unique_ptr<ComposeResource>> composeResourceFuture =
- std::move(m_composeResourceFuture.value());
- if (!composeResourceFuture.valid()) {
- GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
- << "Invalid composeResourceFuture in m_postResourceFutures.";
- }
- std::unique_ptr<ComposeResource> composeResource = composeResourceFuture.get();
-
- if (compareAndSaveComposition(m_inFlightFrameIndex, composeLayers, composeBuffers)) {
- auto composition = std::make_unique<Composition>(std::move(composeLayerVks));
- m_compositorVk->setComposition(m_inFlightFrameIndex, std::move(composition));
- }
-
- VkCommandBuffer cmdBuff = composeResource->m_vkCommandBuffer;
- VK_CHECK(m_vk.vkResetCommandBuffer(cmdBuff, 0));
-
- VkCommandBufferBeginInfo beginInfo = {
- .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
- .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
- };
- VK_CHECK(m_vk.vkBeginCommandBuffer(cmdBuff, &beginInfo));
- m_compositorVk->recordCommandBuffers(m_inFlightFrameIndex, cmdBuff, *compositorVkRenderTarget);
- // Insert a VkImageMemoryBarrier so that the vkCmdBlitImage in post will wait for the rendering
- // to the render target to complete.
- VkImageMemoryBarrier renderTargetBarrier = {
- .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
- .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
- .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
- .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
- .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
- .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .image = targetBuffer->m_vkImage,
- .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
- .baseMipLevel = 0,
- .levelCount = 1,
- .baseArrayLayer = 0,
- .layerCount = 1}};
- m_vk.vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
- VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1,
- &renderTargetBarrier);
- VK_CHECK(m_vk.vkEndCommandBuffer(cmdBuff));
-
- VkFence composeCompleteFence = composeResource->m_composeCompleteFence;
- VK_CHECK(m_vk.vkResetFences(m_vkDevice, 1, &composeCompleteFence));
- VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_TRANSFER_BIT};
- VkSubmitInfo submitInfo = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
- .waitSemaphoreCount = 0,
- .pWaitSemaphores = nullptr,
- .pWaitDstStageMask = waitStages,
- .commandBufferCount = 1,
- .pCommandBuffers = &cmdBuff,
- .signalSemaphoreCount = 0,
- .pSignalSemaphores = nullptr};
- {
- android::base::AutoLock lock(*m_compositorVkQueueLock);
- VK_CHECK(m_vk.vkQueueSubmit(m_compositorVkQueue, 1, &submitInfo, composeCompleteFence));
- }
-
- m_composeResourceFuture =
- std::async(std::launch::deferred,
- [composeCompleteFence, composeResource = std::move(composeResource),
- composeBuffers = std::move(composeBuffers), targetBuffer, this]() mutable {
- VK_CHECK(m_vk.vkWaitForFences(m_vkDevice, 1, &composeCompleteFence, VK_TRUE,
- UINT64_MAX));
- // Explicitly clear the composeBuffers here to ensure the lambda does
- // caputure composeBuffers and correctly extend the lifetime of related
- // DisplayBufferInfo until the render completes.
- composeBuffers.clear();
- // Explicitly reset the targetBuffer here to ensure the lambda does caputure
- // targetBuffer and correctly extend the lifetime of the DisplayBufferInfo
- // until the render completes.
- targetBuffer.reset();
- return std::move(composeResource);
- });
- m_inFlightFrameIndex = (m_inFlightFrameIndex + 1) % m_swapChainStateVk->getVkImages().size();
- return post(targetBuffer);
-}
-
VkFormatFeatureFlags DisplayVk::getFormatFeatures(VkFormat format, VkImageTiling tiling) {
auto i = m_vkFormatProperties.find(format);
if (i == m_vkFormatProperties.end()) {
@@ -607,165 +453,6 @@
return true;
}
-bool DisplayVk::canCompositeFrom(const VkImageCreateInfo& imageCi) {
- VkFormatFeatureFlags formatFeatures = getFormatFeatures(imageCi.format, imageCi.tiling);
- if (!(formatFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
- DISPLAY_VK_ERROR(
- "The format, %s, with tiling, %s, doesn't support the "
- "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT feature. All supported features are %s.",
- string_VkFormat(imageCi.format), string_VkImageTiling(imageCi.tiling),
- string_VkFormatFeatureFlags(formatFeatures).c_str());
- return false;
- }
- return true;
-}
-
-bool DisplayVk::canCompositeTo(const VkImageCreateInfo& imageCi) {
- VkFormatFeatureFlags formatFeatures = getFormatFeatures(imageCi.format, imageCi.tiling);
- if (!(formatFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) {
- DISPLAY_VK_ERROR(
- "The format, %s, with tiling, %s, doesn't support the "
- "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT feature. All supported features are %s.",
- string_VkFormat(imageCi.format), string_VkImageTiling(imageCi.tiling),
- string_VkFormatFeatureFlags(formatFeatures).c_str());
- return false;
- }
- if (!(imageCi.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) {
- DISPLAY_VK_ERROR(
- "The VkImage is not created with the VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT usage flag. "
- "The usage flags are %s.",
- string_VkImageUsageFlags(imageCi.usage).c_str());
- return false;
- }
- if (imageCi.format != k_compositorVkRenderTargetFormat) {
- DISPLAY_VK_ERROR(
- "The format of the image, %s, is not supported by the CompositorVk as the render "
- "target.",
- string_VkFormat(imageCi.format));
- return false;
- }
- return true;
-}
-
-bool DisplayVk::compareAndSaveComposition(
- uint32_t renderTargetIndex, const std::vector<ComposeLayer>& composeLayers,
- const std::vector<std::shared_ptr<DisplayBufferInfo>>& composeBuffers) {
- if (!m_surfaceState) {
- GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
- << "Haven't bound to a surface, can't compare and save composition.";
- }
- auto [iPrevComposition, compositionNotFound] =
- m_surfaceState->m_prevCompositions.emplace(renderTargetIndex, 0);
- auto& prevComposition = iPrevComposition->second;
- bool compositionChanged = false;
- if (composeLayers.size() == prevComposition.size()) {
- for (int i = 0; i < composeLayers.size(); i++) {
- if (composeBuffers[i] == nullptr) {
- // If the display buffer of the current layer doesn't exist, we
- // check if the layer at the same index in the previous
- // composition doesn't exist either.
- if (prevComposition[i] == nullptr) {
- continue;
- } else {
- compositionChanged = true;
- break;
- }
- }
- if (prevComposition[i] == nullptr) {
- // If the display buffer of the current layer exists but the
- // layer at the same index in the previous composition doesn't
- // exist, the composition is changed.
- compositionChanged = true;
- break;
- }
- const auto& prevLayer = *prevComposition[i];
- const auto prevDisplayBufferPtr = prevLayer.m_displayBuffer.lock();
- // prevLayer.m_displayBuffer is a weak pointer, so if
- // prevDisplayBufferPtr is null, the color buffer
- // prevDisplayBufferPtr pointed to should have been released or
- // re-allocated, and we should consider the composition is changed.
- // If prevDisplayBufferPtr exists and it points to the same display
- // buffer as the input composeBuffers[i] we consider the composition
- // not changed.
- if (!prevDisplayBufferPtr || prevDisplayBufferPtr != composeBuffers[i]) {
- compositionChanged = true;
- break;
- }
- const auto& prevHwc2Layer = prevLayer.m_hwc2Layer;
- const auto& hwc2Layer = composeLayers[i];
- compositionChanged =
- (prevHwc2Layer.cbHandle != hwc2Layer.cbHandle) ||
- (prevHwc2Layer.composeMode != hwc2Layer.composeMode) ||
- (prevHwc2Layer.displayFrame.left != hwc2Layer.displayFrame.left) ||
- (prevHwc2Layer.displayFrame.top != hwc2Layer.displayFrame.top) ||
- (prevHwc2Layer.displayFrame.right != hwc2Layer.displayFrame.right) ||
- (prevHwc2Layer.displayFrame.bottom != hwc2Layer.displayFrame.bottom) ||
- (prevHwc2Layer.crop.left != hwc2Layer.crop.left) ||
- (prevHwc2Layer.crop.top != hwc2Layer.crop.top) ||
- (prevHwc2Layer.crop.right != hwc2Layer.crop.right) ||
- (prevHwc2Layer.crop.bottom != hwc2Layer.crop.bottom) ||
- (prevHwc2Layer.blendMode != hwc2Layer.blendMode) ||
- (prevHwc2Layer.alpha != hwc2Layer.alpha) ||
- (prevHwc2Layer.color.r != hwc2Layer.color.r) ||
- (prevHwc2Layer.color.g != hwc2Layer.color.g) ||
- (prevHwc2Layer.color.b != hwc2Layer.color.b) ||
- (prevHwc2Layer.color.a != hwc2Layer.color.a) ||
- (prevHwc2Layer.transform != hwc2Layer.transform);
- if (compositionChanged) {
- break;
- }
- }
- } else {
- compositionChanged = true;
- }
- bool needsSave = compositionNotFound || compositionChanged;
- if (needsSave) {
- prevComposition.clear();
- for (int i = 0; i < composeLayers.size(); i++) {
- if (composeBuffers[i] == nullptr) {
- prevComposition.emplace_back(nullptr);
- continue;
- }
- auto layer = std::make_unique<SurfaceState::Layer>();
- layer->m_hwc2Layer = composeLayers[i];
- layer->m_displayBuffer = composeBuffers[i];
- prevComposition.emplace_back(std::move(layer));
- }
- }
- return needsSave;
-}
-
-DisplayVk::DisplayBufferInfo::DisplayBufferInfo(const goldfish_vk::VulkanDispatch& vk,
- VkDevice vkDevice,
- const VkImageCreateInfo& vkImageCreateInfo,
- VkImage image)
- : m_vk(vk),
- m_vkDevice(vkDevice),
- m_vkImageCreateInfo(vk_make_orphan_copy(vkImageCreateInfo)),
- m_vkImage(image),
- m_vkImageView(VK_NULL_HANDLE),
- m_compositorVkRenderTarget() {
- VkImageViewCreateInfo imageViewCi = {
- .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
- .image = image,
- .viewType = VK_IMAGE_VIEW_TYPE_2D,
- .format = m_vkImageCreateInfo.format,
- .components = {.r = VK_COMPONENT_SWIZZLE_IDENTITY,
- .g = VK_COMPONENT_SWIZZLE_IDENTITY,
- .b = VK_COMPONENT_SWIZZLE_IDENTITY,
- .a = VK_COMPONENT_SWIZZLE_IDENTITY},
- .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
- .baseMipLevel = 0,
- .levelCount = 1,
- .baseArrayLayer = 0,
- .layerCount = 1}};
- VK_CHECK(m_vk.vkCreateImageView(m_vkDevice, &imageViewCi, nullptr, &m_vkImageView));
-}
-
-DisplayVk::DisplayBufferInfo::~DisplayBufferInfo() {
- m_vk.vkDestroyImageView(m_vkDevice, m_vkImageView, nullptr);
-}
-
std::shared_ptr<DisplayVk::PostResource> DisplayVk::PostResource::create(
const goldfish_vk::VulkanDispatch& vk, VkDevice vkDevice, VkCommandPool vkCommandPool) {
VkFenceCreateInfo fenceCi = {
@@ -812,39 +499,3 @@
m_vk(vk),
m_vkDevice(vkDevice),
m_vkCommandPool(vkCommandPool) {}
-
-std::unique_ptr<DisplayVk::ComposeResource> DisplayVk::ComposeResource::create(
- const goldfish_vk::VulkanDispatch& vk, VkDevice vkDevice, VkCommandPool vkCommandPool) {
- VkFenceCreateInfo fenceCi = {
- .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
- };
- VkFence fence;
- VK_CHECK(vk.vkCreateFence(vkDevice, &fenceCi, nullptr, &fence));
-
- VkCommandBuffer commandBuffer;
- VkCommandBufferAllocateInfo commandBufferAllocInfo = {
- .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
- .commandPool = vkCommandPool,
- .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
- .commandBufferCount = 1,
- };
- VK_CHECK(vk.vkAllocateCommandBuffers(vkDevice, &commandBufferAllocInfo, &commandBuffer));
-
- return std::unique_ptr<ComposeResource>(
- new ComposeResource(vk, vkDevice, vkCommandPool, fence, commandBuffer));
-}
-
-DisplayVk::ComposeResource::~ComposeResource() {
- m_vk.vkFreeCommandBuffers(m_vkDevice, m_vkCommandPool, 1, &m_vkCommandBuffer);
- m_vk.vkDestroyFence(m_vkDevice, m_composeCompleteFence, nullptr);
-}
-
-DisplayVk::ComposeResource::ComposeResource(const goldfish_vk::VulkanDispatch& vk,
- VkDevice vkDevice, VkCommandPool vkCommandPool,
- VkFence composeCompleteFence,
- VkCommandBuffer vkCommandBuffer)
- : m_composeCompleteFence(composeCompleteFence),
- m_vkCommandBuffer(vkCommandBuffer),
- m_vk(vk),
- m_vkDevice(vkDevice),
- m_vkCommandPool(vkCommandPool) {}
diff --git a/stream-servers/vulkan/DisplayVk.h b/stream-servers/vulkan/DisplayVk.h
index cae840b..89cfa5b 100644
--- a/stream-servers/vulkan/DisplayVk.h
+++ b/stream-servers/vulkan/DisplayVk.h
@@ -10,6 +10,7 @@
#include <unordered_map>
#include <unordered_set>
+#include "BorrowedImage.h"
#include "CompositorVk.h"
#include "Hwc2.h"
#include "RenderContext.h"
@@ -22,70 +23,22 @@
class DisplayVk {
public:
- class DisplayBufferInfo {
- public:
- ~DisplayBufferInfo();
-
- private:
- DisplayBufferInfo(const goldfish_vk::VulkanDispatch&, VkDevice, const VkImageCreateInfo&,
- VkImage);
-
- const goldfish_vk::VulkanDispatch& m_vk;
- VkDevice m_vkDevice;
- VkImageCreateInfo m_vkImageCreateInfo;
-
- VkImage m_vkImage;
- VkImageView m_vkImageView;
- // m_compositorVkRenderTarget will be created when the first time the ColorBuffer is used as
- // the render target of DisplayVk::compose. DisplayVk owns the m_compositorVkRenderTarget so
- // that when the CompositorVk is recreated m_compositorVkRenderTarget can be restored to
- // nullptr.
- std::weak_ptr<CompositorVkRenderTarget> m_compositorVkRenderTarget;
-
- friend class DisplayVk;
- };
DisplayVk(const goldfish_vk::VulkanDispatch&, VkPhysicalDevice,
uint32_t swapChainQueueFamilyIndex, uint32_t compositorQueueFamilyIndex, VkDevice,
VkQueue compositorVkQueue, std::shared_ptr<android::base::Lock> compositorVkQueueLock,
VkQueue swapChainVkQueue, std::shared_ptr<android::base::Lock> swapChainVkQueueLock);
~DisplayVk();
void bindToSurface(VkSurfaceKHR, uint32_t width, uint32_t height);
- // The caller is responsible to make sure the VkImage lives longer than the DisplayBufferInfo
- // created here.
- std::shared_ptr<DisplayBufferInfo> createDisplayBuffer(VkImage, const VkImageCreateInfo&);
// The first component of the returned tuple is false when the swapchain is no longer valid and
// bindToSurface() needs to be called again. When the first component is true, the second
// component of the returned tuple is a/ future that will complete when the GPU side of work
// completes. The caller is responsible to guarantee the synchronization and the layout of
- // DisplayBufferInfo::m_vkImage is VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL.
- std::tuple<bool, std::shared_future<void>> post(std::shared_ptr<DisplayBufferInfo>);
-
- // dstWidth and dstHeight describe the size of the render target the guest
- // "thinks" it composes to, essentially, the virtual display size. Note that
- // this can be different from the actual window size. The first component of
- // the returned tuple is false when the swapchain is no longer valid and
- // bindToSurface() needs to be called again. When the first component is
- // true, the second component of the returned tuple is a future that will
- // complete when the GPU side of work completes.
- std::tuple<bool, std::shared_future<void>> compose(
- const std::vector<ComposeLayer>& composeLayers,
- std::vector<std::shared_ptr<DisplayBufferInfo>> composeBuffers,
- std::shared_ptr<DisplayBufferInfo> renderTarget);
+ // ColorBufferCompositionInfo::m_vkImage is VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL.
+ std::tuple<bool, std::shared_future<void>> post(const BorrowedImageInfo* info);
private:
VkFormatFeatureFlags getFormatFeatures(VkFormat, VkImageTiling);
bool canPost(const VkImageCreateInfo&);
- // Check if the VkImage can be used as the compose layer to be sampled from.
- bool canCompositeFrom(const VkImageCreateInfo&);
- // Check if the VkImage can be used as the render target of the composition.
- bool canCompositeTo(const VkImageCreateInfo&);
- // Returns if the composition specified by the parameter is different from
- // the previous composition. If the composition is different, update the
- // previous composition stored in m_surfaceState. Must be called after
- // bindToSurface() is called.
- bool compareAndSaveComposition(
- uint32_t renderTargetIndex, const std::vector<ComposeLayer>& composeLayers,
- const std::vector<std::shared_ptr<DisplayBufferInfo>>& composeBuffers);
const goldfish_vk::VulkanDispatch& m_vk;
VkPhysicalDevice m_vkPhysicalDevice;
@@ -97,7 +50,6 @@
VkQueue m_swapChainVkQueue;
std::shared_ptr<android::base::Lock> m_swapChainVkQueueLock;
VkCommandPool m_vkCommandPool;
- VkSampler m_compositionVkSampler;
class PostResource {
public:
@@ -122,40 +74,13 @@
std::deque<std::shared_ptr<PostResource>> m_freePostResources;
std::optional<std::shared_future<std::shared_ptr<PostResource>>> m_postResourceFuture;
- class ComposeResource {
- public:
- const VkFence m_composeCompleteFence;
- const VkCommandBuffer m_vkCommandBuffer;
- static std::unique_ptr<ComposeResource> create(const goldfish_vk::VulkanDispatch&, VkDevice,
- VkCommandPool);
- ~ComposeResource();
- DISALLOW_COPY_ASSIGN_AND_MOVE(ComposeResource);
-
- private:
- ComposeResource(const goldfish_vk::VulkanDispatch&, VkDevice, VkCommandPool, VkFence,
- VkCommandBuffer);
- const goldfish_vk::VulkanDispatch& m_vk;
- const VkDevice m_vkDevice;
- const VkCommandPool m_vkCommandPool;
- };
-
int m_inFlightFrameIndex;
- std::optional<std::future<std::unique_ptr<ComposeResource>>> m_composeResourceFuture;
std::unique_ptr<SwapChainStateVk> m_swapChainStateVk;
- std::unique_ptr<CompositorVk> m_compositorVk;
- static constexpr uint32_t k_compositorVkRenderTargetCacheSize = 128;
- std::deque<std::shared_ptr<CompositorVkRenderTarget>> m_compositorVkRenderTargets;
- static constexpr VkFormat k_compositorVkRenderTargetFormat = VK_FORMAT_R8G8B8A8_UNORM;
- struct SurfaceState {
- struct Layer {
- ComposeLayer m_hwc2Layer;
- std::weak_ptr<DisplayBufferInfo> m_displayBuffer;
- };
+ struct SurfaceState {
uint32_t m_width = 0;
uint32_t m_height = 0;
- std::unordered_map<uint32_t, std::vector<std::unique_ptr<Layer>>> m_prevCompositions;
};
std::unique_ptr<SurfaceState> m_surfaceState;
diff --git a/stream-servers/vulkan/VkAndroidNativeBuffer.cpp b/stream-servers/vulkan/VkAndroidNativeBuffer.cpp
index 2cc9e54..d04a78f 100644
--- a/stream-servers/vulkan/VkAndroidNativeBuffer.cpp
+++ b/stream-servers/vulkan/VkAndroidNativeBuffer.cpp
@@ -144,7 +144,7 @@
if (colorBufferVulkanCompatible && externalMemoryCompatible &&
setupVkColorBuffer(out->colorBufferHandle, false /* not Vulkan only */,
0u /* memoryProperty */, &out->useVulkanNativeImage)) {
- releaseColorBufferFromHostComposingSync({out->colorBufferHandle});
+ releaseColorBufferForGuestUse(out->colorBufferHandle);
out->externallyBacked = true;
}
diff --git a/stream-servers/vulkan/VkCommonOperations.cpp b/stream-servers/vulkan/VkCommonOperations.cpp
index 6f5808a..0f5ca0c 100644
--- a/stream-servers/vulkan/VkCommonOperations.cpp
+++ b/stream-servers/vulkan/VkCommonOperations.cpp
@@ -1123,6 +1123,7 @@
INFO(" useDeferredCommands: %s", features->deferredCommands ? "true" : "false");
INFO(" createResourceWithRequirements: %s",
features->createResourceWithRequirements ? "true" : "false");
+ INFO(" useVulkanComposition: %s", features->useVulkanComposition ? "true" : "false");
INFO(" useVulkanNativeSwapchain: %s", features->useVulkanNativeSwapchain ? "true" : "false");
INFO(" enable guestRenderDoc: %s", features->guestRenderDoc ? "true" : "false");
sVkEmulation->deviceInfo.glInteropSupported = features->glInteropSupported;
@@ -1130,6 +1131,15 @@
sVkEmulation->useCreateResourcesWithRequirements = features->createResourceWithRequirements;
sVkEmulation->guestRenderDoc = std::move(features->guestRenderDoc);
+ if (features->useVulkanComposition) {
+ if (sVkEmulation->compositorVk) {
+ ERR("Reset VkEmulation::compositorVk.");
+ }
+ sVkEmulation->compositorVk = CompositorVk::create(
+ *sVkEmulation->ivk, sVkEmulation->device, sVkEmulation->physdev, sVkEmulation->queue,
+ sVkEmulation->queueLock, sVkEmulation->queueFamilyIndex, 3);
+ }
+
if (features->useVulkanNativeSwapchain) {
if (sVkEmulation->displayVk) {
ERR("Reset VkEmulation::displayVk.");
@@ -1152,6 +1162,7 @@
// Don't try to tear down something that did not set up completely; too risky
if (!sVkEmulation->live) return;
+ sVkEmulation->compositorVk.reset();
sVkEmulation->displayVk.reset();
freeExternalMemoryLocked(sVkEmulation->dvk, &sVkEmulation->staging.memory);
@@ -1666,6 +1677,8 @@
}
res.imageCreateInfoShallow = vk_make_orphan_copy(*imageCi);
+ res.currentLayout = res.imageCreateInfoShallow.initialLayout;
+ res.currentQueueFamilyIndex = sVkEmulation->queueFamilyIndex;
vk->vkGetImageMemoryRequirements(sVkEmulation->device, res.image, &res.memReqs);
@@ -1716,6 +1729,36 @@
return false;
}
+ const VkImageViewCreateInfo imageViewCi = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .image = res.image,
+ .viewType = VK_IMAGE_VIEW_TYPE_2D,
+ .format = res.imageCreateInfoShallow.format,
+ .components =
+ {
+ .r = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .g = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .b = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .a = VK_COMPONENT_SWIZZLE_IDENTITY,
+ },
+ .subresourceRange =
+ {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ };
+ createRes = vk->vkCreateImageView(sVkEmulation->device, &imageViewCi, nullptr, &res.imageView);
+ if (createRes != VK_SUCCESS) {
+ // LOG(VERBOSE) << "Failed to create Vulkan image for ColorBuffer "
+ // << colorBufferHandle;
+ return false;
+ }
+
if (sVkEmulation->instanceSupportsMoltenVK) {
sVkEmulation->getMTLTextureFunc(res.image, &res.mtlTexture);
if (!res.mtlTexture) {
@@ -1740,8 +1783,6 @@
if (typeIndex) *typeIndex = res.memory.typeIndex;
if (mappedPtr) *mappedPtr = res.memory.mappedPtr;
- res.ownedByHost = std::make_shared<std::atomic_bool>(true);
-
sVkEmulation->colorBuffers[colorBufferHandle] = res;
return true;
}
@@ -1762,6 +1803,7 @@
android::base::AutoLock lock(*sVkEmulation->queueLock);
VK_CHECK(vk->vkQueueWaitIdle(sVkEmulation->queue));
}
+ vk->vkDestroyImageView(sVkEmulation->device, info.imageView, nullptr);
vk->vkDestroyImage(sVkEmulation->device, info.image, nullptr);
freeExternalMemoryLocked(vk, &info.memory);
@@ -2467,6 +2509,17 @@
return res;
}
+void setColorBufferCurrentLayout(uint32_t colorBufferHandle, VkImageLayout layout) {
+ AutoLock lock(sVkEmulationLock);
+
+ auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle);
+ if (!infoPtr) {
+ VK_COMMON_ERROR("Invalid ColorBuffer handle %d.", static_cast<int>(colorBufferHandle));
+ return;
+ }
+ infoPtr->currentLayout = layout;
+}
+
// Allocate a ready to use VkCommandBuffer for queue transfer. The caller needs
// to signal the returned VkFence when the VkCommandBuffer completes.
static std::tuple<VkCommandBuffer, VkFence> allocateQueueTransferCommandBuffer_locked() {
@@ -2521,179 +2574,31 @@
return std::make_tuple(commandBuffer, fence);
}
-void acquireColorBuffersForHostComposing(const std::vector<uint32_t>& layerColorBuffers,
- uint32_t renderTargetColorBuffer) {
- if (!sVkEmulation || !sVkEmulation->live) {
- GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "Host Vulkan device lost";
- }
+const VkImageLayout kGuestUseDefaultImageLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
- std::vector<std::tuple<uint32_t, VkImageLayout>> colorBuffersAndLayouts;
- for (uint32_t layerColorBuffer : layerColorBuffers) {
- colorBuffersAndLayouts.emplace_back(
- layerColorBuffer, FrameBuffer::getFB()->getVkImageLayoutForComposeLayer());
- }
- colorBuffersAndLayouts.emplace_back(renderTargetColorBuffer,
- VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
- AutoLock lock(sVkEmulationLock);
- auto vk = sVkEmulation->dvk;
-
- std::vector<std::tuple<VkEmulation::ColorBufferInfo*, VkImageLayout>>
- colorBufferInfosAndLayouts;
- for (auto [colorBufferHandle, newLayout] : colorBuffersAndLayouts) {
- VkEmulation::ColorBufferInfo* infoPtr =
- android::base::find(sVkEmulation->colorBuffers, colorBufferHandle);
- if (!infoPtr) {
- VK_COMMON_ERROR("Invalid ColorBuffer handle %d.", static_cast<int>(colorBufferHandle));
- continue;
- }
- colorBufferInfosAndLayouts.emplace_back(infoPtr, newLayout);
- }
-
- std::vector<VkImageMemoryBarrier> queueTransferBarriers;
- std::stringstream transferredColorBuffers;
- for (auto [infoPtr, _] : colorBufferInfosAndLayouts) {
- if (infoPtr->ownedByHost->load()) {
- VK_COMMON_VERBOSE("Skipping queue transfer from guest to host for ColorBuffer(id = %d)",
- static_cast<int>(infoPtr->handle));
- continue;
- }
- VkImageMemoryBarrier queueTransferBarrier = {
- .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
- .pNext = nullptr,
- .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT,
- // VK_ACCESS_SHADER_READ_BIT for the compose layers, and VK_ACCESS_TRANSFER_READ_BIT for
- // the render target/post source.
- .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
- .oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
- .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
- .srcQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL,
- .dstQueueFamilyIndex = sVkEmulation->queueFamilyIndex,
- .image = infoPtr->image,
- .subresourceRange =
- {
- .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
- .baseMipLevel = 0,
- .levelCount = 1,
- .baseArrayLayer = 0,
- .layerCount = 1,
- },
- };
- queueTransferBarriers.emplace_back(queueTransferBarrier);
- transferredColorBuffers << infoPtr->handle << " ";
- infoPtr->ownedByHost->store(true);
- infoPtr->currentLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
- }
-
- std::vector<VkImageMemoryBarrier> layoutTransitionBarriers;
- for (auto [infoPtr, newLayout] : colorBufferInfosAndLayouts) {
- if (newLayout == VK_IMAGE_LAYOUT_UNDEFINED || infoPtr->currentLayout == newLayout) {
- continue;
- }
- VkImageMemoryBarrier layoutTransitionBarrier = {
- .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
- .pNext = nullptr,
- .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT,
- // VK_ACCESS_SHADER_READ_BIT for the compose layers, and VK_ACCESS_TRANSFER_READ_BIT for
- // the render target/post source.
- .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
- .oldLayout = infoPtr->currentLayout,
- .newLayout = newLayout,
- .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .image = infoPtr->image,
- .subresourceRange =
- {
- .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
- .baseMipLevel = 0,
- .levelCount = 1,
- .baseArrayLayer = 0,
- .layerCount = 1,
- },
- };
- layoutTransitionBarriers.emplace_back(layoutTransitionBarrier);
- infoPtr->currentLayout = newLayout;
- }
-
- auto [commandBuffer, fence] = allocateQueueTransferCommandBuffer_locked();
-
- VkCommandBufferBeginInfo beginInfo = {
- .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
- .pNext = nullptr,
- .flags = 0,
- .pInheritanceInfo = nullptr,
- };
- VK_CHECK(vk->vkBeginCommandBuffer(commandBuffer, &beginInfo));
- if (!queueTransferBarriers.empty()) {
- vk->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
- VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr,
- static_cast<uint32_t>(queueTransferBarriers.size()),
- queueTransferBarriers.data());
- }
- if (!layoutTransitionBarriers.empty()) {
- vk->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
- VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr,
- static_cast<uint32_t>(layoutTransitionBarriers.size()),
- layoutTransitionBarriers.data());
- }
- VK_CHECK(vk->vkEndCommandBuffer(commandBuffer));
-
- // We assume the host Vulkan compositor lives on the same queue, so we don't
- // need to use semaphore to synchronize with the host compositor.
- VkSubmitInfo submitInfo = {
- .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
- .pNext = nullptr,
- .waitSemaphoreCount = 0,
- .pWaitSemaphores = nullptr,
- .pWaitDstStageMask = nullptr,
- .commandBufferCount = 1,
- .pCommandBuffers = &commandBuffer,
- .signalSemaphoreCount = 0,
- .pSignalSemaphores = nullptr,
- };
- {
- std::stringstream ss;
- ss << __func__
- << ": submitting commands to issue acquire queue transfer from "
- "guest to host for ColorBuffer("
- << transferredColorBuffers.str() << ")";
- AEMU_SCOPED_TRACE(ss.str().c_str());
- android::base::AutoLock lock(*sVkEmulation->queueLock);
- VK_CHECK(vk->vkQueueSubmit(sVkEmulation->queue, 1, &submitInfo, fence));
- }
-}
-
-static VkFence doReleaseColorBufferForGuestRendering(
- const std::vector<uint32_t>& colorBufferHandles) {
+void releaseColorBufferForGuestUse(uint32_t colorBufferHandle) {
if (!sVkEmulation || !sVkEmulation->live) {
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "Host Vulkan device lost";
}
AutoLock lock(sVkEmulationLock);
- auto vk = sVkEmulation->dvk;
- std::stringstream transferredColorBuffers;
- std::vector<VkImageMemoryBarrier> layoutTransitionBarriers;
- std::vector<VkImageMemoryBarrier> queueTransferBarriers;
- for (uint32_t colorBufferHandle : colorBufferHandles) {
- auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle);
- if (!infoPtr) {
- VK_COMMON_ERROR("Invalid ColorBuffer handle %d.", static_cast<int>(colorBufferHandle));
- continue;
- }
- if (!infoPtr->ownedByHost->load()) {
- VK_COMMON_VERBOSE(
- "Skipping queue transfer from host to guest for "
- "ColorBuffer(id = %d)",
- static_cast<int>(colorBufferHandle));
- continue;
- }
- VkImageMemoryBarrier layoutTransitionBarrier = {
+ auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle);
+ if (!infoPtr) {
+ VK_COMMON_ERROR("Failed to find ColorBuffer handle %d.",
+ static_cast<int>(colorBufferHandle));
+ return;
+ }
+
+ std::optional<VkImageMemoryBarrier> layoutTransitionBarrier;
+ if (infoPtr->currentLayout != kGuestUseDefaultImageLayout) {
+ layoutTransitionBarrier = VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
.oldLayout = infoPtr->currentLayout,
- .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+ .newLayout = kGuestUseDefaultImageLayout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = infoPtr->image,
@@ -2706,17 +2611,19 @@
.layerCount = 1,
},
};
- layoutTransitionBarriers.emplace_back(layoutTransitionBarrier);
- infoPtr->currentLayout = layoutTransitionBarrier.newLayout;
+ infoPtr->currentLayout = kGuestUseDefaultImageLayout;
+ }
- VkImageMemoryBarrier queueTransferBarrier = {
+ std::optional<VkImageMemoryBarrier> queueTransferBarrier;
+ if (infoPtr->currentQueueFamilyIndex != VK_QUEUE_FAMILY_EXTERNAL) {
+ queueTransferBarrier = VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
.oldLayout = infoPtr->currentLayout,
.newLayout = infoPtr->currentLayout,
- .srcQueueFamilyIndex = sVkEmulation->queueFamilyIndex,
+ .srcQueueFamilyIndex = infoPtr->currentQueueFamilyIndex,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL,
.image = infoPtr->image,
.subresourceRange =
@@ -2728,33 +2635,40 @@
.layerCount = 1,
},
};
- queueTransferBarriers.emplace_back(queueTransferBarrier);
- transferredColorBuffers << colorBufferHandle << " ";
- infoPtr->ownedByHost->store(false);
+ infoPtr->currentQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL;
}
+ if (!layoutTransitionBarrier && !queueTransferBarrier) {
+ return;
+ }
+
+ auto vk = sVkEmulation->dvk;
auto [commandBuffer, fence] = allocateQueueTransferCommandBuffer_locked();
VK_CHECK(vk->vkResetCommandBuffer(commandBuffer, 0));
- VkCommandBufferBeginInfo beginInfo = {
+
+ const VkCommandBufferBeginInfo beginInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.pNext = nullptr,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
.pInheritanceInfo = nullptr,
};
VK_CHECK(vk->vkBeginCommandBuffer(commandBuffer, &beginInfo));
- vk->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
- VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr,
- static_cast<uint32_t>(layoutTransitionBarriers.size()),
- layoutTransitionBarriers.data());
- vk->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
- VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr,
- static_cast<uint32_t>(queueTransferBarriers.size()),
- queueTransferBarriers.data());
+
+ if (layoutTransitionBarrier) {
+ vk->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1,
+ &layoutTransitionBarrier.value());
+ }
+ if (queueTransferBarrier) {
+ vk->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1,
+ &queueTransferBarrier.value());
+ }
+
VK_CHECK(vk->vkEndCommandBuffer(commandBuffer));
- // We assume the host Vulkan compositor lives on the same queue, so we don't
- // need to use semaphore to synchronize with the host compositor.
- VkSubmitInfo submitInfo = {
+
+ const VkSubmitInfo submitInfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = nullptr,
.waitSemaphoreCount = 0,
@@ -2765,45 +2679,84 @@
.signalSemaphoreCount = 0,
.pSignalSemaphores = nullptr,
};
-
{
- std::stringstream ss;
- ss << __func__
- << ": submitting commands to issue release queue transfer from host "
- "to guest for ColorBuffer("
- << transferredColorBuffers.str() << ")";
- AEMU_SCOPED_TRACE(ss.str().c_str());
android::base::AutoLock lock(*sVkEmulation->queueLock);
VK_CHECK(vk->vkQueueSubmit(sVkEmulation->queue, 1, &submitInfo, fence));
}
- return fence;
-}
-void releaseColorBufferFromHostComposing(const std::vector<uint32_t>& colorBufferHandles) {
- doReleaseColorBufferForGuestRendering(colorBufferHandles);
-}
-
-void releaseColorBufferFromHostComposingSync(const std::vector<uint32_t>& colorBufferHandles) {
- VkFence fence = doReleaseColorBufferForGuestRendering(colorBufferHandles);
- if (!sVkEmulation || !sVkEmulation->live) {
- GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "Host Vulkan device lost";
- }
-
- AutoLock lock(sVkEmulationLock);
- auto vk = sVkEmulation->dvk;
static constexpr uint64_t ANB_MAX_WAIT_NS = 5ULL * 1000ULL * 1000ULL * 1000ULL;
VK_CHECK(vk->vkWaitForFences(sVkEmulation->device, 1, &fence, VK_TRUE, ANB_MAX_WAIT_NS));
}
-void setColorBufferCurrentLayout(uint32_t colorBufferHandle, VkImageLayout layout) {
+std::unique_ptr<BorrowedImageInfoVk> borrowColorBufferForComposition(uint32_t colorBufferHandle,
+ bool colorBufferIsTarget) {
AutoLock lock(sVkEmulationLock);
- auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle);
- if (!infoPtr) {
+ auto colorBufferInfo = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle);
+ if (!colorBufferInfo) {
VK_COMMON_ERROR("Invalid ColorBuffer handle %d.", static_cast<int>(colorBufferHandle));
- return;
+ return nullptr;
}
- infoPtr->currentLayout = layout;
+
+ auto compositorInfo = std::make_unique<BorrowedImageInfoVk>();
+ compositorInfo->id = colorBufferInfo->handle;
+ compositorInfo->width = colorBufferInfo->imageCreateInfoShallow.extent.width;
+ compositorInfo->height = colorBufferInfo->imageCreateInfoShallow.extent.height;
+ compositorInfo->image = colorBufferInfo->image;
+ compositorInfo->imageView = colorBufferInfo->imageView;
+ compositorInfo->imageCreateInfo = colorBufferInfo->imageCreateInfoShallow;
+ compositorInfo->preBorrowLayout = colorBufferInfo->currentLayout;
+ compositorInfo->preBorrowQueueFamilyIndex = colorBufferInfo->currentQueueFamilyIndex;
+ if (colorBufferIsTarget && sVkEmulation->displayVk) {
+ // Instruct the compositor to perform the layout transition after use so
+ // that it is ready to be blitted to the display.
+ compositorInfo->postBorrowQueueFamilyIndex = sVkEmulation->queueFamilyIndex;
+ compositorInfo->postBorrowLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+ } else {
+ // Instruct the compositor to perform the queue transfer release after use
+ // so that the color buffer can be acquired by the guest.
+ compositorInfo->postBorrowQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL;
+ compositorInfo->postBorrowLayout = colorBufferInfo->currentLayout;
+
+ if (compositorInfo->postBorrowLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
+ compositorInfo->postBorrowLayout = kGuestUseDefaultImageLayout;
+ }
+ }
+
+ colorBufferInfo->currentLayout = compositorInfo->postBorrowLayout;
+ colorBufferInfo->currentQueueFamilyIndex = compositorInfo->postBorrowQueueFamilyIndex;
+
+ return compositorInfo;
+}
+
+std::unique_ptr<BorrowedImageInfoVk> borrowColorBufferForDisplay(uint32_t colorBufferHandle) {
+ AutoLock lock(sVkEmulationLock);
+
+ auto colorBufferInfo = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle);
+ if (!colorBufferInfo) {
+ VK_COMMON_ERROR("Invalid ColorBuffer handle %d.", static_cast<int>(colorBufferHandle));
+ return nullptr;
+ }
+
+ auto compositorInfo = std::make_unique<BorrowedImageInfoVk>();
+ compositorInfo->id = colorBufferInfo->handle;
+ compositorInfo->width = colorBufferInfo->imageCreateInfoShallow.extent.width;
+ compositorInfo->height = colorBufferInfo->imageCreateInfoShallow.extent.height;
+ compositorInfo->image = colorBufferInfo->image;
+ compositorInfo->imageView = colorBufferInfo->imageView;
+ compositorInfo->imageCreateInfo = colorBufferInfo->imageCreateInfoShallow;
+ compositorInfo->preBorrowLayout = colorBufferInfo->currentLayout;
+ compositorInfo->preBorrowQueueFamilyIndex = sVkEmulation->queueFamilyIndex;
+
+ // Instruct the display to perform the queue transfer release after use so
+ // that the color buffer can be acquired by the guest.
+ compositorInfo->postBorrowQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL;
+ compositorInfo->postBorrowLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+
+ colorBufferInfo->currentLayout = compositorInfo->postBorrowLayout;
+ colorBufferInfo->currentQueueFamilyIndex = compositorInfo->postBorrowQueueFamilyIndex;
+
+ return compositorInfo;
}
} // namespace goldfish_vk
diff --git a/stream-servers/vulkan/VkCommonOperations.h b/stream-servers/vulkan/VkCommonOperations.h
index e987df2..a2a1bd5 100644
--- a/stream-servers/vulkan/VkCommonOperations.h
+++ b/stream-servers/vulkan/VkCommonOperations.h
@@ -22,6 +22,8 @@
#include <unordered_set>
#include <vector>
+#include "BorrowedImageVk.h"
+#include "CompositorVk.h"
#include "DisplayVk.h"
#include "base/Lock.h"
#include "base/Optional.h"
@@ -250,21 +252,18 @@
int frameworkStride;
VkImage image = VK_NULL_HANDLE;
+ VkImageView imageView = VK_NULL_HANDLE;
VkImageCreateInfo imageCreateInfoShallow = {};
VkMemoryRequirements memReqs;
VkImageLayout currentLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ uint32_t currentQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL;
bool glExported = false;
VulkanMode vulkanMode = VulkanMode::Default;
MTLTextureRef mtlTexture = nullptr;
- // shared_ptr is required so that ColorBufferInfo::ownedByHost can have
- // an uninitialized default value that is neither true or false. The
- // actual value will be set by setupVkColorBuffer when creating
- // ColorBufferInfo.
- std::shared_ptr<std::atomic_bool> ownedByHost = nullptr;
};
struct BufferInfo {
@@ -337,6 +336,8 @@
// signaled only if the command buffer completes.
std::vector<std::tuple<VkCommandBuffer, VkFence>> transferQueueCommandBufferPool;
+ std::unique_ptr<CompositorVk> compositorVk;
+
// The implementation for Vulkan native swapchain. Only initialized in initVkEmulationFeatures
// if useVulkanNativeSwapchain is set.
std::unique_ptr<DisplayVk> displayVk;
@@ -347,6 +348,7 @@
bool glInteropSupported = false;
bool deferredCommands = false;
bool createResourceWithRequirements = false;
+ bool useVulkanComposition = false;
bool useVulkanNativeSwapchain = false;
std::unique_ptr<emugl::RenderDocWithMultipleVkInstances> guestRenderDoc = nullptr;
};
@@ -409,13 +411,12 @@
VkExternalMemoryProperties transformExternalMemoryProperties_fromhost(
VkExternalMemoryProperties props, VkExternalMemoryHandleTypeFlags wantedGuestHandleType);
-void acquireColorBuffersForHostComposing(const std::vector<uint32_t>& layerColorBuffers,
- uint32_t renderTargetColorBuffer);
-
-void releaseColorBufferFromHostComposing(const std::vector<uint32_t>& colorBufferHandles);
-
-void releaseColorBufferFromHostComposingSync(const std::vector<uint32_t>& colorBufferHandles);
-
void setColorBufferCurrentLayout(uint32_t colorBufferHandle, VkImageLayout);
+void releaseColorBufferForGuestUse(uint32_t colorBufferHandle);
+
+std::unique_ptr<BorrowedImageInfoVk> borrowColorBufferForComposition(uint32_t colorBufferHandle,
+ bool colorBufferIsTarget);
+std::unique_ptr<BorrowedImageInfoVk> borrowColorBufferForDisplay(uint32_t colorBufferHandle);
+
} // namespace goldfish_vk
diff --git a/stream-servers/vulkan/VkDecoderGlobalState.cpp b/stream-servers/vulkan/VkDecoderGlobalState.cpp
index d2d44c8..70c7212 100644
--- a/stream-servers/vulkan/VkDecoderGlobalState.cpp
+++ b/stream-servers/vulkan/VkDecoderGlobalState.cpp
@@ -3234,6 +3234,17 @@
auto imageInfo = android::base::find(mImageInfo, image);
auto anbInfo = imageInfo->anbInfo;
+ if (anbInfo->useVulkanNativeImage) {
+ // vkQueueSignalReleaseImageANDROID() is only called by the Android framework's
+ // implementation of vkQueuePresentKHR(). The guest application is responsible for
+ // transitioning the image layout of the image passed to vkQueuePresentKHR() to
+ // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR before the call. If the host is using native
+ // Vulkan images where `image` is backed with the same memory as its ColorBuffer,
+ // then we need to update the tracked layout for that ColorBuffer.
+ setColorBufferCurrentLayout(anbInfo->colorBufferHandle,
+ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
+ }
+
return syncImageToColorBuffer(vk, queueInfo->queueFamilyIndex, queue, queueInfo->lock,
waitSemaphoreCount, pWaitSemaphores, pNativeFenceFd, anbInfo);
}