summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-28 19:44:31 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-28 19:44:31 +0000
commitf4b6b758c3da21807c5cced3a150f68f4b6eac8c (patch)
treec14dcb0be2165778e237b7d8185dd53fa82b1993
parent5b2ca6ff5f2f40c4cca83d9984ee26f5f7352f9a (diff)
parent7eb1b27a6873bb989c15b962047117e5fdab7932 (diff)
downloadvulkan-cereal-f4b6b758c3da21807c5cced3a150f68f4b6eac8c.tar.gz
Snap for 8505378 from 7eb1b27a6873bb989c15b962047117e5fdab7932 to mainline-go-uwb-release
Change-Id: Icc6eaacb70a2266235b7c97aa98bd90714e126db
-rw-r--r--host-common/FeatureControlDefHost.h1
-rw-r--r--host-common/HostAddressSpace_unittest.cpp7
-rw-r--r--host-common/address_space_graphics_unittests.cpp7
-rw-r--r--host-common/testing/MockAndroidAgentFactory.cpp35
-rw-r--r--host-common/testing/MockAndroidAgentFactory.h16
-rw-r--r--scripts/print_gfx_logs/command_printer.py35
-rw-r--r--scripts/print_gfx_logs/vulkan_printer.py35
-rw-r--r--stream-servers/CMakeLists.txt4
-rw-r--r--stream-servers/ColorBuffer.cpp1
-rw-r--r--stream-servers/DisplayVk.cpp2
-rw-r--r--stream-servers/FrameBuffer.cpp35
-rw-r--r--stream-servers/FrameworkFormats.h1
-rw-r--r--stream-servers/PostWorker.cpp2
-rw-r--r--stream-servers/RenderControl.cpp10
-rw-r--r--stream-servers/SyncThread.cpp6
-rw-r--r--stream-servers/YUVConverter.cpp1295
-rw-r--r--stream-servers/YUVConverter.h75
-rw-r--r--stream-servers/tests/DefaultFramebufferBlit_unittest.cpp8
-rw-r--r--stream-servers/tests/FrameBuffer_unittest.cpp8
-rw-r--r--stream-servers/tests/GLSnapshotRasterization_unittest.cpp15
-rw-r--r--stream-servers/tests/GLSnapshotRendering_unittest.cpp8
-rw-r--r--stream-servers/tests/VirtioGpuTimelines_unittest.cpp5
-rw-r--r--stream-servers/tests/Vulkan_unittest.cpp8
-rw-r--r--stream-servers/virtio-gpu-gfxstream-renderer.cpp14
-rw-r--r--stream-servers/vulkan/VkCommonOperations.cpp36
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();