diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-04-28 19:44:31 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-04-28 19:44:31 +0000 |
commit | f4b6b758c3da21807c5cced3a150f68f4b6eac8c (patch) | |
tree | c14dcb0be2165778e237b7d8185dd53fa82b1993 | |
parent | 5b2ca6ff5f2f40c4cca83d9984ee26f5f7352f9a (diff) | |
parent | 7eb1b27a6873bb989c15b962047117e5fdab7932 (diff) | |
download | vulkan-cereal-f4b6b758c3da21807c5cced3a150f68f4b6eac8c.tar.gz |
Snap for 8505378 from 7eb1b27a6873bb989c15b962047117e5fdab7932 to mainline-go-uwb-release
Change-Id: Icc6eaacb70a2266235b7c97aa98bd90714e126db
25 files changed, 873 insertions, 796 deletions
diff --git a/host-common/FeatureControlDefHost.h b/host-common/FeatureControlDefHost.h index 72d3c9f5..d769e03d 100644 --- a/host-common/FeatureControlDefHost.h +++ b/host-common/FeatureControlDefHost.h @@ -74,3 +74,4 @@ FEATURE_CONTROL_ITEM(S3tcTextureSupport) FEATURE_CONTROL_ITEM(RgtcTextureSupport) FEATURE_CONTROL_ITEM(VulkanNativeSwapchain) FEATURE_CONTROL_ITEM(VirtioGpuFenceContexts) +FEATURE_CONTROL_ITEM(AsyncComposeSupport) diff --git a/host-common/HostAddressSpace_unittest.cpp b/host-common/HostAddressSpace_unittest.cpp index fbbd6abd..26cc9570 100644 --- a/host-common/HostAddressSpace_unittest.cpp +++ b/host-common/HostAddressSpace_unittest.cpp @@ -17,6 +17,7 @@ #include "host-common/AndroidAgentFactory.h" #include "host-common/address_space_device.h" #include "host-common/address_space_device.hpp" +#include "host-common/testing/MockAndroidAgentFactory.h" #include <gtest/gtest.h> @@ -24,11 +25,13 @@ namespace android { class HostAddressSpaceTest : public ::testing::Test { protected: - static void SetUpTestCase() { + static void SetUpTestSuite() { + android::emulation::injectConsoleAgents( + android::emulation::MockAndroidConsoleFactory()); emulation::goldfish_address_space_set_vm_operations(getConsoleAgents()->vm); } - static void TearDownTestCase() { } + static void TearDownTestSuite() { } void SetUp() override { mDevice = HostAddressSpaceDevice::get(); diff --git a/host-common/address_space_graphics_unittests.cpp b/host-common/address_space_graphics_unittests.cpp index 82b653df..32c76717 100644 --- a/host-common/address_space_graphics_unittests.cpp +++ b/host-common/address_space_graphics_unittests.cpp @@ -29,6 +29,7 @@ #include "host-common/address_space_device.hpp" // for goldfish... #include "host-common/address_space_graphics.h" // for AddressS... #include "host-common/address_space_graphics_types.h" // for asg_context +#include "host-common/testing/MockAndroidAgentFactory.h" #include "testing/HostAddressSpace.h" // for HostAddr... #include "host-common/globals.h" // for android_hw @@ -550,11 +551,13 @@ public: }; protected: - static void SetUpTestCase() { + static void SetUpTestSuite() { + android::emulation::injectConsoleAgents( + android::emulation::MockAndroidConsoleFactory()); goldfish_address_space_set_vm_operations(getConsoleAgents()->vm); } - static void TearDownTestCase() { } + static void TearDownTestSuite() { } void SetUp() override { android_hw->hw_gltransport_asg_writeBufferSize = 524288; diff --git a/host-common/testing/MockAndroidAgentFactory.cpp b/host-common/testing/MockAndroidAgentFactory.cpp index a17783ad..e0ac989d 100644 --- a/host-common/testing/MockAndroidAgentFactory.cpp +++ b/host-common/testing/MockAndroidAgentFactory.cpp @@ -17,14 +17,6 @@ #include "gtest/gtest.h" -#ifdef _WIN32 -using android::emulation::WindowsFlags; -bool WindowsFlags::sIsParentProcess = true; -HANDLE WindowsFlags::sChildRead = nullptr; -HANDLE WindowsFlags::sChildWrite = nullptr; -char WindowsFlags::sFileLockPath[MAX_PATH] = {}; -#endif - extern "C" const QAndroidVmOperations* const gMockQAndroidVmOperations; extern "C" const QAndroidEmulatorWindowAgent* const gMockQAndroidEmulatorWindowAgent; @@ -50,30 +42,3 @@ MockAndroidConsoleFactory::android_get_QAndroidEmulatorWindowAgent() const { } } // namespace emulation } // namespace android - - -// The gtest entry point that will configure the mock agents. -int main(int argc, char** argv) { -#ifdef _WIN32 -#define _READ_STR "--read" -#define _WRITE_STR "--write" -#define _FILE_PATH_STR "--file-lock-path" - for (int i = 1; i < argc; i++) { - if (!strcmp("--child", argv[i])) { - WindowsFlags::sIsParentProcess = false; - } else if (!strncmp(_READ_STR, argv[i], strlen(_READ_STR))) { - sscanf(argv[i], _READ_STR "=%p", &WindowsFlags::sChildRead); - } else if (!strncmp(_WRITE_STR, argv[i], strlen(_WRITE_STR))) { - sscanf(argv[i], _WRITE_STR "=%p", &WindowsFlags::sChildWrite); - } else if (!strncmp(_FILE_PATH_STR, argv[i], strlen(_FILE_PATH_STR))) { - sscanf(argv[i], _FILE_PATH_STR "=%s", - WindowsFlags::sFileLockPath); - } - } -#endif - ::testing::InitGoogleTest(&argc, argv); - fprintf(stderr, " -- Injecting mock agents. -- \n"); - android::emulation::injectConsoleAgents( - android::emulation::MockAndroidConsoleFactory()); - return RUN_ALL_TESTS(); -} diff --git a/host-common/testing/MockAndroidAgentFactory.h b/host-common/testing/MockAndroidAgentFactory.h index 8f931d10..b37afc63 100644 --- a/host-common/testing/MockAndroidAgentFactory.h +++ b/host-common/testing/MockAndroidAgentFactory.h @@ -13,10 +13,6 @@ // limitations under the License. #pragma once -#ifdef _WIN32 -#include <windows.h> -#endif - #include <stdio.h> #include "host-common/AndroidAgentFactory.h" @@ -43,17 +39,5 @@ public: }; - -#ifdef _WIN32 -// A set of flags that are only relevant for windows based unit tests -class WindowsFlags { - public: - static bool sIsParentProcess; - static HANDLE sChildRead; - static HANDLE sChildWrite; - static char sFileLockPath[MAX_PATH]; -}; -#endif - } // namespace emulation } // namespace android diff --git a/scripts/print_gfx_logs/command_printer.py b/scripts/print_gfx_logs/command_printer.py index 37858f29..0f051eab 100644 --- a/scripts/print_gfx_logs/command_printer.py +++ b/scripts/print_gfx_logs/command_printer.py @@ -1,5 +1,6 @@ import io import textwrap +from typing import Dict import vulkan_printer @@ -31,7 +32,9 @@ class CommandPrinter: pretty_printer(self, indent=4) # Check that we processed all the bytes, otherwise there's probably a bug in the pretty printing logic if self.data.tell() != len(self.data.getbuffer()): - raise BufferError("Not all data was decoded.") + raise BufferError( + "Not all data was decoded. Decoded {} bytes but expected {}".format( + self.data.tell(), len(self.data.getbuffer()))) except Exception as ex: print("Error while processing {}: {}".format(self.cmd_name(), repr(ex))) print("Command raw data:") @@ -53,12 +56,12 @@ class CommandPrinter: for l in lines: print(l) - def read_int(self, num_bytes: int) -> int: + def read_int(self, num_bytes: int, signed: bool = False) -> int: assert num_bytes == 4 or num_bytes == 8 buf = self.data.read(num_bytes) if len(buf) != num_bytes: raise EOFError("Unexpectly reached the end of the buffer") - return int.from_bytes(buf, byteorder='little', signed=False) + return int.from_bytes(buf, byteorder='little', signed=signed) def write(self, msg: str, indent: int): """Prints a string at a given indentation level""" @@ -66,13 +69,18 @@ class CommandPrinter: assert type(indent) == int and indent >= 0 print(" " * indent + msg, end='') - def write_int(self, field_name: str, num_bytes: int, indent: int, value: int = None): + def write_int(self, + field_name: str, + num_bytes: int, + indent: int, + signed: bool = False, + value: int = None): """Reads the next 32 or 64 bytes integer from the data stream and prints it""" if value is None: - value = self.read_int(num_bytes) + value = self.read_int(num_bytes, signed) self.write("{}: {}\n".format(field_name, value), indent) - def write_enum(self, field_name: str, enum: dict[int, str], indent: int, value: int = None): + def write_enum(self, field_name: str, enum: Dict[int, str], indent: int, value: int = None): """Reads the next 32-byte int from the data stream and prints it as an enum""" if value is None: value = self.read_int(4) @@ -95,9 +103,20 @@ class CommandPrinter: self.write("{}:\n".format(field_name), indent) struct_fn(self, indent + 1) - def write_repeated(self, count_name: str, field_name: str, struct_fn, indent: int): - """Reads and prints repeated structs, with a 32-byte count field followed by the struct data""" + def write_repeated(self, + count_name: str, + field_name: str, + struct_fn, + indent: int, + pointer_name: str = None): + """ + Reads and prints repeated structs, with a 32-byte count field followed by the struct data. + If pointer_name is not None, reads an additional 64-bit pointer within the repeated block + before reading repeated data. + """ count = self.read_int(4) + if pointer_name is not None: + self.write_int(pointer_name, 8, indent) assert count < 1000, "count too large: {}".format(count) # Sanity check that we haven't read garbage data self.write_int(count_name, 4, indent, value=count) for i in range(0, count): diff --git a/scripts/print_gfx_logs/vulkan_printer.py b/scripts/print_gfx_logs/vulkan_printer.py index 2fe69f0b..bbddce88 100644 --- a/scripts/print_gfx_logs/vulkan_printer.py +++ b/scripts/print_gfx_logs/vulkan_printer.py @@ -4,18 +4,20 @@ # Actually right now it is written by hand - TODO(gregschlom): auto-generate this ##################################################################################################### +def OP_vkCmdBeginRenderPass(printer, indent: int): + printer.write_struct("pRenderPassBegin", struct_VkRenderPassBeginInfo, indent) + printer.write_enum("contents", VkSubpassContents, indent) + def OP_vkCmdBindPipeline(printer, indent: int): printer.write_enum("pipelineBindPoint", VkPipelineBindPoint, indent) printer.write_int("pipeline", 8, indent) - def OP_vkCmdCopyBufferToImage(printer, indent: int): printer.write_int("srcBuffer", 8, indent) printer.write_int("dstImage", 8, indent) printer.write_enum("dstImageLayout", VkImageLayout, indent) printer.write_repeated("regionCount", "pRegions", struct_VkBufferImageCopy, indent) - def OP_vkCmdPipelineBarrier(printer, indent: int): printer.write_int("srcStageMask", 4, indent) printer.write_int("dstStageMask", 4, indent) @@ -44,6 +46,12 @@ def struct_VkBufferMemoryBarrier(printer, indent: int): printer.write_int("offset", 8, indent) printer.write_int("size", 8, indent) +def struct_VkClearValue(printer, indent: int): + printer.write_struct("color", struct_VkClearColorValue, indent) + +def struct_VkClearColorValue(printer, indent: int): + printer.write("0x{:04X}, 0x{:04X}, 0x{:04X}, 0x{:04X}\n".format( + printer.read_int(4), printer.read_int(4), printer.read_int(4), printer.read_int(4)), indent) def struct_VkImageMemoryBarrier(printer, indent: int): printer.write_stype_and_pnext("VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER", indent) @@ -56,6 +64,9 @@ def struct_VkImageMemoryBarrier(printer, indent: int): printer.write_int("image", 8, indent) printer.write_struct("subresourceRange", struct_VkImageSubresourceRange, indent) +def struct_VkExtent2D(printer, indent: int): + printer.write_int("width", 4, indent) + printer.write_int("height", 4, indent) def struct_VkImageSubresourceLayers(printer, indent: int): printer.write_int("aspectMask", 4, indent) @@ -63,7 +74,6 @@ def struct_VkImageSubresourceLayers(printer, indent: int): printer.write_int("baseArrayLayer", 4, indent) printer.write_int("layerCount", 4, indent) - def struct_VkImageSubresourceRange(printer, indent: int): printer.write_int("aspectMask", 4, indent) printer.write_int("baseMipLevel", 4, indent) @@ -71,17 +81,29 @@ def struct_VkImageSubresourceRange(printer, indent: int): printer.write_int("baseArrayLayer", 4, indent) printer.write_int("layerCount", 4, indent) - def struct_VkMemoryBarrier(printer, indent: int): printer.write_stype_and_pnext("VK_STRUCTURE_TYPE_MEMORY_BARRIER", indent) printer.write_int("srcAccessMask", 4, indent) printer.write_int("dstAccessMask", 4, indent) +def struct_VkOffset2D(printer, indent: int): + printer.write_int("x", 4, indent, signed=True) + printer.write_int("y", 4, indent, signed=True) def struct_VkOffset3D(printer, indent: int): printer.write("x: {}, y: {}, z: {}\n".format(printer.read_int(4), printer.read_int(4), printer.read_int(4)), indent) +def struct_VkRect2D(printer, indent: int): + printer.write_struct("offset", struct_VkOffset2D, indent) + printer.write_struct("extent", struct_VkExtent2D, indent) +def struct_VkRenderPassBeginInfo(printer, indent: int): + printer.write_stype_and_pnext("VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO", indent) + printer.write_int("renderPass", 8, indent) + printer.write_int("framebuffer", 8, indent) + printer.write_struct("renderArea", struct_VkRect2D, indent) + printer.write_repeated("clearValueCount", "pClearValues", struct_VkClearValue, indent, + "pClearValuesPtr") VkImageLayout = { 0: "VK_IMAGE_LAYOUT_UNDEFINED", 1: "VK_IMAGE_LAYOUT_GENERAL", @@ -756,6 +778,11 @@ VkStructureType = { 1000430000: "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINEAR_COLOR_ATTACHMENT_FEATURES_NV", } +VkSubpassContents = { + 0: "VK_SUBPASS_CONTENTS_INLINE", + 1: "VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS", +} + opcodes = { # From http://source/play-internal/battlestar/aosp/device/generic/vulkan-cereal/stream-servers/vulkan/cereal/common/goldfish_vk_marshaling.h 20000: "OP_vkCreateInstance", diff --git a/stream-servers/CMakeLists.txt b/stream-servers/CMakeLists.txt index 42c12908..8208c92e 100644 --- a/stream-servers/CMakeLists.txt +++ b/stream-servers/CMakeLists.txt @@ -213,7 +213,8 @@ target_link_libraries( gfxstream_backend_static stream-server-testing-support gfxstream-base-testing-support - gfxstream-host-common-testing-support) + gfxstream-host-common-testing-support + gtest_main) gtest_discover_tests(OpenglRender_snapshot_unittests) # Vulkan tests################################################################## @@ -235,6 +236,7 @@ target_link_libraries( gfxstream-base-testing-support gfxstream-host-common-testing-support gtest + gtest_main gmock) gtest_discover_tests(Vulkan_unittests) diff --git a/stream-servers/ColorBuffer.cpp b/stream-servers/ColorBuffer.cpp index 11ba8221..fb2209dd 100644 --- a/stream-servers/ColorBuffer.cpp +++ b/stream-servers/ColorBuffer.cpp @@ -960,6 +960,7 @@ void ColorBuffer::restore() { switch (m_frameworkFormat) { case FRAMEWORK_FORMAT_GL_COMPATIBLE: break; + case FRAMEWORK_FORMAT_P010: case FRAMEWORK_FORMAT_YV12: case FRAMEWORK_FORMAT_YUV_420_888: m_yuv_converter.reset( diff --git a/stream-servers/DisplayVk.cpp b/stream-servers/DisplayVk.cpp index fb6cea17..c5d74406 100644 --- a/stream-servers/DisplayVk.cpp +++ b/stream-servers/DisplayVk.cpp @@ -239,7 +239,7 @@ std::tuple<bool, std::shared_future<void>> DisplayVk::post( VkImageMemoryBarrier presentToXferDstBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, - .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, diff --git a/stream-servers/FrameBuffer.cpp b/stream-servers/FrameBuffer.cpp index 57e27b9a..2f733ae3 100644 --- a/stream-servers/FrameBuffer.cpp +++ b/stream-servers/FrameBuffer.cpp @@ -367,6 +367,8 @@ bool FrameBuffer::initialize(int width, int height, bool useSubWindow, return false; } + std::unique_ptr<ScopedBind> eglColorBufferBind; + std::unique_ptr<emugl::RenderDocWithMultipleVkInstances> renderDocMultipleVkInstances = nullptr; if (!android::base::getEnvironmentVariable("ANDROID_EMU_RENDERDOC").empty()) { SharedLibrary* renderdocLib = nullptr; @@ -624,8 +626,8 @@ bool FrameBuffer::initialize(int width, int height, bool useSubWindow, GL_LOG("attempting to make context current"); // Make the context current - ScopedBind bind(fb->m_colorBufferHelper); - if (!bind.isOk()) { + eglColorBufferBind = std::make_unique<ScopedBind>(fb->m_colorBufferHelper); + if (!eglColorBufferBind->isOk()) { ERR("Failed to make current"); return false; } @@ -836,12 +838,6 @@ bool FrameBuffer::initialize(int width, int height, bool useSubWindow, } } - INFO("Graphics Adapter Vendor %s", fb->m_graphicsAdapterVendor.c_str()); - INFO("Graphics Adapter %s", fb->m_graphicsAdapterName.c_str()); - INFO("Graphics API Version %s", fb->m_graphicsApiVersion.c_str()); - INFO("Graphics API Extensions %s", fb->m_graphicsApiExtensions.c_str()); - INFO("Graphics Device Extensions %s", fb->m_graphicsDeviceExtensions.c_str()); - fb->m_textureDraw = new TextureDraw(); if (!fb->m_textureDraw) { ERR("Failed: creation of TextureDraw instance"); @@ -872,6 +868,12 @@ bool FrameBuffer::initialize(int width, int height, bool useSubWindow, } } + INFO("Graphics Adapter Vendor %s", fb->m_graphicsAdapterVendor.c_str()); + INFO("Graphics Adapter %s", fb->m_graphicsAdapterName.c_str()); + INFO("Graphics API Version %s", fb->m_graphicsApiVersion.c_str()); + INFO("Graphics API Extensions %s", fb->m_graphicsApiExtensions.c_str()); + INFO("Graphics Device Extensions %s", fb->m_graphicsDeviceExtensions.c_str()); + // Start up the single sync thread. If we are using Vulkan native // swapchain, then don't initialize SyncThread worker threads with EGL // contexts. @@ -2217,23 +2219,22 @@ void FrameBuffer::createYUVTextures(uint32_t type, int width, int height, uint32_t* output) { - constexpr bool kIsInterleaved = true; - constexpr bool kIsNotInterleaved = false; + FrameworkFormat format = static_cast<FrameworkFormat>(type); AutoLock mutex(m_lock); ScopedBind bind(m_colorBufferHelper); for (uint32_t i = 0; i < count; ++i) { - if (type == FRAMEWORK_FORMAT_NV12) { + if (format == FRAMEWORK_FORMAT_NV12) { YUVConverter::createYUVGLTex(GL_TEXTURE0, width, height, - &output[2 * i], kIsNotInterleaved); + format, YUVPlane::Y, &output[2 * i]); YUVConverter::createYUVGLTex(GL_TEXTURE1, width / 2, height / 2, - &output[2 * i + 1], kIsInterleaved); - } else if (type == FRAMEWORK_FORMAT_YUV_420_888) { + format, YUVPlane::UV, &output[2 * i + 1]); + } else if (format == FRAMEWORK_FORMAT_YUV_420_888) { YUVConverter::createYUVGLTex(GL_TEXTURE0, width, height, - &output[3 * i], kIsNotInterleaved); + format, YUVPlane::Y, &output[3 * i]); YUVConverter::createYUVGLTex(GL_TEXTURE1, width / 2, height / 2, - &output[3 * i + 1], kIsNotInterleaved); + format, YUVPlane::U, &output[3 * i + 1]); YUVConverter::createYUVGLTex(GL_TEXTURE2, width / 2, height / 2, - &output[3 * i + 2], kIsNotInterleaved); + format, YUVPlane::V, &output[3 * i + 2]); } } } diff --git a/stream-servers/FrameworkFormats.h b/stream-servers/FrameworkFormats.h index 051f3331..d6ca5f79 100644 --- a/stream-servers/FrameworkFormats.h +++ b/stream-servers/FrameworkFormats.h @@ -17,4 +17,5 @@ enum FrameworkFormat { FRAMEWORK_FORMAT_YV12 = 1, FRAMEWORK_FORMAT_YUV_420_888 = 2, FRAMEWORK_FORMAT_NV12 = 3, + FRAMEWORK_FORMAT_P010 = 4, }; diff --git a/stream-servers/PostWorker.cpp b/stream-servers/PostWorker.cpp index 7ec16c84..8161ebae 100644 --- a/stream-servers/PostWorker.cpp +++ b/stream-servers/PostWorker.cpp @@ -97,8 +97,6 @@ void PostWorker::postImpl(ColorBuffer* cb) { } goldfish_vk::acquireColorBuffersForHostComposing({}, cb->getHndl()); auto [success, waitForGpu] = m_displayVk->post(cb->getDisplayBufferVk()); - goldfish_vk::setColorBufferCurrentLayout(cb->getHndl(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); goldfish_vk::releaseColorBufferFromHostComposing({cb->getHndl()}); if (success) { waitForGpu.wait(); diff --git a/stream-servers/RenderControl.cpp b/stream-servers/RenderControl.cpp index 4a75964a..cec056fa 100644 --- a/stream-servers/RenderControl.cpp +++ b/stream-servers/RenderControl.cpp @@ -475,7 +475,7 @@ static EGLint rcGetGLString(EGLenum name, void* buffer, EGLint bufferSize) { feature_is_enabled(kFeature_YUV420888toNV21); bool YUVCacheEnabled = feature_is_enabled(kFeature_YUVCache); - bool AsyncUnmapBufferEnabled = false; + bool AsyncUnmapBufferEnabled = feature_is_enabled(kFeature_AsyncComposeSupport); bool vulkanIgnoredHandlesEnabled = shouldEnableVulkan() && feature_is_enabled(kFeature_VulkanIgnoredHandles); bool virtioGpuNextEnabled = @@ -664,9 +664,11 @@ static EGLint rcGetGLString(EGLenum name, void* buffer, EGLint bufferSize) { glStr += kHostSideTracing; glStr += " "; - // Async makecurrent support. - // glStr += kAsyncFrameCommands; - // glStr += " "; + if (feature_is_enabled(kFeature_AsyncComposeSupport)) { + // Async makecurrent support. + glStr += kAsyncFrameCommands; + glStr += " "; + } if (feature_is_enabled(kFeature_IgnoreHostOpenGLErrors)) { glStr += kGLESNoHostError; diff --git a/stream-servers/SyncThread.cpp b/stream-servers/SyncThread.cpp index fbad448f..8e24b413 100644 --- a/stream-servers/SyncThread.cpp +++ b/stream-servers/SyncThread.cpp @@ -313,8 +313,10 @@ void SyncThread::initSyncEGLContext() { void SyncThread::doSyncWait(FenceSync* fenceSync, std::function<void()> onComplete) { DPRINT("enter"); - if (!FenceSync::getFromHandle((uint64_t)(uintptr_t)fenceSync) && onComplete) { - onComplete(); + if (!FenceSync::getFromHandle((uint64_t)(uintptr_t)fenceSync)) { + if (onComplete) { + onComplete(); + } return; } // We shouldn't use FenceSync to wait, when SyncThread is initialized diff --git a/stream-servers/YUVConverter.cpp b/stream-servers/YUVConverter.cpp index 22858e46..10e97c50 100644 --- a/stream-servers/YUVConverter.cpp +++ b/stream-servers/YUVConverter.cpp @@ -16,11 +16,14 @@ #include "YUVConverter.h" -#include "DispatchTables.h" -#include "host-common/feature_control.h" #include <assert.h> #include <stdio.h> -#include <string.h> +#include <string> + +#include "DispatchTables.h" +#include "host-common/feature_control.h" +#include "host-common/misc.h" + #define FATAL(fmt,...) do { \ fprintf(stderr, "%s: FATAL: " fmt "\n", __func__, ##__VA_ARGS__); \ @@ -30,18 +33,267 @@ #define YUV_CONVERTER_DEBUG 0 #if YUV_CONVERTER_DEBUG -#define DDD(fmt, ...) \ +#define YUV_DEBUG_LOG(fmt, ...) \ fprintf(stderr, "yuv-converter: %s:%d " fmt "\n", __func__, __LINE__, \ ##__VA_ARGS__); #else -#define DDD(fmt, ...) +#define YUV_DEBUG_LOG(fmt, ...) #endif -enum YUVInterleaveDirection { - YUVInterleaveDirectionVU = 0, - YUVInterleaveDirectionUV = 1, +bool isInterleaved(FrameworkFormat format) { + switch (format) { + case FRAMEWORK_FORMAT_NV12: + case FRAMEWORK_FORMAT_P010: + return true; + case FRAMEWORK_FORMAT_YUV_420_888: + return feature_is_enabled(kFeature_YUV420888toNV21); + case FRAMEWORK_FORMAT_YV12: + return false; + default: + FATAL("Invalid for format:%d", format); + return false; + } +} + +enum class YUVInterleaveDirection { + VU = 0, + UV = 1, }; +YUVInterleaveDirection getInterleaveDirection(FrameworkFormat format) { + if (!isInterleaved(format)) { + FATAL("Format:%d not interleaved", format); + } + + switch (format) { + case FRAMEWORK_FORMAT_NV12: + case FRAMEWORK_FORMAT_P010: + return YUVInterleaveDirection::UV; + case FRAMEWORK_FORMAT_YUV_420_888: + if (feature_is_enabled(kFeature_YUV420888toNV21)) { + return YUVInterleaveDirection::VU; + } + FATAL("Format:%d not interleaved", format); + return YUVInterleaveDirection::UV; + case FRAMEWORK_FORMAT_YV12: + default: + FATAL("Format:%d not interleaved", format); + return YUVInterleaveDirection::UV; + } +} + +GLint getGlTextureFormat(FrameworkFormat format, YUVPlane plane) { + switch (format) { + case FRAMEWORK_FORMAT_YV12: + switch (plane) { + case YUVPlane::Y: + case YUVPlane::U: + case YUVPlane::V: + return GL_R8; + case YUVPlane::UV: + FATAL("Invalid plane:%d for format:%d", plane, format); + return 0; + } + case FRAMEWORK_FORMAT_YUV_420_888: + if (feature_is_enabled(kFeature_YUV420888toNV21)) { + switch (plane) { + case YUVPlane::Y: + return GL_R8; + case YUVPlane::UV: + return GL_RG8; + case YUVPlane::U: + case YUVPlane::V: + FATAL("Invalid plane:%d for format:%d", plane, format); + return 0; + } + } else { + switch (plane) { + case YUVPlane::Y: + case YUVPlane::U: + case YUVPlane::V: + return GL_R8; + case YUVPlane::UV: + FATAL("Invalid plane:%d for format:%d", plane, format); + return 0; + } + } + case FRAMEWORK_FORMAT_NV12: + switch (plane) { + case YUVPlane::Y: + return GL_R8; + case YUVPlane::UV: + return GL_RG8; + case YUVPlane::U: + case YUVPlane::V: + FATAL("Invalid plane:%d for format:%d", plane, format); + return 0; + } + case FRAMEWORK_FORMAT_P010: + switch (plane) { + case YUVPlane::Y: + return GL_R16UI; + case YUVPlane::UV: + return GL_RG16UI; + case YUVPlane::U: + case YUVPlane::V: + FATAL("Invalid plane:%d for format:%d", plane, format); + return 0; + } + default: + FATAL("Invalid format:%d", format); + return 0; + } +} + +GLenum getGlPixelFormat(FrameworkFormat format, YUVPlane plane) { + switch (format) { + case FRAMEWORK_FORMAT_YV12: + switch (plane) { + case YUVPlane::Y: + case YUVPlane::U: + case YUVPlane::V: + return GL_RED; + case YUVPlane::UV: + FATAL("Invalid plane:%d for format:%d", plane, format); + return 0; + } + case FRAMEWORK_FORMAT_YUV_420_888: + if (feature_is_enabled(kFeature_YUV420888toNV21)) { + switch (plane) { + case YUVPlane::Y: + return GL_RED; + case YUVPlane::UV: + return GL_RG; + case YUVPlane::U: + case YUVPlane::V: + FATAL("Invalid plane:%d for format:%d", plane, format); + return 0; + } + } else { + switch (plane) { + case YUVPlane::Y: + case YUVPlane::U: + case YUVPlane::V: + return GL_RED; + case YUVPlane::UV: + FATAL("Invalid plane:%d for format:%d", plane, format); + return 0; + } + } + case FRAMEWORK_FORMAT_NV12: + switch (plane) { + case YUVPlane::Y: + return GL_RED; + case YUVPlane::UV: + return GL_RG; + case YUVPlane::U: + case YUVPlane::V: + FATAL("Invalid plane:%d for format:%d", plane, format); + return 0; + } + case FRAMEWORK_FORMAT_P010: + switch (plane) { + case YUVPlane::Y: + return GL_RED_INTEGER; + case YUVPlane::UV: + return GL_RG_INTEGER; + case YUVPlane::U: + case YUVPlane::V: + FATAL("Invalid plane:%d for format:%d", plane, format); + return 0; + } + default: + FATAL("Invalid format:%d", format); + return 0; + } +} + +GLsizei getGlPixelType(FrameworkFormat format, YUVPlane plane) { + switch (format) { + case FRAMEWORK_FORMAT_YV12: + switch (plane) { + case YUVPlane::Y: + case YUVPlane::U: + case YUVPlane::V: + return GL_UNSIGNED_BYTE; + case YUVPlane::UV: + FATAL("Invalid plane:%d for format:%d", plane, format); + return 0; + } + case FRAMEWORK_FORMAT_YUV_420_888: + if (feature_is_enabled(kFeature_YUV420888toNV21)) { + switch (plane) { + case YUVPlane::Y: + case YUVPlane::UV: + return GL_UNSIGNED_BYTE; + case YUVPlane::U: + case YUVPlane::V: + FATAL("Invalid plane:%d for format:%d", plane, format); + return 0; + } + } else { + switch (plane) { + case YUVPlane::Y: + case YUVPlane::U: + case YUVPlane::V: + return GL_UNSIGNED_BYTE; + case YUVPlane::UV: + FATAL("Invalid plane:%d for format:%d", plane, format); + return 0; + } + } + case FRAMEWORK_FORMAT_NV12: + switch (plane) { + case YUVPlane::Y: + case YUVPlane::UV: + return GL_UNSIGNED_BYTE; + case YUVPlane::U: + case YUVPlane::V: + FATAL("Invalid plane:%d for format:%d", plane, format); + return 0; + } + case FRAMEWORK_FORMAT_P010: + switch (plane) { + case YUVPlane::Y: + case YUVPlane::UV: + return GL_UNSIGNED_SHORT; + case YUVPlane::U: + case YUVPlane::V: + FATAL("Invalid plane:%d for format:%d", plane, format); + return 0; + } + default: + FATAL("Invalid format:%d", format); + return 0; + } +} + +// NV12 and YUV420 are all packed +static void NV12ToYUV420PlanarInPlaceConvert(int nWidth, + int nHeight, + uint8_t* pFrame, + uint8_t* pQuad) { + std::vector<uint8_t> tmp; + if (pQuad == nullptr) { + tmp.resize(nWidth * nHeight / 4); + pQuad = tmp.data(); + } + int nPitch = nWidth; + uint8_t *puv = pFrame + nPitch * nHeight, *pu = puv, + *pv = puv + nPitch * nHeight / 4; + for (int y = 0; y < nHeight / 2; y++) { + for (int x = 0; x < nWidth / 2; x++) { + pu[y * nPitch / 2 + x] = puv[y * nPitch + x * 2]; + pQuad[y * nWidth / 2 + x] = puv[y * nPitch + x * 2 + 1]; + } + } + memcpy(pv, pQuad, nWidth * nHeight / 4); +} + +inline uint32_t alignToPower2(uint32_t val, uint32_t align) { + return (val + (align - 1)) & ~(align - 1); +} + // getYUVOffsets(), given a YUV-formatted buffer that is arranged // according to the spec // https://developer.android.com/reference/android/graphics/ImageFormat.html#YUV @@ -49,65 +301,75 @@ enum YUVInterleaveDirection { // Inputs: // |yv12|: the YUV-formatted buffer // Outputs: -// |yoff|: offset into |yv12| of the start of the Y component -// |uoff|: offset into |yv12| of the start of the U component -// |voff|: offset into |yv12| of the start of the V component -static void getYUVOffsets(int width, int height, FrameworkFormat format, - uint32_t* yoff, uint32_t* uoff, uint32_t* voff, - uint32_t* alignwidth, uint32_t* alignwidthc) { - uint32_t yStride, cStride, cHeight, cSize, align; +// |yOffset|: offset into |yv12| of the start of the Y component +// |uOffset|: offset into |yv12| of the start of the U component +// |vOffset|: offset into |yv12| of the start of the V component +static void getYUVOffsets(int width, + int height, + FrameworkFormat format, + uint32_t* yOffset, + uint32_t* uOffset, + uint32_t* vOffset, + uint32_t* yWidth, + uint32_t* cWidth) { + uint32_t yStride, cStride, cHeight, cSize; switch (format) { case FRAMEWORK_FORMAT_YV12: // Luma stride is 32 bytes aligned. - align = 32; - yStride = (width + (align - 1)) & ~(align - 1); + yStride = alignToPower2(width, 32); // Chroma stride is 16 bytes aligned. - align = 16; - cStride = (yStride / 2 + (align - 1)) & ~(align - 1); + cStride = alignToPower2(yStride, 16); cHeight = height / 2; cSize = cStride * cHeight; - *yoff = 0; - *voff = yStride * height; - *uoff = (*voff) + cSize; - *alignwidth = yStride; - *alignwidthc = cStride; + *yOffset = 0; + *vOffset = yStride * height; + *uOffset = (*vOffset) + cSize; + *yWidth = yStride; + *cWidth = cStride; break; case FRAMEWORK_FORMAT_YUV_420_888: - if (feature_is_enabled( - kFeature_YUV420888toNV21)) { - align = 1; - yStride = (width + (align - 1)) & ~(align - 1); + if (feature_is_enabled(kFeature_YUV420888toNV21)) { + yStride = width; cStride = yStride; cHeight = height / 2; - *yoff = 0; - *voff = yStride * height; - *uoff = (*voff) + 1; - *alignwidth = yStride; - *alignwidthc = cStride / 2; + *yOffset = 0; + *vOffset = yStride * height; + *uOffset = (*vOffset) + 1; + *yWidth = yStride; + *cWidth = cStride / 2; } else { - align = 1; - yStride = (width + (align - 1)) & ~(align - 1); - cStride = (yStride / 2 + (align - 1)) & ~(align - 1); + yStride = width; + cStride = yStride / 2; cHeight = height / 2; cSize = cStride * cHeight; - *yoff = 0; - *uoff = yStride * height; - *voff = (*uoff) + cSize; - *alignwidth = yStride; - *alignwidthc = cStride; + *yOffset = 0; + *uOffset = yStride * height; + *vOffset = (*uOffset) + cSize; + *yWidth = yStride; + *cWidth = cStride; } break; case FRAMEWORK_FORMAT_NV12: - align = 1; yStride = width; cStride = yStride; cHeight = height / 2; cSize = cStride * cHeight; - *yoff = 0; - *uoff = yStride * height; - *voff = (*uoff) + 1; - *alignwidth = yStride; - *alignwidthc = cStride / 2; + *yOffset = 0; + *uOffset = yStride * height; + *vOffset = (*uOffset) + 1; + *yWidth = yStride; + *cWidth = cStride / 2; + break; + case FRAMEWORK_FORMAT_P010: + *yWidth = width; + *cWidth = width / 2; + yStride = width * /*bytes per pixel=*/2; + cStride = *cWidth * /*bytes per pixel=*/2; + cHeight = height / 2; + cSize = cStride * cHeight; + *yOffset = 0; + *uOffset = yStride * height; + *vOffset = (*uOffset) + 2; break; case FRAMEWORK_FORMAT_GL_COMPATIBLE: FATAL("Input not a YUV format! (FRAMEWORK_FORMAT_GL_COMPATIBLE)"); @@ -116,334 +378,278 @@ static void getYUVOffsets(int width, int height, FrameworkFormat format, } } -// createYUVGLTex() allocates GPU memory that is enough -// to hold the raw data of the YV12 buffer. -// The memory is in the form of an OpenGL texture -// with one component (GL_LUMINANCE) and -// of type GL_UNSIGNED_BYTE. -// In order to process all Y, U, V components -// simultaneously in conversion, the simple thing to do -// is to use multiple texture units, hence -// the |texture_unit| argument. -// Returns a new OpenGL texture object in |texName_out| -// that is to be cleaned up by the caller. -void YUVConverter::createYUVGLTex(GLenum texture_unit, +// Allocates an OpenGL texture that is large enough for a single plane of +// a YUV buffer of the given format and returns the texture name in the +// `outTextureName` argument. +void YUVConverter::createYUVGLTex(GLenum textureUnit, GLsizei width, GLsizei height, - GLuint* texName_out, - bool uvInterleaved) { - assert(texName_out); - - s_gles2.glActiveTexture(texture_unit); - s_gles2.glGenTextures(1, texName_out); - s_gles2.glBindTexture(GL_TEXTURE_2D, *texName_out); + FrameworkFormat format, + YUVPlane plane, + GLuint* outTextureName) { + YUV_DEBUG_LOG("w:%d h:%d format:%d plane:%d", width, height, format, plane); + + s_gles2.glActiveTexture(textureUnit); + s_gles2.glGenTextures(1, outTextureName); + s_gles2.glBindTexture(GL_TEXTURE_2D, *outTextureName); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); GLint unprevAlignment = 0; s_gles2.glGetIntegerv(GL_UNPACK_ALIGNMENT, &unprevAlignment); s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - if (uvInterleaved) { - s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, - width, height, 0, - GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, - NULL); - } else { - s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, - width, height, 0, - GL_LUMINANCE, GL_UNSIGNED_BYTE, - NULL); - } + const GLint textureFormat = getGlTextureFormat(format, plane); + const GLenum pixelFormat = getGlPixelFormat(format, plane); + const GLenum pixelType = getGlPixelType(format, plane); + s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, textureFormat, width, height, 0, pixelFormat, pixelType, NULL); s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, unprevAlignment); s_gles2.glActiveTexture(GL_TEXTURE0); } -static void readYUVTex(GLuint tex, void* pixels, bool uvInterleaved) { +static void readYUVTex(GLuint tex, FrameworkFormat format, YUVPlane plane, void* pixels) { + YUV_DEBUG_LOG("format%d plane:%d pixels:%p", format, plane, pixels); + GLuint prevTexture = 0; s_gles2.glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&prevTexture); s_gles2.glBindTexture(GL_TEXTURE_2D, tex); GLint prevAlignment = 0; s_gles2.glGetIntegerv(GL_PACK_ALIGNMENT, &prevAlignment); s_gles2.glPixelStorei(GL_PACK_ALIGNMENT, 1); - if (uvInterleaved) { - if (s_gles2.glGetTexImage) { - s_gles2.glGetTexImage(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, - GL_UNSIGNED_BYTE, pixels); - } else { - DDD("empty glGetTexImage"); - } + const GLenum pixelFormat = getGlPixelFormat(format, plane); + const GLenum pixelType = getGlPixelType(format, plane); + if (s_gles2.glGetTexImage) { + s_gles2.glGetTexImage(GL_TEXTURE_2D, 0, pixelFormat, pixelType, pixels); } else { - if (s_gles2.glGetTexImage) { - s_gles2.glGetTexImage(GL_TEXTURE_2D, 0, GL_LUMINANCE, - GL_UNSIGNED_BYTE, pixels); - } else { - DDD("empty glGetTexImage"); - } + YUV_DEBUG_LOG("empty glGetTexImage"); } + s_gles2.glPixelStorei(GL_PACK_ALIGNMENT, prevAlignment); s_gles2.glBindTexture(GL_TEXTURE_2D, prevTexture); } -// subUpdateYUVGLTex() updates a given YUV texture -// at the coordinates (x, y, width, height), -// with the raw YUV data in |pixels|. -// We cannot view the result properly until -// after conversion; this is to be used only -// as input to the conversion shader. +// Updates a given YUV buffer's plane texture at the coordinates +// (x, y, width, height), with the raw YUV data in |pixels|. We +// cannot view the result properly until after conversion; this is +// to be used only as input to the conversion shader. static void subUpdateYUVGLTex(GLenum texture_unit, GLuint tex, - int x, int y, int width, int height, - void* pixels, bool uvInterleaved) { + int x, + int y, + int width, + int height, + FrameworkFormat format, + YUVPlane plane, + const void* pixels) { + YUV_DEBUG_LOG("x:%d y:%d w:%d h:%d format:%d plane:%d", x, y, width, height, format, plane); + + const GLenum pixelFormat = getGlPixelFormat(format, plane); + const GLenum pixelType = getGlPixelType(format, plane); + s_gles2.glActiveTexture(texture_unit); s_gles2.glBindTexture(GL_TEXTURE_2D, tex); GLint unprevAlignment = 0; s_gles2.glGetIntegerv(GL_UNPACK_ALIGNMENT, &unprevAlignment); s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - if (uvInterleaved) { - s_gles2.glTexSubImage2D(GL_TEXTURE_2D, 0, - x, y, width, height, - GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, - pixels); - } else { - s_gles2.glTexSubImage2D(GL_TEXTURE_2D, 0, - x, y, width, height, - GL_LUMINANCE, GL_UNSIGNED_BYTE, - pixels); - } + s_gles2.glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, pixelFormat, pixelType, pixels); s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, unprevAlignment); - s_gles2.glActiveTexture(GL_TEXTURE0); } -// createYUVGLShader() defines the vertex/fragment -// shader that does the actual work of converting -// YUV to RGB. The resulting program is stored in |program_out|. -static void createYUVGLShader(GLuint* program_out, - GLint* ywidthcutoffloc_out, - GLint* cwidthcutoffloc_out, - GLint* ysamplerloc_out, - GLint* usamplerloc_out, - GLint* vsamplerloc_out, - GLint* incoordloc_out, - GLint* posloc_out) { - assert(program_out); - - static const char kVShader[] = R"( -precision highp float; -attribute mediump vec4 position; -attribute highp vec2 inCoord; -varying highp vec2 outCoord; -void main(void) { - gl_Position = position; - outCoord = inCoord; -} - )"; - const GLchar* const kVShaders = - static_cast<const GLchar*>(kVShader); - - // Based on: - // http://stackoverflow.com/questions/11093061/yv12-to-rgb-using-glsl-in-ios-result-image-attached - // + account for 16-pixel alignment using |yWidthCutoff| / |cWidthCutoff| - // + use conversion matrix in - // frameworks/av/media/libstagefright/colorconversion/ColorConverter.cpp (YUV420p) - // + more precision from - // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion - static const char kFShader[] = R"( +void YUVConverter::createYUVGLShader() { + YUV_DEBUG_LOG("format:%d", mFormat); + + // P010 needs uint samplers. + if (mFormat == FRAMEWORK_FORMAT_P010 && !mHasGlsl3Support) { + return; + } + + static const char kVertShader[] = R"( precision highp float; -varying highp vec2 outCoord; -uniform highp float yWidthCutoff; -uniform highp float cWidthCutoff; -uniform sampler2D ysampler; -uniform sampler2D usampler; -uniform sampler2D vsampler; +attribute mediump vec4 aPosition; +attribute highp vec2 aTexCoord; +varying highp vec2 vTexCoord; void main(void) { - highp vec2 cutoffCoordsY; - highp vec2 cutoffCoordsC; - highp vec3 yuv; - highp vec3 rgb; - cutoffCoordsY.x = outCoord.x * yWidthCutoff; - cutoffCoordsY.y = outCoord.y; - cutoffCoordsC.x = outCoord.x * cWidthCutoff; - cutoffCoordsC.y = outCoord.y; - yuv[0] = texture2D(ysampler, cutoffCoordsY).r - 0.0625; - yuv[1] = 0.96*(texture2D(usampler, cutoffCoordsC).r - 0.5); - yuv[2] = texture2D(vsampler, cutoffCoordsC).r - 0.5; - highp float yscale = 1.1643835616438356; - rgb = mat3(yscale, yscale, yscale, - 0, -0.39176229009491365, 2.017232142857143, - 1.5960267857142856, -0.8129676472377708, 0) * yuv; - gl_FragColor = vec4(rgb, 1); + gl_Position = aPosition; + vTexCoord = aTexCoord; } )"; - const GLchar* const kFShaders = - static_cast<const GLchar*>(kFShader); - - GLuint vshader = s_gles2.glCreateShader(GL_VERTEX_SHADER); - GLuint fshader = s_gles2.glCreateShader(GL_FRAGMENT_SHADER); - - const GLint vtextLen = strlen(kVShader); - const GLint ftextLen = strlen(kFShaders); - s_gles2.glShaderSource(vshader, 1, &kVShaders, &vtextLen); - s_gles2.glShaderSource(fshader, 1, &kFShaders, &ftextLen); - s_gles2.glCompileShader(vshader); - s_gles2.glCompileShader(fshader); - - *program_out = s_gles2.glCreateProgram(); - s_gles2.glAttachShader(*program_out, vshader); - s_gles2.glAttachShader(*program_out, fshader); - s_gles2.glLinkProgram(*program_out); - - *ywidthcutoffloc_out = s_gles2.glGetUniformLocation(*program_out, "yWidthCutoff"); - *cwidthcutoffloc_out = s_gles2.glGetUniformLocation(*program_out, "cWidthCutoff"); - *ysamplerloc_out = s_gles2.glGetUniformLocation(*program_out, "ysampler"); - *usamplerloc_out = s_gles2.glGetUniformLocation(*program_out, "usampler"); - *vsamplerloc_out = s_gles2.glGetUniformLocation(*program_out, "vsampler"); - *posloc_out = s_gles2.glGetAttribLocation(*program_out, "position"); - *incoordloc_out = s_gles2.glGetAttribLocation(*program_out, "inCoord"); - - s_gles2.glDeleteShader(vshader); - s_gles2.glDeleteShader(fshader); -} + static const char kFragShaderVersion3[] = R"(#version 300 es)"; -static void createYUVInterleavedGLShader(GLuint* program_out, - GLint* ywidthcutoffloc_out, - GLint* cwidthcutoffloc_out, - GLint* ysamplerloc_out, - GLint* vusamplerloc_out, - GLint* incoordloc_out, - GLint* posloc_out, - YUVInterleaveDirection interleaveDir) { - assert(program_out); - - static const char kVShader[] = R"( + static const char kFragShaderBegin[] = R"( precision highp float; -attribute mediump vec4 position; -attribute highp vec2 inCoord; -varying highp vec2 outCoord; -void main(void) { - gl_Position = position; - outCoord = inCoord; -} + +varying highp vec2 vTexCoord; + +uniform highp float uYWidthCutoff; +uniform highp float uCWidthCutoff; )"; - const GLchar* const kVShaders = - static_cast<const GLchar*>(kVShader); - - // Based on: - // https://stackoverflow.com/questions/22456884/how-to-render-androids-yuv-nv21-camera-image-on-the-background-in-libgdx-with-o - // + account for 16-pixel alignment using |yWidthCutoff| / |cWidthCutoff| - // + use conversion matrix in - // frameworks/av/media/libstagefright/colorconversion/ColorConverter.cpp (YUV420p) - // + more precision from - // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion - // UV texture is (width/2*height/2) in size (downsampled by 2 in - // both dimensions, each pixel corresponds to 4 pixels of the Y channel) - // and each pixel is two bytes. By setting GL_LUMINANCE_ALPHA, OpenGL - // puts first byte (V) into R,G and B components and of the texture - // and the second byte (U) into the A component of the texture. That's - // why we find U and V at A and R respectively in the fragment shader code. - // Note that we could have also found V at G or B as well. - static const char kFShaderVu[] = R"( -precision highp float; -varying highp vec2 outCoord; -uniform highp float yWidthCutoff; -uniform highp float cWidthCutoff; -uniform sampler2D ysampler; -uniform sampler2D vusampler; -void main(void) { - highp vec2 cutoffCoordsY; - highp vec2 cutoffCoordsC; - highp vec3 yuv; - highp vec3 rgb; - cutoffCoordsY.x = outCoord.x * yWidthCutoff; - cutoffCoordsY.y = outCoord.y; - cutoffCoordsC.x = outCoord.x * cWidthCutoff; - cutoffCoordsC.y = outCoord.y; - yuv[0] = texture2D(ysampler, cutoffCoordsY).r - 0.0625; - yuv[1] = 0.96 * (texture2D(vusampler, cutoffCoordsC).a - 0.5); - yuv[2] = texture2D(vusampler, cutoffCoordsC).r - 0.5; - highp float yscale = 1.1643835616438356; - rgb = mat3(yscale, yscale, yscale, - 0, -0.39176229009491365, 2.017232142857143, - 1.5960267857142856, -0.8129676472377708, 0) * yuv; - gl_FragColor = vec4(rgb, 1); -} + + static const char kSamplerUniforms[] = R"( +uniform sampler2D uSamplerY; +uniform sampler2D uSamplerU; +uniform sampler2D uSamplerV; + )"; + static const char kSamplerUniformsUint[] = R"( +uniform usampler2D uSamplerY; +uniform usampler2D uSamplerU; +uniform usampler2D uSamplerV; )"; - static const char kFShaderUv[] = R"( -precision highp float; -varying highp vec2 outCoord; -uniform highp float yWidthCutoff; -uniform highp float cWidthCutoff; -uniform sampler2D ysampler; -uniform sampler2D uvsampler; + static const char kFragShaderMainBegin[] = R"( void main(void) { - highp vec2 cutoffCoordsY; - highp vec2 cutoffCoordsC; + highp vec2 yTexCoords = vTexCoord; + highp vec2 uvTexCoords = vTexCoord; + + // For textures with extra padding for alignment (e.g. YV12 pads to 16), + // scale the coordinates to only sample from the non-padded area. + yTexCoords.x *= uYWidthCutoff; + uvTexCoords.y *= uCWidthCutoff; + highp vec3 yuv; - highp vec3 rgb; - cutoffCoordsY.x = outCoord.x * yWidthCutoff; - cutoffCoordsY.y = outCoord.y; - cutoffCoordsC.x = outCoord.x * cWidthCutoff; - cutoffCoordsC.y = outCoord.y; - yuv[0] = texture2D(ysampler, cutoffCoordsY).r - 0.0625; - yuv[1] = 0.96 * (texture2D(uvsampler, cutoffCoordsC).r - 0.5); - yuv[2] = (texture2D(uvsampler, cutoffCoordsC).a - 0.5); +)"; + + static const char kSampleY[] = R"( + yuv[0] = texture2D(uSamplerY, yTexCoords).r; + )"; + static const char kSampleUV[] = R"( + yuv[1] = texture2D(uSamplerU, uvTexCoords).r; + yuv[2] = texture2D(uSamplerV, uvTexCoords).r; + )"; + static const char kSampleInterleavedUV[] = R"( + // Note: uSamplerU and vSamplerV refer to the same texture. + yuv[1] = texture2D(uSamplerU, uvTexCoords).r; + yuv[2] = texture2D(uSamplerV, uvTexCoords).g; + )"; + static const char kSampleInterleavedVU[] = R"( + // Note: uSamplerU and vSamplerV refer to the same texture. + yuv[1] = texture2D(uSamplerU, uvTexCoords).g; + yuv[2] = texture2D(uSamplerV, uvTexCoords).r; + )"; + + static const char kSampleP010[] = R"( + uint yRaw = texture(uSamplerY, yTexCoords).r; + uint uRaw = texture(uSamplerU, uvTexCoords).r; + uint vRaw = texture(uSamplerV, uvTexCoords).g; + + // P010 values are stored in the upper 10-bits of 16-bit unsigned shorts. + yuv[0] = float(yRaw >> 6) / 1023.0; + yuv[1] = float(uRaw >> 6) / 1023.0; + yuv[2] = float(vRaw >> 6) / 1023.0; + )"; + + static const char kFragShaderMainEnd[] = R"( + yuv[0] = yuv[0] - 0.0625; + yuv[1] = 0.96 * (yuv[1] - 0.5); + yuv[2] = (yuv[2] - 0.5); + highp float yscale = 1.1643835616438356; - rgb = mat3(yscale, yscale, yscale, - 0, -0.39176229009491365, 2.017232142857143, - 1.5960267857142856, -0.8129676472377708, 0) * yuv; - gl_FragColor = vec4(rgb, 1); + highp vec3 rgb = mat3( yscale, yscale, yscale, + 0, -0.39176229009491365, 2.017232142857143, + 1.5960267857142856, -0.8129676472377708, 0) * yuv; + + gl_FragColor = vec4(rgb, 1.0); } )"; - const GLchar* const kFShaders = - interleaveDir == YUVInterleaveDirectionVU ? kFShaderVu : kFShaderUv; - - GLuint vshader = s_gles2.glCreateShader(GL_VERTEX_SHADER); - GLuint fshader = s_gles2.glCreateShader(GL_FRAGMENT_SHADER); - - const GLint vtextLen = strlen(kVShader); - const GLint ftextLen = strlen(kFShaders); - s_gles2.glShaderSource(vshader, 1, &kVShaders, &vtextLen); - s_gles2.glShaderSource(fshader, 1, &kFShaders, &ftextLen); - s_gles2.glCompileShader(vshader); - s_gles2.glCompileShader(fshader); - - *program_out = s_gles2.glCreateProgram(); - s_gles2.glAttachShader(*program_out, vshader); - s_gles2.glAttachShader(*program_out, fshader); - s_gles2.glLinkProgram(*program_out); - - *ywidthcutoffloc_out = s_gles2.glGetUniformLocation(*program_out, "yWidthCutoff"); - *cwidthcutoffloc_out = s_gles2.glGetUniformLocation(*program_out, "cWidthCutoff"); - *ysamplerloc_out = s_gles2.glGetUniformLocation(*program_out, "ysampler"); - *vusamplerloc_out = s_gles2.glGetUniformLocation( - *program_out, YUVInterleaveDirectionVU ? "vusampler" : "uvsampler"); - *posloc_out = s_gles2.glGetAttribLocation(*program_out, "position"); - *incoordloc_out = s_gles2.glGetAttribLocation(*program_out, "inCoord"); - - s_gles2.glDeleteShader(vshader); - s_gles2.glDeleteShader(fshader); + std::string vertShaderSource(kVertShader); + std::string fragShaderSource; + + if (mFormat == FRAMEWORK_FORMAT_P010) { + fragShaderSource += kFragShaderVersion3; + } + + fragShaderSource += kFragShaderBegin; + + if (mFormat == FRAMEWORK_FORMAT_P010) { + fragShaderSource += kSamplerUniformsUint; + } else { + fragShaderSource += kSamplerUniforms; + } + + fragShaderSource += kFragShaderMainBegin; + + switch (mFormat) { + case FRAMEWORK_FORMAT_NV12: + case FRAMEWORK_FORMAT_YUV_420_888: + case FRAMEWORK_FORMAT_YV12: + fragShaderSource += kSampleY; + if (isInterleaved(mFormat)) { + if (getInterleaveDirection(mFormat) == YUVInterleaveDirection::UV) { + fragShaderSource += kSampleInterleavedUV; + } else { + fragShaderSource += kSampleInterleavedVU; + } + } else { + fragShaderSource += kSampleUV; + } + break; + case FRAMEWORK_FORMAT_P010: + fragShaderSource += kSampleP010; + break; + default: + FATAL("%s: invalid format:%d", __FUNCTION__, mFormat); + return; + } + + fragShaderSource += kFragShaderMainEnd; + + YUV_DEBUG_LOG("format:%d vert-source:%s frag-source:%s", mFormat, vertShaderSource.c_str(), fragShaderSource.c_str()); + + const GLchar* const vertShaderSourceChars = vertShaderSource.c_str(); + const GLchar* const fragShaderSourceChars = fragShaderSource.c_str(); + const GLint vertShaderSourceLen = vertShaderSource.length(); + const GLint fragShaderSourceLen = fragShaderSource.length(); + + GLuint vertShader = s_gles2.glCreateShader(GL_VERTEX_SHADER); + GLuint fragShader = s_gles2.glCreateShader(GL_FRAGMENT_SHADER); + s_gles2.glShaderSource(vertShader, 1, &vertShaderSourceChars, &vertShaderSourceLen); + s_gles2.glShaderSource(fragShader, 1, &fragShaderSourceChars, &fragShaderSourceLen); + s_gles2.glCompileShader(vertShader); + s_gles2.glCompileShader(fragShader); + + for (GLuint shader : {vertShader, fragShader}) { + GLint status = GL_FALSE; + s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (status == GL_FALSE) { + GLchar error[1024]; + s_gles2.glGetShaderInfoLog(shader, sizeof(error), nullptr, &error[0]); + FATAL("Failed to compile YUV conversion shader: %s", error); + s_gles2.glDeleteShader(shader); + return; + } + } + + mProgram = s_gles2.glCreateProgram(); + s_gles2.glAttachShader(mProgram, vertShader); + s_gles2.glAttachShader(mProgram, fragShader); + s_gles2.glLinkProgram(mProgram); + + GLint status = GL_FALSE; + s_gles2.glGetProgramiv(mProgram, GL_LINK_STATUS, &status); + if (status == GL_FALSE) { + GLchar error[1024]; + s_gles2.glGetProgramInfoLog(mProgram, sizeof(error), 0, &error[0]); + FATAL("Failed to link YUV conversion program: %s", error); + s_gles2.glDeleteProgram(mProgram); + mProgram = 0; + return; + } + + mUniformLocYWidthCutoff = s_gles2.glGetUniformLocation(mProgram, "uYWidthCutoff"); + mUniformLocCWidthCutoff = s_gles2.glGetUniformLocation(mProgram, "uCWidthCutoff"); + mUniformLocSamplerY = s_gles2.glGetUniformLocation(mProgram, "uSamplerY"); + mUniformLocSamplerU = s_gles2.glGetUniformLocation(mProgram, "uSamplerU"); + mUniformLocSamplerV = s_gles2.glGetUniformLocation(mProgram, "uSamplerV"); + mAttributeLocPos = s_gles2.glGetAttribLocation(mProgram, "aPosition"); + mAttributeLocTexCoord = s_gles2.glGetAttribLocation(mProgram, "aTexCoord"); + + s_gles2.glDeleteShader(vertShader); + s_gles2.glDeleteShader(fragShader); } -// When converting YUV to RGB with shaders, -// we are using the OpenGL graphics pipeline to do compute, -// so we need to express the place to store the result -// with triangles and draw calls. -// createYUVGLFullscreenQuad() defines a fullscreen quad -// with position and texture coordinates. -// The quad will be textured with the resulting RGB colors, -// and we will read back the pixels from the framebuffer -// to retrieve our RGB result. -static void createYUVGLFullscreenQuad(GLuint* vbuf_out, - GLuint* ibuf_out, - int picture_width, - int aligned_width) { - assert(vbuf_out); - assert(ibuf_out); - - s_gles2.glGenBuffers(1, vbuf_out); - s_gles2.glGenBuffers(1, ibuf_out); + +void YUVConverter::createYUVGLFullscreenQuad() { + s_gles2.glGenBuffers(1, &mQuadVertexBuffer); + s_gles2.glGenBuffers(1, &mQuadIndexBuffer); static const float kVertices[] = { +1, -1, +0, +1, +0, @@ -454,69 +660,53 @@ static void createYUVGLFullscreenQuad(GLuint* vbuf_out, static const GLubyte kIndices[] = { 0, 1, 2, 2, 3, 0 }; - s_gles2.glBindBuffer(GL_ARRAY_BUFFER, *vbuf_out); - s_gles2.glBufferData(GL_ARRAY_BUFFER, sizeof(kVertices), kVertices, - GL_STATIC_DRAW); - s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *ibuf_out); - s_gles2.glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(kIndices), kIndices, - GL_STATIC_DRAW); + s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexBuffer); + s_gles2.glBufferData(GL_ARRAY_BUFFER, sizeof(kVertices), kVertices, GL_STATIC_DRAW); + s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mQuadIndexBuffer); + s_gles2.glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(kIndices), kIndices, GL_STATIC_DRAW); } -// doYUVConversionDraw() does the actual work of setting up -// and submitting draw commands to the GPU. -// It uses the textures, shaders, and fullscreen quad defined above -// and executes the pipeline on them. -// Note, however, that it is up to the caller to dig out -// the result of the draw. static void doYUVConversionDraw(GLuint program, - GLint yWidthCutoffLoc, - GLint cWidthCutoffLoc, - GLint ySamplerLoc, - GLint uSamplerLoc, - GLint vSamplerLoc, - GLint vuSamplerLoc, - GLint inCoordLoc, - GLint posLoc, - GLuint vbuf, GLuint ibuf, - int width, int ywidth, - int halfwidth, int cwidth, - float yWidthCutoff, - float cWidthCutoff, - bool uvInterleaved) { - + GLint uniformLocYWidthCutoff, + GLint uniformLocCWidthCutoff, + GLint uniformLocYSampler, + GLint uniformLocUSampler, + GLint uniformLocVSampler, + GLint attributeLocTexCoord, + GLint attributeLocPos, + GLuint quadVertexBuffer, + GLuint quadIndexBuffer, + float uYWidthCutoff, + float uCWidthCutoff) { const GLsizei kVertexAttribStride = 5 * sizeof(GL_FLOAT); const GLvoid* kVertexAttribPosOffset = (GLvoid*)0; const GLvoid* kVertexAttribCoordOffset = (GLvoid*)(3 * sizeof(GL_FLOAT)); s_gles2.glUseProgram(program); - s_gles2.glUniform1f(yWidthCutoffLoc, yWidthCutoff); - s_gles2.glUniform1f(cWidthCutoffLoc, cWidthCutoff); + s_gles2.glUniform1f(uniformLocYWidthCutoff, uYWidthCutoff); + s_gles2.glUniform1f(uniformLocCWidthCutoff, uCWidthCutoff); - s_gles2.glUniform1i(ySamplerLoc, 0); - if (uvInterleaved) { - s_gles2.glUniform1i(vuSamplerLoc, 1); - } else { - s_gles2.glUniform1i(uSamplerLoc, 1); - s_gles2.glUniform1i(vSamplerLoc, 2); - } + s_gles2.glUniform1i(uniformLocYSampler, 0); + s_gles2.glUniform1i(uniformLocUSampler, 1); + s_gles2.glUniform1i(uniformLocVSampler, 2); - s_gles2.glBindBuffer(GL_ARRAY_BUFFER, vbuf); - s_gles2.glEnableVertexAttribArray(posLoc); - s_gles2.glEnableVertexAttribArray(inCoordLoc); + s_gles2.glBindBuffer(GL_ARRAY_BUFFER, quadVertexBuffer); + s_gles2.glEnableVertexAttribArray(attributeLocPos); + s_gles2.glEnableVertexAttribArray(attributeLocTexCoord); - s_gles2.glVertexAttribPointer(posLoc, 3, GL_FLOAT, false, + s_gles2.glVertexAttribPointer(attributeLocPos, 3, GL_FLOAT, false, kVertexAttribStride, kVertexAttribPosOffset); - s_gles2.glVertexAttribPointer(inCoordLoc, 2, GL_FLOAT, false, + s_gles2.glVertexAttribPointer(attributeLocTexCoord, 2, GL_FLOAT, false, kVertexAttribStride, kVertexAttribCoordOffset); - s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf); + s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadIndexBuffer); s_gles2.glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, 0); - s_gles2.glDisableVertexAttribArray(posLoc); - s_gles2.glDisableVertexAttribArray(inCoordLoc); + s_gles2.glDisableVertexAttribArray(attributeLocPos); + s_gles2.glDisableVertexAttribArray(attributeLocTexCoord); } // initialize(): allocate GPU memory for YUV components, @@ -525,79 +715,42 @@ YUVConverter::YUVConverter(int width, int height, FrameworkFormat format) : mWidth(width), mHeight(height), mFormat(format), - mCbFormat(format) {} + mColorBufferFormat(format) {} void YUVConverter::init(int width, int height, FrameworkFormat format) { - uint32_t yoff, uoff, voff, ywidth, cwidth, cheight; - getYUVOffsets(width, height, mFormat, - &yoff, &uoff, &voff, - &ywidth, &cwidth); + YUV_DEBUG_LOG("w:%d h:%d format:%d", width, height, format); + + uint32_t yOffset, uOffset, vOffset, ywidth, cwidth, cheight; + getYUVOffsets(width, height, mFormat, &yOffset, &uOffset, &vOffset, &ywidth, &cwidth); cheight = height / 2; mWidth = width; mHeight = height; - if (!mYtex) - createYUVGLTex(GL_TEXTURE0, ywidth, height, &mYtex, false); - switch (mFormat) { - case FRAMEWORK_FORMAT_YV12: - if (!mUtex) - createYUVGLTex(GL_TEXTURE1, cwidth, cheight, &mUtex, false); - if (!mVtex) - createYUVGLTex(GL_TEXTURE2, cwidth, cheight, &mVtex, false); - createYUVGLShader(&mProgram, - &mYWidthCutoffLoc, - &mCWidthCutoffLoc, - &mYSamplerLoc, - &mUSamplerLoc, - &mVSamplerLoc, - &mInCoordLoc, - &mPosLoc); - break; - case FRAMEWORK_FORMAT_YUV_420_888: - if (feature_is_enabled( - kFeature_YUV420888toNV21)) { - if (!mVUtex) - createYUVGLTex(GL_TEXTURE1, cwidth, cheight, &mVUtex, true); - createYUVInterleavedGLShader(&mProgram, - &mYWidthCutoffLoc, - &mCWidthCutoffLoc, - &mYSamplerLoc, - &mVUSamplerLoc, - &mInCoordLoc, - &mPosLoc, - YUVInterleaveDirectionVU); - } else { - if (!mUtex) - createYUVGLTex(GL_TEXTURE1, cwidth, cheight, &mUtex, false); - if (!mVtex) - createYUVGLTex(GL_TEXTURE2, cwidth, cheight, &mVtex, false); - createYUVGLShader(&mProgram, - &mYWidthCutoffLoc, - &mCWidthCutoffLoc, - &mYSamplerLoc, - &mUSamplerLoc, - &mVSamplerLoc, - &mInCoordLoc, - &mPosLoc); - } - break; - case FRAMEWORK_FORMAT_NV12: - if (!mUVtex) - createYUVGLTex(GL_TEXTURE1, cwidth, cheight, &mUVtex, true); - createYUVInterleavedGLShader(&mProgram, - &mYWidthCutoffLoc, - &mCWidthCutoffLoc, - &mYSamplerLoc, - &mVUSamplerLoc, - &mInCoordLoc, - &mPosLoc, - YUVInterleaveDirectionUV); - break; - default: - FATAL("Unknown format: 0x%x", mFormat); + if (!mTextureY) { + createYUVGLTex(GL_TEXTURE0, ywidth, height, mFormat, YUVPlane::Y, &mTextureY); + } + if (isInterleaved(mFormat)) { + if (!mTextureU) { + createYUVGLTex(GL_TEXTURE1, cwidth, cheight, mFormat, YUVPlane::UV, &mTextureU); + mTextureV = mTextureU; + } + } else { + if (!mTextureU) { + createYUVGLTex(GL_TEXTURE1, cwidth, cheight, mFormat, YUVPlane::U, &mTextureU); + } + if (!mTextureV) { + createYUVGLTex(GL_TEXTURE2, cwidth, cheight, mFormat, YUVPlane::V, &mTextureV); + } } - createYUVGLFullscreenQuad(&mVbuf, &mIbuf, width, ywidth); + int glesMajor; + int glesMinor; + emugl::getGlesVersion(&glesMajor, &glesMinor); + mHasGlsl3Support = glesMajor >= 3; + YUV_DEBUG_LOG("YUVConverter has GLSL ES 3 support:%s (major:%d minor:%d", (mHasGlsl3Support ? "yes" : "no"), glesMajor, glesMinor); + + createYUVGLShader(); + createYUVGLFullscreenQuad(); } void YUVConverter::saveGLState() { @@ -628,80 +781,45 @@ uint32_t YUVConverter::getDataSize() { } void YUVConverter::readPixels(uint8_t* pixels, uint32_t pixels_size) { - int width = mWidth; - int height = mHeight; - - DDD("calling %s %d\n texture: width %d height %d\n", __func__, __LINE__, - width, height); - { - uint32_t align = (mFormat == FRAMEWORK_FORMAT_YV12) ? 16 : 1; - uint32_t yStride = (width + (align - 1)) & ~(align - 1); - uint32_t uvStride = (yStride / 2 + (align - 1)) & ~(align - 1); - uint32_t uvHeight = height / 2; - uint32_t dataSize = yStride * height + 2 * (uvHeight * uvStride); - if (pixels_size != dataSize) { - DDD("failed %s %d\n", __func__, __LINE__); - return; - } - DDD("%s %d reading %d bytes\n", __func__, __LINE__, (int)dataSize); - } + YUV_DEBUG_LOG("w:%d h:%d format:%d pixels:%p pixels-size:%d", mWidth, mHeight, mFormat, pixels, pixels_size); - uint32_t yoff, uoff, voff, ywidth, cwidth; - getYUVOffsets(width, height, mFormat, &yoff, &uoff, &voff, &ywidth, - &cwidth); + uint32_t yOffset, uOffset, vOffset, ywidth, cwidth; + getYUVOffsets(mWidth, mHeight, mFormat, &yOffset, &uOffset, &vOffset, &ywidth, &cwidth); - if (mFormat == FRAMEWORK_FORMAT_YUV_420_888) { - if (feature_is_enabled( - kFeature_YUV420888toNV21)) { - readYUVTex(mVUtex, pixels + voff, true); - DDD("done"); - } else { - readYUVTex(mUtex, pixels + uoff, false); - readYUVTex(mVtex, pixels + voff, false); - DDD("done"); - } - } else if (mFormat == FRAMEWORK_FORMAT_NV12) { - readYUVTex(mUVtex, pixels + uoff, true); - if (mCbFormat == FRAMEWORK_FORMAT_YUV_420_888) { - // do a conversion here inplace: NV12 to YUV 420 888 - uint8_t* scrath_memory = pixels; - NV12ToYUV420PlanarInPlaceConvert(width, height, pixels, - scrath_memory); - DDD("done"); - } - DDD("done"); - } else if (mFormat == FRAMEWORK_FORMAT_YV12) { - readYUVTex(mUtex, pixels + uoff, false); - readYUVTex(mVtex, pixels + voff, false); - DDD("done"); + if (isInterleaved(mFormat)) { + readYUVTex(mTextureV, mFormat, YUVPlane::UV, pixels + std::min(uOffset, vOffset)); + } else { + readYUVTex(mTextureU, mFormat, YUVPlane::U, pixels + uOffset); + readYUVTex(mTextureV, mFormat, YUVPlane::V, pixels + vOffset); + } + + if (mFormat == FRAMEWORK_FORMAT_NV12 && mColorBufferFormat == FRAMEWORK_FORMAT_YUV_420_888) { + NV12ToYUV420PlanarInPlaceConvert(mWidth, mHeight, pixels, pixels); } - // read Y the last, because we can might used it as a scratch space - readYUVTex(mYtex, pixels + yoff, false); - DDD("done"); + + // Read the Y plane last because so that we can use it as a scratch space. + readYUVTex(mTextureY, mFormat, YUVPlane::Y, pixels + yOffset); } void YUVConverter::swapTextures(uint32_t type, uint32_t* textures) { - if (type == FRAMEWORK_FORMAT_NV12) { - mFormat = FRAMEWORK_FORMAT_NV12; - std::swap(textures[0], mYtex); - std::swap(textures[1], mUVtex); - } else if (type == FRAMEWORK_FORMAT_YUV_420_888) { - mFormat = FRAMEWORK_FORMAT_YUV_420_888; - std::swap(textures[0], mYtex); - std::swap(textures[1], mUtex); - std::swap(textures[2], mVtex); + FrameworkFormat format = static_cast<FrameworkFormat>(type); + + if (isInterleaved(format)) { + std::swap(textures[0], mTextureY); + std::swap(textures[1], mTextureU); + mTextureV = mTextureU; } else { - FATAL("Unknown format: 0x%x", type); + std::swap(textures[0], mTextureY); + std::swap(textures[1], mTextureU); + std::swap(textures[2], mTextureV); } + + mFormat = format; } -// drawConvert: per-frame updates. -// Update YUV textures, then draw the fullscreen -// quad set up above, which results in a framebuffer -// with the RGB colors. -void YUVConverter::drawConvert(int x, int y, - int width, int height, - char* pixels) { +void YUVConverter::drawConvert(int x, int y, int width, int height, const char* pixels) { + YUV_DEBUG_LOG("x:%d y:%d w:%d h:%d", x, y, width, height); + saveGLState(); if (pixels && (width != mWidth || height != mHeight)) { reset(); @@ -710,129 +828,52 @@ void YUVConverter::drawConvert(int x, int y, if (mProgram == 0) { init(width, height, mFormat); } + + if (mFormat == FRAMEWORK_FORMAT_P010 && !mHasGlsl3Support) { + // TODO: perhaps fallback to just software conversion. + return; + } + s_gles2.glViewport(x, y, width, height); - uint32_t yoff, uoff, voff, ywidth, cwidth, cheight; - getYUVOffsets(width, height, mFormat, &yoff, &uoff, &voff, &ywidth, - &cwidth); + uint32_t yOffset, uOffset, vOffset, ywidth, cwidth, cheight; + getYUVOffsets(width, height, mFormat, &yOffset, &uOffset, &vOffset, &ywidth, &cwidth); cheight = height / 2; updateCutoffs(width, ywidth, width / 2, cwidth); - if (!pixels) { + if (pixels) { + subUpdateYUVGLTex(GL_TEXTURE0, mTextureY, x, y, ywidth, height, mFormat, YUVPlane::Y, pixels + yOffset); + if (isInterleaved(mFormat)) { + subUpdateYUVGLTex(GL_TEXTURE1, mTextureU, x, y, cwidth, cheight, mFormat, YUVPlane::UV, pixels + std::min(uOffset, vOffset)); + } else { + subUpdateYUVGLTex(GL_TEXTURE1, mTextureU, x, y, cwidth, cheight, mFormat, YUVPlane::U, pixels + uOffset); + subUpdateYUVGLTex(GL_TEXTURE2, mTextureV, x, y, cwidth, cheight, mFormat, YUVPlane::V, pixels + vOffset); + } + } else { // special case: draw from texture, only support NV12 for now // as cuvid's native format is NV12. // TODO: add more formats if there are such needs in the future. assert(mFormat == FRAMEWORK_FORMAT_NV12); - s_gles2.glActiveTexture(GL_TEXTURE1); - s_gles2.glBindTexture(GL_TEXTURE_2D, mUVtex); - s_gles2.glActiveTexture(GL_TEXTURE0); - s_gles2.glBindTexture(GL_TEXTURE_2D, mYtex); - - doYUVConversionDraw(mProgram, mYWidthCutoffLoc, mCWidthCutoffLoc, - mYSamplerLoc, mUSamplerLoc, mVSamplerLoc, - mVUSamplerLoc, mInCoordLoc, mPosLoc, mVbuf, mIbuf, - width, ywidth, width / 2, cwidth, mYWidthCutoff, - mCWidthCutoff, true); - - restoreGLState(); - return; } - subUpdateYUVGLTex(GL_TEXTURE0, mYtex, - x, y, ywidth, height, - pixels + yoff, false); - - switch (mFormat) { - case FRAMEWORK_FORMAT_YV12: - subUpdateYUVGLTex(GL_TEXTURE1, mUtex, - x, y, cwidth, cheight, - pixels + uoff, false); - subUpdateYUVGLTex(GL_TEXTURE2, mVtex, - x, y, cwidth, cheight, - pixels + voff, false); - doYUVConversionDraw(mProgram, - mYWidthCutoffLoc, - mCWidthCutoffLoc, - mYSamplerLoc, - mUSamplerLoc, - mVSamplerLoc, - mVUSamplerLoc, - mInCoordLoc, - mPosLoc, - mVbuf, mIbuf, - width, ywidth, - width / 2, cwidth, - mYWidthCutoff, - mCWidthCutoff, - false); - break; - case FRAMEWORK_FORMAT_YUV_420_888: - if (feature_is_enabled( - kFeature_YUV420888toNV21)) { - subUpdateYUVGLTex(GL_TEXTURE1, mVUtex, - x, y, cwidth, cheight, - pixels + voff, true); - doYUVConversionDraw(mProgram, - mYWidthCutoffLoc, - mCWidthCutoffLoc, - mYSamplerLoc, - mUSamplerLoc, - mVSamplerLoc, - mVUSamplerLoc, - mInCoordLoc, - mPosLoc, - mVbuf, mIbuf, - width, ywidth, - width / 2, cwidth, - mYWidthCutoff, - mCWidthCutoff, - true); - } else { - subUpdateYUVGLTex(GL_TEXTURE1, mUtex, - x, y, cwidth, cheight, - pixels + uoff, false); - subUpdateYUVGLTex(GL_TEXTURE2, mVtex, - x, y, cwidth, cheight, - pixels + voff, false); - doYUVConversionDraw(mProgram, - mYWidthCutoffLoc, - mCWidthCutoffLoc, - mYSamplerLoc, - mUSamplerLoc, - mVSamplerLoc, - mVUSamplerLoc, - mInCoordLoc, - mPosLoc, - mVbuf, mIbuf, - width, ywidth, - width / 2, cwidth, - mYWidthCutoff, - mCWidthCutoff, - false); - } - break; - case FRAMEWORK_FORMAT_NV12: - subUpdateYUVGLTex(GL_TEXTURE1, mUVtex, - x, y, cwidth, cheight, - pixels + uoff, true); - doYUVConversionDraw(mProgram, - mYWidthCutoffLoc, - mCWidthCutoffLoc, - mYSamplerLoc, - mUSamplerLoc, - mVSamplerLoc, - mVUSamplerLoc, - mInCoordLoc, - mPosLoc, - mVbuf, mIbuf, - width, ywidth, - width / 2, cwidth, - mYWidthCutoff, - mCWidthCutoff, - true); - break; - default: - FATAL("Unknown format: 0x%x", mFormat); - } + s_gles2.glActiveTexture(GL_TEXTURE0); + s_gles2.glBindTexture(GL_TEXTURE_2D, mTextureY); + s_gles2.glActiveTexture(GL_TEXTURE1); + s_gles2.glBindTexture(GL_TEXTURE_2D, mTextureU); + s_gles2.glActiveTexture(GL_TEXTURE2); + s_gles2.glBindTexture(GL_TEXTURE_2D, mTextureV); + + doYUVConversionDraw(mProgram, + mUniformLocYWidthCutoff, + mUniformLocCWidthCutoff, + mUniformLocSamplerY, + mUniformLocSamplerU, + mUniformLocSamplerV, + mAttributeLocTexCoord, + mAttributeLocPos, + mQuadVertexBuffer, + mQuadIndexBuffer, + mYWidthCutoff, + mCWidthCutoff); restoreGLState(); } @@ -844,11 +885,9 @@ void YUVConverter::updateCutoffs(float width, float ywidth, mYWidthCutoff = ((float)width) / ((float)ywidth); mCWidthCutoff = ((float)halfwidth) / ((float)cwidth); break; - case FRAMEWORK_FORMAT_YUV_420_888: - mYWidthCutoff = 1.0f; - mCWidthCutoff = 1.0f; - break; case FRAMEWORK_FORMAT_NV12: + case FRAMEWORK_FORMAT_P010: + case FRAMEWORK_FORMAT_YUV_420_888: mYWidthCutoff = 1.0f; mCWidthCutoff = 1.0f; break; @@ -858,22 +897,22 @@ void YUVConverter::updateCutoffs(float width, float ywidth, } void YUVConverter::reset() { - if (mIbuf) s_gles2.glDeleteBuffers(1, &mIbuf); - if (mVbuf) s_gles2.glDeleteBuffers(1, &mVbuf); + if (mQuadIndexBuffer) s_gles2.glDeleteBuffers(1, &mQuadIndexBuffer); + if (mQuadVertexBuffer) s_gles2.glDeleteBuffers(1, &mQuadVertexBuffer); if (mProgram) s_gles2.glDeleteProgram(mProgram); - if (mYtex) s_gles2.glDeleteTextures(1, &mYtex); - if (mUtex) s_gles2.glDeleteTextures(1, &mUtex); - if (mVtex) s_gles2.glDeleteTextures(1, &mVtex); - if (mVUtex) s_gles2.glDeleteTextures(1, &mVUtex); - if (mUVtex) s_gles2.glDeleteTextures(1, &mUVtex); - mIbuf = 0; - mVbuf = 0; + if (mTextureY) s_gles2.glDeleteTextures(1, &mTextureY); + if (isInterleaved(mFormat)) { + if (mTextureU) s_gles2.glDeleteTextures(1, &mTextureU); + } else { + if (mTextureU) s_gles2.glDeleteTextures(1, &mTextureU); + if (mTextureV) s_gles2.glDeleteTextures(1, &mTextureV); + } + mQuadIndexBuffer = 0; + mQuadVertexBuffer = 0; mProgram = 0; - mYtex = 0; - mUtex = 0; - mVtex = 0; - mVUtex = 0; - mUVtex = 0; + mTextureY = 0; + mTextureU = 0; + mTextureV = 0; } YUVConverter::~YUVConverter() { diff --git a/stream-servers/YUVConverter.h b/stream-servers/YUVConverter.h index e323b546..0140f896 100644 --- a/stream-servers/YUVConverter.h +++ b/stream-servers/YUVConverter.h @@ -24,6 +24,14 @@ #include <stdint.h> #include <cstring> #include <vector> + +enum class YUVPlane { + Y = 0, + U = 1, + V = 2, + UV = 3, +}; + // The purpose of YUVConverter is to use // OpenGL shaders to convert YUV images to RGB // images that can be displayed on screen. @@ -54,7 +62,7 @@ public: // call when gralloc_unlock updates // the host color buffer // (rcUpdateColorBuffer) - void drawConvert(int x, int y, int width, int height, char* pixels); + void drawConvert(int x, int y, int width, int height, const char* pixels); uint32_t getDataSize(); // read YUV data into pixels, exactly pixels_size bytes; @@ -63,65 +71,46 @@ public: void swapTextures(uint32_t type, uint32_t* textures); - // NV12 and YUV420 are all packed - static void NV12ToYUV420PlanarInPlaceConvert(int nWidth, - int nHeight, - uint8_t* pFrame, - uint8_t* pQuad) { - std::vector<uint8_t> tmp; - if (pQuad == nullptr) { - tmp.resize(nWidth * nHeight / 4); - pQuad = tmp.data(); - } - int nPitch = nWidth; - uint8_t *puv = pFrame + nPitch * nHeight, *pu = puv, - *pv = puv + nPitch * nHeight / 4; - for (int y = 0; y < nHeight / 2; y++) { - for (int x = 0; x < nWidth / 2; x++) { - pu[y * nPitch / 2 + x] = puv[y * nPitch + x * 2]; - pQuad[y * nWidth / 2 + x] = puv[y * nPitch + x * 2 + 1]; - } - } - memcpy(pv, pQuad, nWidth * nHeight / 4); - } - // public so other classes can call - static void createYUVGLTex(GLenum texture_unit, + static void createYUVGLTex(GLenum textureUnit, GLsizei width, GLsizei height, - GLuint* texName_out, - bool uvInterleaved); + FrameworkFormat format, + YUVPlane plane, + GLuint* outTextureName); private: void init(int w, int h, FrameworkFormat format); void reset(); + void createYUVGLShader(); + void createYUVGLFullscreenQuad(); + // For dealing with n-pixel-aligned buffers void updateCutoffs(float width, float ywidth, float halfwidth, float cwidth); + int mWidth = 0; int mHeight = 0; FrameworkFormat mFormat; // colorbuffer w/h/format, could be different - FrameworkFormat mCbFormat; + FrameworkFormat mColorBufferFormat; // We need the following GL objects: GLuint mProgram = 0; - GLuint mVbuf = 0; - GLuint mIbuf = 0; - GLuint mYtex = 0; - GLuint mUtex = 0; - GLuint mVtex = 0; - GLuint mVUtex = 0; - GLuint mUVtex = 0; - // shader uniform locations - GLint mYWidthCutoffLoc = -1; - GLint mCWidthCutoffLoc = -1; - GLint mYSamplerLoc = -1; - GLint mUSamplerLoc = -1; - GLint mVSamplerLoc = -1; - GLint mVUSamplerLoc = -1; - GLint mInCoordLoc = -1; - GLint mPosLoc = -1; + GLuint mQuadVertexBuffer = 0; + GLuint mQuadIndexBuffer = 0; + GLuint mTextureY = 0; + GLuint mTextureU = 0; + GLuint mTextureV = 0; + GLint mUniformLocYWidthCutoff = -1; + GLint mUniformLocCWidthCutoff = -1; + GLint mUniformLocSamplerY = -1; + GLint mUniformLocSamplerU = -1; + GLint mUniformLocSamplerV = -1; + GLint mAttributeLocPos = -1; + GLint mAttributeLocTexCoord = -1; + float mYWidthCutoff = 1.0; float mCWidthCutoff = 1.0; + bool mHasGlsl3Support = false; // YUVConverter can end up being used // in a TextureDraw / subwindow context, and subsequently diff --git a/stream-servers/tests/DefaultFramebufferBlit_unittest.cpp b/stream-servers/tests/DefaultFramebufferBlit_unittest.cpp index 3003b04c..8fb34005 100644 --- a/stream-servers/tests/DefaultFramebufferBlit_unittest.cpp +++ b/stream-servers/tests/DefaultFramebufferBlit_unittest.cpp @@ -13,6 +13,7 @@ // limitations under the License. #include <gtest/gtest.h> +#include "host-common/testing/MockAndroidAgentFactory.h" #include "Standalone.h" #include "GLTestUtils.h" @@ -172,6 +173,13 @@ static constexpr float kDrawColorGreen[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; class CombinedFramebufferBlit : public ::testing::Test, public ::testing::WithParamInterface<ClearColorParam> { protected: + static void SetUpTestSuite() { + android::emulation::injectConsoleAgents( + android::emulation::MockAndroidConsoleFactory()); + } + + static void TearDownTestSuite() { } + virtual void SetUp() override { mApp.reset(new ClearColor(GetParam())); } diff --git a/stream-servers/tests/FrameBuffer_unittest.cpp b/stream-servers/tests/FrameBuffer_unittest.cpp index f4266b6f..d211953f 100644 --- a/stream-servers/tests/FrameBuffer_unittest.cpp +++ b/stream-servers/tests/FrameBuffer_unittest.cpp @@ -19,6 +19,7 @@ #include "base/testing/TestSystem.h" #include "host-common/AndroidAgentFactory.h" #include "host-common/multi_display_agent.h" +#include "host-common/testing/MockAndroidAgentFactory.h" #include "host-common/window_agent.h" #include "host-common/MultiDisplay.h" #include "snapshot/TextureLoader.h" @@ -53,6 +54,13 @@ public: protected: + static void SetUpTestSuite() { + android::emulation::injectConsoleAgents( + android::emulation::MockAndroidConsoleFactory()); + } + + static void TearDownTestSuite() { } + virtual void SetUp() override { // setupStandaloneLibrarySearchPaths(); emugl::setGLObjectCounter(android::base::GLObjectCounter::get()); diff --git a/stream-servers/tests/GLSnapshotRasterization_unittest.cpp b/stream-servers/tests/GLSnapshotRasterization_unittest.cpp index 9719dc34..3f016af5 100644 --- a/stream-servers/tests/GLSnapshotRasterization_unittest.cpp +++ b/stream-servers/tests/GLSnapshotRasterization_unittest.cpp @@ -22,9 +22,6 @@ namespace emugl { // Line width settings to attempt static const GLfloat kGLES2TestLineWidths[] = {2.0f}; -// Polygon offset settings to attempt -static const GLfloat kGLES2TestPolygonOffset[] = {0.5f, 0.5f}; - class SnapshotGlLineWidthTest : public SnapshotSetValueTest<GLfloat>, public ::testing::WithParamInterface<GLfloat> { void stateCheck(GLfloat expected) override { @@ -85,8 +82,7 @@ INSTANTIATE_TEST_SUITE_P(GLES2SnapshotRasterization, ::testing::ValuesIn(kGLES2FrontFaceModes)); class SnapshotGlPolygonOffsetTest - : public SnapshotSetValueTest<GLfloat*>, - public ::testing::WithParamInterface<const GLfloat*> { + : public SnapshotSetValueTest<GLfloat*> { void stateCheck(GLfloat* expected) override { EXPECT_TRUE(compareGlobalGlFloat(gl, GL_POLYGON_OFFSET_FACTOR, expected[0])); @@ -94,19 +90,16 @@ class SnapshotGlPolygonOffsetTest compareGlobalGlFloat(gl, GL_POLYGON_OFFSET_UNITS, expected[1])); } void stateChange() override { - gl->glPolygonOffset(GetParam()[0], GetParam()[1]); + gl->glPolygonOffset(0.5f, 0.5f); } }; -TEST_P(SnapshotGlPolygonOffsetTest, SetPolygonOffset) { +TEST_F(SnapshotGlPolygonOffsetTest, SetPolygonOffset) { GLfloat defaultOffset[2] = {0.0f, 0.0f}; - GLfloat testOffset[2] = {GetParam()[0], GetParam()[1]}; + GLfloat testOffset[2] = {0.5f, 0.5f}; setExpectedValues(defaultOffset, testOffset); doCheckedSnapshot(); } -INSTANTIATE_TEST_SUITE_P(GLES2SnapshotRasterization, - SnapshotGlPolygonOffsetTest, - ::testing::Values(kGLES2TestPolygonOffset)); } // namespace emugl diff --git a/stream-servers/tests/GLSnapshotRendering_unittest.cpp b/stream-servers/tests/GLSnapshotRendering_unittest.cpp index 34449841..ab39e851 100644 --- a/stream-servers/tests/GLSnapshotRendering_unittest.cpp +++ b/stream-servers/tests/GLSnapshotRendering_unittest.cpp @@ -17,6 +17,7 @@ #include "Standalone.h" #include "HelloTriangle.h" #include "host-common/AndroidAgentFactory.h" +#include "host-common/testing/MockAndroidAgentFactory.h" #include <gtest/gtest.h> @@ -60,6 +61,13 @@ protected: template <typename T> class SnapshotGlRenderingSampleTest : public ::testing::Test { protected: + static void SetUpTestSuite() { + android::emulation::injectConsoleAgents( + android::emulation::MockAndroidConsoleFactory()); + } + + static void TearDownTestSuite() { } + virtual void SetUp() override { // setupStandaloneLibrarySearchPaths(); emugl::set_emugl_window_operations(*getConsoleAgents()->emu); diff --git a/stream-servers/tests/VirtioGpuTimelines_unittest.cpp b/stream-servers/tests/VirtioGpuTimelines_unittest.cpp index 8b3a8d47..a5aa1489 100644 --- a/stream-servers/tests/VirtioGpuTimelines_unittest.cpp +++ b/stream-servers/tests/VirtioGpuTimelines_unittest.cpp @@ -140,8 +140,3 @@ TEST_F(VirtioGpuTimelinesTest, TasksAndFencesOnMultipleContexts) { check.Call(3); mVirtioGpuTimelines->notifyTaskCompletion(taskId3); } - -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -}
\ No newline at end of file diff --git a/stream-servers/tests/Vulkan_unittest.cpp b/stream-servers/tests/Vulkan_unittest.cpp index 44c337d9..708fb2ec 100644 --- a/stream-servers/tests/Vulkan_unittest.cpp +++ b/stream-servers/tests/Vulkan_unittest.cpp @@ -25,6 +25,7 @@ #include "base/System.h" #include "base/testing/TestSystem.h" #include "host-common/AndroidAgentFactory.h" +#include "host-common/testing/MockAndroidAgentFactory.h" #include "Standalone.h" @@ -416,6 +417,13 @@ static void teardownVulkanTest(const VulkanDispatch* vk, class VulkanTest : public ::testing::Test { protected: + static void SetUpTestSuite() { + android::emulation::injectConsoleAgents( + android::emulation::MockAndroidConsoleFactory()); + } + + static void TearDownTestSuite() { } + void SetUp() override { goldfish_vk::init_vulkan_dispatch_from_system_loader( dlOpenFuncForTesting, diff --git a/stream-servers/virtio-gpu-gfxstream-renderer.cpp b/stream-servers/virtio-gpu-gfxstream-renderer.cpp index 0cb0af6f..f469fc39 100644 --- a/stream-servers/virtio-gpu-gfxstream-renderer.cpp +++ b/stream-servers/virtio-gpu-gfxstream-renderer.cpp @@ -202,6 +202,7 @@ static inline uint32_t align_up_power_of_2(uint32_t n, uint32_t a) { #define VIRGL_FORMAT_NV12 166 #define VIRGL_FORMAT_YV12 163 +#define VIRGL_FORMAT_P010 314 const uint32_t kGlBgra = 0x80e1; const uint32_t kGlRgba = 0x1908; @@ -220,6 +221,7 @@ constexpr uint32_t kFwkFormatGlCompat = 0; constexpr uint32_t kFwkFormatYV12 = 1; // constexpr uint32_t kFwkFormatYUV420888 = 2; constexpr uint32_t kFwkFormatNV12 = 3; +constexpr uint32_t kFwkFormatP010 = 4; static inline bool virgl_format_is_yuv(uint32_t format) { switch (format) { @@ -235,6 +237,7 @@ static inline bool virgl_format_is_yuv(uint32_t format) { case VIRGL_FORMAT_R10G10B10A2_UNORM: return false; case VIRGL_FORMAT_NV12: + case VIRGL_FORMAT_P010: case VIRGL_FORMAT_YV12: return true; default: @@ -262,6 +265,7 @@ static inline uint32_t virgl_format_to_gl(uint32_t virgl_format) { case VIRGL_FORMAT_R8G8_UNORM: return kGlRg8; case VIRGL_FORMAT_NV12: + case VIRGL_FORMAT_P010: case VIRGL_FORMAT_YV12: // emulated as RGBA8888 return kGlRgba; @@ -276,6 +280,8 @@ static inline uint32_t virgl_format_to_fwk_format(uint32_t virgl_format) { switch (virgl_format) { case VIRGL_FORMAT_NV12: return kFwkFormatNV12; + case VIRGL_FORMAT_P010: + return kFwkFormatP010; case VIRGL_FORMAT_YV12: return kFwkFormatYV12; case VIRGL_FORMAT_R8_UNORM: @@ -349,10 +355,11 @@ static inline size_t virgl_format_to_total_xfer_len( uint32_t totalWidth, uint32_t totalHeight, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { if (virgl_format_is_yuv(format)) { + uint32_t bpp = format == VIRGL_FORMAT_P010 ? 2 : 1; uint32_t yAlign = (format == VIRGL_FORMAT_YV12) ? 32 : 16; uint32_t yWidth = totalWidth; uint32_t yHeight = totalHeight; - uint32_t yStride = align_up_power_of_2(yWidth, yAlign); + uint32_t yStride = align_up_power_of_2(yWidth, yAlign) * bpp; uint32_t ySize = yStride * yHeight; uint32_t uvAlign = 16; @@ -361,6 +368,9 @@ static inline size_t virgl_format_to_total_xfer_len( if (format == VIRGL_FORMAT_NV12) { uvWidth = totalWidth; uvPlaneCount = 1; + } else if (format == VIRGL_FORMAT_P010) { + uvWidth = totalWidth; + uvPlaneCount = 1; } else if (format == VIRGL_FORMAT_YV12) { uvWidth = totalWidth / 2; uvPlaneCount = 2; @@ -368,7 +378,7 @@ static inline size_t virgl_format_to_total_xfer_len( VGP_FATAL() << "Unknown yuv virgl format: 0x" << std::hex << format; } uint32_t uvHeight = totalHeight / 2; - uint32_t uvStride = align_up_power_of_2(uvWidth, uvAlign); + uint32_t uvStride = align_up_power_of_2(uvWidth, uvAlign) * bpp; uint32_t uvSize = uvStride * uvHeight * uvPlaneCount; uint32_t dataSize = ySize + uvSize; diff --git a/stream-servers/vulkan/VkCommonOperations.cpp b/stream-servers/vulkan/VkCommonOperations.cpp index 60d8a5cf..abf7677d 100644 --- a/stream-servers/vulkan/VkCommonOperations.cpp +++ b/stream-servers/vulkan/VkCommonOperations.cpp @@ -587,7 +587,8 @@ VkEmulation* createGlobalVkEmulation(VulkanDispatch* vk) { VkResult res = gvk->vkCreateInstance(&instCi, nullptr, &sVkEmulation->instance); if (res != VK_SUCCESS) { - VK_EMU_INIT_RETURN_ON_ERROR("Failed to create Vulkan instance."); + VK_EMU_INIT_RETURN_ON_ERROR("Failed to create Vulkan instance. Error %s.", + string_VkResult(res)); } // Create instance level dispatch. @@ -621,7 +622,8 @@ VkEmulation* createGlobalVkEmulation(VulkanDispatch* vk) { VkResult res = gvk->vkCreateInstance(&instCi, nullptr, &sVkEmulation->instance); if (res != VK_SUCCESS) { - VK_EMU_INIT_RETURN_ON_ERROR("Failed to create Vulkan 1.1 instance."); + VK_EMU_INIT_RETURN_ON_ERROR("Failed to create Vulkan 1.1 instance. Error %s.", + string_VkResult(res)); } init_vulkan_dispatch_from_instance( @@ -968,7 +970,8 @@ VkEmulation* createGlobalVkEmulation(VulkanDispatch* vk) { &sVkEmulation->device); if (res != VK_SUCCESS) { - VK_EMU_INIT_RETURN_ON_ERROR("Failed to create Vulkan device."); + VK_EMU_INIT_RETURN_ON_ERROR("Failed to create Vulkan device. Error %s.", + string_VkResult(res)); } // device created; populate dispatch table @@ -1540,11 +1543,16 @@ static std::unique_ptr<VkImageCreateInfo> generateColorBufferVkImageCreateInfo_l const VkFormatProperties& formatProperties = *maybeFormatProperties; constexpr std::pair<VkFormatFeatureFlags, VkImageUsageFlags> formatUsagePairs[] = { - {VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT}, - {VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT, VK_IMAGE_USAGE_SAMPLED_BIT}, - {VK_FORMAT_FEATURE_TRANSFER_SRC_BIT, VK_IMAGE_USAGE_TRANSFER_SRC_BIT}, - {VK_FORMAT_FEATURE_TRANSFER_DST_BIT, VK_IMAGE_USAGE_TRANSFER_DST_BIT}, - {VK_FORMAT_FEATURE_BLIT_SRC_BIT, VK_IMAGE_USAGE_TRANSFER_SRC_BIT}, + {VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT}, + {VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT, + VK_IMAGE_USAGE_SAMPLED_BIT}, + {VK_FORMAT_FEATURE_TRANSFER_SRC_BIT, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT}, + {VK_FORMAT_FEATURE_TRANSFER_DST_BIT, + VK_IMAGE_USAGE_TRANSFER_DST_BIT}, + {VK_FORMAT_FEATURE_BLIT_SRC_BIT, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT}, }; VkFormatFeatureFlags tilingFeatures = (tiling == VK_IMAGE_TILING_OPTIMAL) ? formatProperties.optimalTilingFeatures @@ -2614,8 +2622,9 @@ static std::tuple<VkCommandBuffer, VkFence> allocateQueueTransferCommandBuffer_l if (res == VK_NOT_READY) { continue; } - GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) - << "Invalid fence state: " << static_cast<int>(res); + // We either have a device lost, or an invalid fence state. For the device lost case, + // VK_CHECK will ensure we capture the relevant streams. + VK_CHECK(res); } VkCommandBuffer commandBuffer; VkCommandBufferAllocateInfo allocateInfo = { @@ -2657,7 +2666,8 @@ void acquireColorBuffersForHostComposing(const std::vector<uint32_t>& layerColor colorBuffersAndLayouts.emplace_back( layerColorBuffer, FrameBuffer::getFB()->getVkImageLayoutForComposeLayer()); } - colorBuffersAndLayouts.emplace_back(renderTargetColorBuffer, VK_IMAGE_LAYOUT_UNDEFINED); + colorBuffersAndLayouts.emplace_back(renderTargetColorBuffer, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); AutoLock lock(sVkEmulationLock); auto vk = sVkEmulation->dvk; @@ -2711,8 +2721,7 @@ void acquireColorBuffersForHostComposing(const std::vector<uint32_t>& layerColor std::vector<VkImageMemoryBarrier> layoutTransitionBarriers; for (auto [infoPtr, newLayout] : colorBufferInfosAndLayouts) { - infoPtr->currentLayout = newLayout; - if (newLayout == VK_IMAGE_LAYOUT_UNDEFINED) { + if (newLayout == VK_IMAGE_LAYOUT_UNDEFINED || infoPtr->currentLayout == newLayout) { continue; } VkImageMemoryBarrier layoutTransitionBarrier = { @@ -2737,6 +2746,7 @@ void acquireColorBuffersForHostComposing(const std::vector<uint32_t>& layerColor }, }; layoutTransitionBarriers.emplace_back(layoutTransitionBarrier); + infoPtr->currentLayout = newLayout; } auto [commandBuffer, fence] = allocateQueueTransferCommandBuffer_locked(); |