mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 21:21:17 +01:00
fix(Vulkan) Work around a Vulkan driver OOM on Mali devices (#11271) 37439d5fb6
The Mali Vulkan driver/device was running out of memory internally, occasionally. This adds a hook into the testing window that gets run after each GM finishes, which TestingWindowAndroidVulkan now uses to tear the device down completely. The device then gets rebuilt as needed. Co-authored-by: Josh Jersild <joshua@rive.app>
This commit is contained in:
@@ -1 +1 @@
|
|||||||
1dd286c979e2168ac0f899ca55b562ace933c3e3
|
37439d5fb6388d0315fdea9b9772f0213ef1dfbc
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ public:
|
|||||||
bool coreFeaturesOnly = false;
|
bool coreFeaturesOnly = false;
|
||||||
const char* gpuNameFilter = nullptr;
|
const char* gpuNameFilter = nullptr;
|
||||||
bool headless = false;
|
bool headless = false;
|
||||||
|
bool printInitializationMessage = true;
|
||||||
|
|
||||||
uint32_t minimumSupportedAPIVersion = VK_API_VERSION_1_0;
|
uint32_t minimumSupportedAPIVersion = VK_API_VERSION_1_0;
|
||||||
|
|
||||||
@@ -29,6 +30,13 @@ public:
|
|||||||
VkSurfaceKHR presentationSurfaceForDeviceSelection = VK_NULL_HANDLE;
|
VkSurfaceKHR presentationSurfaceForDeviceSelection = VK_NULL_HANDLE;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DriverVersion
|
||||||
|
{
|
||||||
|
uint32_t major;
|
||||||
|
uint32_t minor;
|
||||||
|
uint32_t patch;
|
||||||
|
};
|
||||||
|
|
||||||
VulkanDevice(VulkanInstance&, const Options&);
|
VulkanDevice(VulkanInstance&, const Options&);
|
||||||
~VulkanDevice();
|
~VulkanDevice();
|
||||||
|
|
||||||
@@ -66,6 +74,8 @@ public:
|
|||||||
|
|
||||||
const std::string& name() const { return m_name; }
|
const std::string& name() const { return m_name; }
|
||||||
|
|
||||||
|
const DriverVersion& driverVersion() const { return m_driverVersion; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct FindDeviceResult
|
struct FindDeviceResult
|
||||||
{
|
{
|
||||||
@@ -73,9 +83,7 @@ private:
|
|||||||
std::string deviceName;
|
std::string deviceName;
|
||||||
VkPhysicalDeviceType deviceType;
|
VkPhysicalDeviceType deviceType;
|
||||||
uint32_t deviceAPIVersion;
|
uint32_t deviceAPIVersion;
|
||||||
uint32_t driverVersionMajor;
|
DriverVersion driverVersion;
|
||||||
uint32_t driverVersionMinor;
|
|
||||||
uint32_t driverVersionPatch;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static FindDeviceResult findCompatiblePhysicalDevice(
|
static FindDeviceResult findCompatiblePhysicalDevice(
|
||||||
@@ -103,6 +111,7 @@ private:
|
|||||||
|
|
||||||
std::vector<VkQueueFamilyProperties> m_queueFamilyProperties;
|
std::vector<VkQueueFamilyProperties> m_queueFamilyProperties;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
|
DriverVersion m_driverVersion;
|
||||||
|
|
||||||
VkPhysicalDevice m_physicalDevice;
|
VkPhysicalDevice m_physicalDevice;
|
||||||
VkDevice m_device;
|
VkDevice m_device;
|
||||||
|
|||||||
@@ -32,33 +32,37 @@ static const char* physicalDeviceTypeName(VkPhysicalDeviceType type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void unpackDriverVersion(const VkPhysicalDeviceProperties& props,
|
VulkanDevice::DriverVersion unpackDriverVersion(
|
||||||
uint32_t* majorOut,
|
const VkPhysicalDeviceProperties& props)
|
||||||
uint32_t* minorOut,
|
|
||||||
uint32_t* patchOut)
|
|
||||||
{
|
{
|
||||||
if (props.vendorID == VULKAN_VENDOR_NVIDIA)
|
if (props.vendorID == VULKAN_VENDOR_NVIDIA)
|
||||||
{
|
{
|
||||||
// NVidia uses 10|8|8|6 encoding for driver version. We'll ignore the
|
// NVidia uses 10|8|8|6 encoding for driver version. We'll ignore the
|
||||||
// fourth version section.
|
// fourth version section.
|
||||||
*majorOut = props.driverVersion >> 22;
|
return {
|
||||||
*minorOut = (props.driverVersion >> 14) & 0xff;
|
.major = props.driverVersion >> 22,
|
||||||
*patchOut = (props.driverVersion >> 6) & 0xff;
|
.minor = (props.driverVersion >> 14) & 0xff,
|
||||||
|
.patch = (props.driverVersion >> 6) & 0xff,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
else if (props.vendorID == VULKAN_VENDOR_INTEL)
|
else if (props.vendorID == VULKAN_VENDOR_INTEL)
|
||||||
{
|
{
|
||||||
*majorOut = props.driverVersion >> 14;
|
return {
|
||||||
*minorOut = props.driverVersion & 0x3fff;
|
.major = props.driverVersion >> 14,
|
||||||
*patchOut = 0;
|
.minor = props.driverVersion & 0x3fff,
|
||||||
|
.patch = 0,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Everything else seems to use the standard Vulkan encoding.
|
// Everything else seems to use the standard Vulkan encoding.
|
||||||
*majorOut = VK_API_VERSION_MAJOR(props.driverVersion);
|
return {
|
||||||
*minorOut = VK_API_VERSION_MINOR(props.driverVersion);
|
.major = VK_API_VERSION_MAJOR(props.driverVersion),
|
||||||
*patchOut = VK_API_VERSION_PATCH(props.driverVersion);
|
.minor = VK_API_VERSION_MINOR(props.driverVersion),
|
||||||
|
.patch = VK_API_VERSION_PATCH(props.driverVersion),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +87,7 @@ VulkanDevice::VulkanDevice(VulkanInstance& instance, const Options& opts)
|
|||||||
opts.minimumSupportedAPIVersion);
|
opts.minimumSupportedAPIVersion);
|
||||||
m_physicalDevice = findResult.physicalDevice;
|
m_physicalDevice = findResult.physicalDevice;
|
||||||
m_name = findResult.deviceName;
|
m_name = findResult.deviceName;
|
||||||
|
m_driverVersion = findResult.driverVersion;
|
||||||
|
|
||||||
DEFINE_AND_LOAD_INSTANCE_FUNC(vkGetPhysicalDeviceFeatures, instance);
|
DEFINE_AND_LOAD_INSTANCE_FUNC(vkGetPhysicalDeviceFeatures, instance);
|
||||||
assert(vkGetPhysicalDeviceFeatures != nullptr);
|
assert(vkGetPhysicalDeviceFeatures != nullptr);
|
||||||
@@ -252,35 +257,39 @@ VulkanDevice::VulkanDevice(VulkanInstance& instance, const Options& opts)
|
|||||||
LOAD_MEMBER_INSTANCE_FUNC(vkGetPhysicalDeviceSurfaceCapabilitiesKHR,
|
LOAD_MEMBER_INSTANCE_FUNC(vkGetPhysicalDeviceSurfaceCapabilitiesKHR,
|
||||||
instance);
|
instance);
|
||||||
|
|
||||||
printf("==== Vulkan %i.%i.%i GPU (%s): %s (driver %i.%i.%i) [ ",
|
if (opts.printInitializationMessage)
|
||||||
VK_API_VERSION_MAJOR(m_riveVulkanFeatures.apiVersion),
|
|
||||||
VK_API_VERSION_MINOR(m_riveVulkanFeatures.apiVersion),
|
|
||||||
VK_API_VERSION_PATCH(m_riveVulkanFeatures.apiVersion),
|
|
||||||
physicalDeviceTypeName(findResult.deviceType),
|
|
||||||
findResult.deviceName.c_str(),
|
|
||||||
findResult.driverVersionMajor,
|
|
||||||
findResult.driverVersionMinor,
|
|
||||||
findResult.driverVersionPatch);
|
|
||||||
struct CommaSeparator
|
|
||||||
{
|
{
|
||||||
const char* m_separator = "";
|
printf("==== Vulkan %i.%i.%i GPU (%s): %s (driver %i.%i.%i) [ ",
|
||||||
const char* operator*() { return std::exchange(m_separator, ", "); }
|
VK_API_VERSION_MAJOR(m_riveVulkanFeatures.apiVersion),
|
||||||
} commaSeparator;
|
VK_API_VERSION_MINOR(m_riveVulkanFeatures.apiVersion),
|
||||||
if (m_riveVulkanFeatures.independentBlend)
|
VK_API_VERSION_PATCH(m_riveVulkanFeatures.apiVersion),
|
||||||
printf("%sindependentBlend", *commaSeparator);
|
physicalDeviceTypeName(findResult.deviceType),
|
||||||
if (m_riveVulkanFeatures.fillModeNonSolid)
|
findResult.deviceName.c_str(),
|
||||||
printf("%sfillModeNonSolid", *commaSeparator);
|
findResult.driverVersion.major,
|
||||||
if (m_riveVulkanFeatures.fragmentStoresAndAtomics)
|
findResult.driverVersion.minor,
|
||||||
printf("%sfragmentStoresAndAtomics", *commaSeparator);
|
findResult.driverVersion.patch);
|
||||||
if (m_riveVulkanFeatures.shaderClipDistance)
|
struct CommaSeparator
|
||||||
printf("%sshaderClipDistance", *commaSeparator);
|
{
|
||||||
if (m_riveVulkanFeatures.rasterizationOrderColorAttachmentAccess)
|
const char* m_separator = "";
|
||||||
printf("%srasterizationOrderColorAttachmentAccess", *commaSeparator);
|
const char* operator*() { return std::exchange(m_separator, ", "); }
|
||||||
if (m_riveVulkanFeatures.fragmentShaderPixelInterlock)
|
} commaSeparator;
|
||||||
printf("%sfragmentShaderPixelInterlock", *commaSeparator);
|
if (m_riveVulkanFeatures.independentBlend)
|
||||||
if (m_riveVulkanFeatures.VK_KHR_portability_subset)
|
printf("%sindependentBlend", *commaSeparator);
|
||||||
printf("%sVK_KHR_portability_subset", *commaSeparator);
|
if (m_riveVulkanFeatures.fillModeNonSolid)
|
||||||
printf(" ] ====\n");
|
printf("%sfillModeNonSolid", *commaSeparator);
|
||||||
|
if (m_riveVulkanFeatures.fragmentStoresAndAtomics)
|
||||||
|
printf("%sfragmentStoresAndAtomics", *commaSeparator);
|
||||||
|
if (m_riveVulkanFeatures.shaderClipDistance)
|
||||||
|
printf("%sshaderClipDistance", *commaSeparator);
|
||||||
|
if (m_riveVulkanFeatures.rasterizationOrderColorAttachmentAccess)
|
||||||
|
printf("%srasterizationOrderColorAttachmentAccess",
|
||||||
|
*commaSeparator);
|
||||||
|
if (m_riveVulkanFeatures.fragmentShaderPixelInterlock)
|
||||||
|
printf("%sfragmentShaderPixelInterlock", *commaSeparator);
|
||||||
|
if (m_riveVulkanFeatures.VK_KHR_portability_subset)
|
||||||
|
printf("%sVK_KHR_portability_subset", *commaSeparator);
|
||||||
|
printf(" ] ====\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanDevice::~VulkanDevice() { m_vkDestroyDevice(m_device, nullptr); }
|
VulkanDevice::~VulkanDevice() { m_vkDestroyDevice(m_device, nullptr); }
|
||||||
@@ -417,16 +426,12 @@ VulkanDevice::FindDeviceResult VulkanDevice::findCompatiblePhysicalDevice(
|
|||||||
|
|
||||||
if (strstr(props.deviceName, nameFilter) != nullptr)
|
if (strstr(props.deviceName, nameFilter) != nullptr)
|
||||||
{
|
{
|
||||||
uint32_t major, minor, patch;
|
|
||||||
unpackDriverVersion(props, &major, &minor, &patch);
|
|
||||||
matchResult = {
|
matchResult = {
|
||||||
.physicalDevice = device,
|
.physicalDevice = device,
|
||||||
.deviceName = props.deviceName,
|
.deviceName = props.deviceName,
|
||||||
.deviceType = props.deviceType,
|
.deviceType = props.deviceType,
|
||||||
.deviceAPIVersion = props.apiVersion,
|
.deviceAPIVersion = props.apiVersion,
|
||||||
.driverVersionMajor = major,
|
.driverVersion = unpackDriverVersion(props),
|
||||||
.driverVersionMinor = minor,
|
|
||||||
.driverVersionPatch = patch,
|
|
||||||
};
|
};
|
||||||
matchedDeviceNames.push_back(std::string{props.deviceName});
|
matchedDeviceNames.push_back(std::string{props.deviceName});
|
||||||
}
|
}
|
||||||
@@ -484,17 +489,12 @@ VulkanDevice::FindDeviceResult VulkanDevice::findCompatiblePhysicalDevice(
|
|||||||
if (!onlyAcceptDesiredType ||
|
if (!onlyAcceptDesiredType ||
|
||||||
props.deviceType == desiredDeviceType)
|
props.deviceType == desiredDeviceType)
|
||||||
{
|
{
|
||||||
uint32_t major, minor, patch;
|
|
||||||
unpackDriverVersion(props, &major, &minor, &patch);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
.physicalDevice = device,
|
.physicalDevice = device,
|
||||||
.deviceName = props.deviceName,
|
.deviceName = props.deviceName,
|
||||||
.deviceType = props.deviceType,
|
.deviceType = props.deviceType,
|
||||||
.deviceAPIVersion = props.apiVersion,
|
.deviceAPIVersion = props.apiVersion,
|
||||||
.driverVersionMajor = major,
|
.driverVersion = unpackDriverVersion(props),
|
||||||
.driverVersionMinor = minor,
|
|
||||||
.driverVersionPatch = patch,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,6 +170,9 @@ public:
|
|||||||
m_height = height;
|
m_height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is called by the gm testing after each GM runs
|
||||||
|
virtual void onceAfterGM() {}
|
||||||
|
|
||||||
struct FrameOptions
|
struct FrameOptions
|
||||||
{
|
{
|
||||||
uint32_t clearColor;
|
uint32_t clearColor;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ TestingWindow* TestingWindow::MakeAndroidVulkan(const BackendParams&,
|
|||||||
#include <vk_mem_alloc.h>
|
#include <vk_mem_alloc.h>
|
||||||
#include <android/native_app_glue/android_native_app_glue.h>
|
#include <android/native_app_glue/android_native_app_glue.h>
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
using namespace rive;
|
using namespace rive;
|
||||||
using namespace rive::gpu;
|
using namespace rive::gpu;
|
||||||
@@ -45,7 +46,7 @@ class TestingWindowAndroidVulkan : public TestingWindow
|
|||||||
public:
|
public:
|
||||||
TestingWindowAndroidVulkan(const BackendParams& backendParams,
|
TestingWindowAndroidVulkan(const BackendParams& backendParams,
|
||||||
ANativeWindow* window) :
|
ANativeWindow* window) :
|
||||||
m_backendParams(backendParams)
|
m_backendParams(backendParams), m_window(window)
|
||||||
{
|
{
|
||||||
using namespace rive_vkb;
|
using namespace rive_vkb;
|
||||||
|
|
||||||
@@ -80,7 +81,7 @@ public:
|
|||||||
|
|
||||||
VkAndroidSurfaceCreateInfoKHR androidSurfaceCreateInfo = {
|
VkAndroidSurfaceCreateInfoKHR androidSurfaceCreateInfo = {
|
||||||
.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
|
.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
|
||||||
.window = window,
|
.window = m_window,
|
||||||
};
|
};
|
||||||
auto pfnvkCreateAndroidSurfaceKHR =
|
auto pfnvkCreateAndroidSurfaceKHR =
|
||||||
m_instance->loadInstanceFunc<PFN_vkCreateAndroidSurfaceKHR>(
|
m_instance->loadInstanceFunc<PFN_vkCreateAndroidSurfaceKHR>(
|
||||||
@@ -90,99 +91,11 @@ public:
|
|||||||
&androidSurfaceCreateInfo,
|
&androidSurfaceCreateInfo,
|
||||||
nullptr,
|
nullptr,
|
||||||
&m_windowSurface));
|
&m_windowSurface));
|
||||||
|
|
||||||
m_device = std::make_unique<VulkanDevice>(
|
|
||||||
*m_instance,
|
|
||||||
VulkanDevice::Options{
|
|
||||||
.coreFeaturesOnly = m_backendParams.core,
|
|
||||||
});
|
|
||||||
|
|
||||||
m_renderContext = RenderContextVulkanImpl::MakeContext(
|
|
||||||
m_instance->vkInstance(),
|
|
||||||
m_device->vkPhysicalDevice(),
|
|
||||||
m_device->vkDevice(),
|
|
||||||
m_device->vulkanFeatures(),
|
|
||||||
m_instance->getVkGetInstanceProcAddrPtr(),
|
|
||||||
{.forceAtomicMode = backendParams.atomic});
|
|
||||||
|
|
||||||
auto windowCapabilities =
|
|
||||||
m_device->getSurfaceCapabilities(m_windowSurface);
|
|
||||||
|
|
||||||
auto swapOpts = VulkanSwapchain::Options{
|
|
||||||
.formatPreferences =
|
|
||||||
{
|
|
||||||
{
|
|
||||||
.format = m_backendParams.srgb
|
|
||||||
? VK_FORMAT_R8G8B8A8_SRGB
|
|
||||||
: VK_FORMAT_R8G8B8A8_UNORM,
|
|
||||||
.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
|
||||||
},
|
|
||||||
// Fall back to either ordering of ARGB
|
|
||||||
{
|
|
||||||
.format = VK_FORMAT_R8G8B8A8_UNORM,
|
|
||||||
.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.format = VK_FORMAT_B8G8R8A8_UNORM,
|
|
||||||
.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.presentModePreferences =
|
|
||||||
{
|
|
||||||
VK_PRESENT_MODE_IMMEDIATE_KHR,
|
|
||||||
VK_PRESENT_MODE_FIFO_KHR,
|
|
||||||
},
|
|
||||||
.imageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
|
||||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
|
||||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
|
||||||
};
|
|
||||||
|
|
||||||
if ((windowCapabilities.supportedUsageFlags &
|
|
||||||
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) != 0)
|
|
||||||
{
|
|
||||||
swapOpts.imageUsageFlags |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_swapchain =
|
|
||||||
std::make_unique<rive_vkb::VulkanSwapchain>(*m_instance,
|
|
||||||
*m_device,
|
|
||||||
ref_rcp(vk()),
|
|
||||||
m_windowSurface,
|
|
||||||
swapOpts);
|
|
||||||
|
|
||||||
m_androidWindowWidth = m_width = m_swapchain->width();
|
|
||||||
m_androidWindowHeight = m_height = m_swapchain->height();
|
|
||||||
|
|
||||||
m_renderTarget =
|
|
||||||
impl()->makeRenderTarget(m_width,
|
|
||||||
m_height,
|
|
||||||
m_swapchain->imageFormat(),
|
|
||||||
m_swapchain->imageUsageFlags());
|
|
||||||
|
|
||||||
if (m_device->name() == "Mali-G76" || m_device->name() == "Mali-G72")
|
|
||||||
{
|
|
||||||
// These devices (like the Huawei P30 or Galaxy S10) will end up
|
|
||||||
// with a DEVICE_LOST error if we blit to the screen, so don't.
|
|
||||||
m_allowBlitOffscreenToScreen = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_device->name() == "PowerVR Rogue GM9446")
|
|
||||||
{
|
|
||||||
// These devices (like the Oppo Reno 3 Pro) only give correct
|
|
||||||
// results when rendering to an off-screen texture.
|
|
||||||
m_alwaysUseOffscreenTexture = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~TestingWindowAndroidVulkan()
|
~TestingWindowAndroidVulkan()
|
||||||
{
|
{
|
||||||
// Destroy the swapchain first because it synchronizes for in-flight
|
destroyRenderContext();
|
||||||
// command buffers.
|
|
||||||
m_swapchain = nullptr;
|
|
||||||
|
|
||||||
m_renderContext.reset();
|
|
||||||
m_renderTarget.reset();
|
|
||||||
m_overflowTexture.reset();
|
|
||||||
|
|
||||||
if (m_windowSurface != VK_NULL_HANDLE)
|
if (m_windowSurface != VK_NULL_HANDLE)
|
||||||
{
|
{
|
||||||
@@ -192,21 +105,40 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Factory* factory() override { return m_renderContext.get(); }
|
Factory* factory() override
|
||||||
|
{
|
||||||
|
// Some GMs call factory() during construction (before the call to
|
||||||
|
// resize will rebuild the device), so this function also needs to build
|
||||||
|
// the render context
|
||||||
|
if (m_renderContext == nullptr)
|
||||||
|
{
|
||||||
|
makeDeviceAndRenderContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_renderContext.get();
|
||||||
|
}
|
||||||
|
|
||||||
rive::gpu::RenderContext* renderContext() const override
|
rive::gpu::RenderContext* renderContext() const override
|
||||||
{
|
{
|
||||||
|
assert(m_renderContext != nullptr);
|
||||||
return m_renderContext.get();
|
return m_renderContext.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
rive::gpu::RenderTarget* renderTarget() const override
|
rive::gpu::RenderTarget* renderTarget() const override
|
||||||
{
|
{
|
||||||
|
assert(m_renderTarget != nullptr);
|
||||||
return m_renderTarget.get();
|
return m_renderTarget.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void resize(int width, int height) override
|
void resize(int width, int height) override
|
||||||
{
|
{
|
||||||
TestingWindow::resize(width, height);
|
TestingWindow::resize(width, height);
|
||||||
|
|
||||||
|
if (m_renderContext == nullptr)
|
||||||
|
{
|
||||||
|
makeDeviceAndRenderContext();
|
||||||
|
}
|
||||||
|
|
||||||
m_renderTarget =
|
m_renderTarget =
|
||||||
impl()->makeRenderTarget(m_width,
|
impl()->makeRenderTarget(m_width,
|
||||||
m_height,
|
m_height,
|
||||||
@@ -252,6 +184,11 @@ public:
|
|||||||
std::unique_ptr<rive::Renderer> beginFrame(
|
std::unique_ptr<rive::Renderer> beginFrame(
|
||||||
const FrameOptions& options) override
|
const FrameOptions& options) override
|
||||||
{
|
{
|
||||||
|
if (m_renderContext == nullptr)
|
||||||
|
{
|
||||||
|
makeDeviceAndRenderContext();
|
||||||
|
}
|
||||||
|
|
||||||
m_renderContext->beginFrame(RenderContext::FrameDescriptor{
|
m_renderContext->beginFrame(RenderContext::FrameDescriptor{
|
||||||
.renderTargetWidth = m_width,
|
.renderTargetWidth = m_width,
|
||||||
.renderTargetHeight = m_height,
|
.renderTargetHeight = m_height,
|
||||||
@@ -381,7 +318,129 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onceAfterGM() override
|
||||||
|
{
|
||||||
|
// Mali devices with a driver version before 50 have been known to run
|
||||||
|
// out of memory, so for these devices, tear down the device between
|
||||||
|
// GMs.
|
||||||
|
if (m_device != nullptr &&
|
||||||
|
strstr(m_device->name().c_str(), "Mali") != nullptr &&
|
||||||
|
m_device->driverVersion().major < 50)
|
||||||
|
{
|
||||||
|
destroyRenderContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void makeDeviceAndRenderContext()
|
||||||
|
{
|
||||||
|
using namespace rive_vkb;
|
||||||
|
|
||||||
|
m_device = std::make_unique<VulkanDevice>(
|
||||||
|
*m_instance,
|
||||||
|
VulkanDevice::Options{
|
||||||
|
.coreFeaturesOnly = m_backendParams.core,
|
||||||
|
.printInitializationMessage =
|
||||||
|
m_printDeviceInitializationMessage,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only want to print the device initialization message the first time
|
||||||
|
m_printDeviceInitializationMessage = false;
|
||||||
|
|
||||||
|
m_renderContext = RenderContextVulkanImpl::MakeContext(
|
||||||
|
m_instance->vkInstance(),
|
||||||
|
m_device->vkPhysicalDevice(),
|
||||||
|
m_device->vkDevice(),
|
||||||
|
m_device->vulkanFeatures(),
|
||||||
|
m_instance->getVkGetInstanceProcAddrPtr(),
|
||||||
|
{.forceAtomicMode = m_backendParams.atomic});
|
||||||
|
|
||||||
|
auto windowCapabilities =
|
||||||
|
m_device->getSurfaceCapabilities(m_windowSurface);
|
||||||
|
|
||||||
|
auto swapOpts = VulkanSwapchain::Options{
|
||||||
|
.formatPreferences =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
.format = m_backendParams.srgb
|
||||||
|
? VK_FORMAT_R8G8B8A8_SRGB
|
||||||
|
: VK_FORMAT_R8G8B8A8_UNORM,
|
||||||
|
.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
||||||
|
},
|
||||||
|
// Fall back to either ordering of ARGB
|
||||||
|
{
|
||||||
|
.format = VK_FORMAT_R8G8B8A8_UNORM,
|
||||||
|
.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.format = VK_FORMAT_B8G8R8A8_UNORM,
|
||||||
|
.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.presentModePreferences =
|
||||||
|
{
|
||||||
|
VK_PRESENT_MODE_IMMEDIATE_KHR,
|
||||||
|
VK_PRESENT_MODE_FIFO_KHR,
|
||||||
|
},
|
||||||
|
.imageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||||
|
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||||
|
VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
if ((windowCapabilities.supportedUsageFlags &
|
||||||
|
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) != 0)
|
||||||
|
{
|
||||||
|
swapOpts.imageUsageFlags |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_swapchain =
|
||||||
|
std::make_unique<rive_vkb::VulkanSwapchain>(*m_instance,
|
||||||
|
*m_device,
|
||||||
|
ref_rcp(vk()),
|
||||||
|
m_windowSurface,
|
||||||
|
swapOpts);
|
||||||
|
|
||||||
|
m_androidWindowWidth = m_swapchain->width();
|
||||||
|
m_androidWindowHeight = m_swapchain->height();
|
||||||
|
|
||||||
|
if (m_width == 0)
|
||||||
|
{
|
||||||
|
m_width = m_androidWindowWidth;
|
||||||
|
}
|
||||||
|
if (m_height == 0)
|
||||||
|
{
|
||||||
|
m_height = m_androidWindowHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_device->name() == "Mali-G76" || m_device->name() == "Mali-G72")
|
||||||
|
{
|
||||||
|
// These devices (like the Huawei P30 or Galaxy S10) will end up
|
||||||
|
// with a DEVICE_LOST error if we blit to the screen, so don't.
|
||||||
|
m_allowBlitOffscreenToScreen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_device->name() == "PowerVR Rogue GM9446")
|
||||||
|
{
|
||||||
|
// These devices (like the Oppo Reno 3 Pro) only give correct
|
||||||
|
// results when rendering to an off-screen texture.
|
||||||
|
m_alwaysUseOffscreenTexture = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyRenderContext()
|
||||||
|
{
|
||||||
|
if (m_device != nullptr)
|
||||||
|
{
|
||||||
|
m_device->waitUntilIdle();
|
||||||
|
|
||||||
|
m_swapchain = nullptr;
|
||||||
|
m_renderTarget = nullptr;
|
||||||
|
m_overflowTexture = nullptr;
|
||||||
|
m_renderContext = nullptr;
|
||||||
|
m_device = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RenderContextVulkanImpl* impl() const
|
RenderContextVulkanImpl* impl() const
|
||||||
{
|
{
|
||||||
return m_renderContext->static_impl_cast<RenderContextVulkanImpl>();
|
return m_renderContext->static_impl_cast<RenderContextVulkanImpl>();
|
||||||
@@ -389,6 +448,7 @@ private:
|
|||||||
|
|
||||||
VulkanContext* vk() const { return impl()->vulkanContext(); }
|
VulkanContext* vk() const { return impl()->vulkanContext(); }
|
||||||
|
|
||||||
|
ANativeWindow* m_window = nullptr;
|
||||||
BackendParams m_backendParams;
|
BackendParams m_backendParams;
|
||||||
uint32_t m_androidWindowWidth;
|
uint32_t m_androidWindowWidth;
|
||||||
uint32_t m_androidWindowHeight;
|
uint32_t m_androidWindowHeight;
|
||||||
@@ -402,6 +462,7 @@ private:
|
|||||||
// size doesn't fit in the window.
|
// size doesn't fit in the window.
|
||||||
PFN_vkDestroySurfaceKHR m_vkDestroySurfaceKHR = nullptr;
|
PFN_vkDestroySurfaceKHR m_vkDestroySurfaceKHR = nullptr;
|
||||||
|
|
||||||
|
bool m_printDeviceInitializationMessage = true;
|
||||||
bool m_allowBlitOffscreenToScreen = true;
|
bool m_allowBlitOffscreenToScreen = true;
|
||||||
bool m_alwaysUseOffscreenTexture = false;
|
bool m_alwaysUseOffscreenTexture = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -205,23 +205,33 @@ static void dumpGMs(const std::string& match, bool interactive)
|
|||||||
|
|
||||||
for (const auto& [make_gm, name] : gmRegistry)
|
for (const auto& [make_gm, name] : gmRegistry)
|
||||||
{
|
{
|
||||||
std::unique_ptr<GM> gm(make_gm());
|
// Scope the GM so that it destructs (and releases its resources) before
|
||||||
|
// we call `onceAfterGM` which potentially tears down the entire display
|
||||||
|
// devices (see: TestingWindowAndroidVulkan)
|
||||||
|
{
|
||||||
|
std::unique_ptr<GM> gm(make_gm());
|
||||||
|
|
||||||
if (!gm)
|
if (!gm)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (match.size() && !contains(name, match))
|
if (match.size() && !contains(name, match))
|
||||||
{
|
{
|
||||||
continue; // This gm got filtered out by the '--match' argument.
|
continue; // This gm got filtered out by the '--match' argument.
|
||||||
}
|
}
|
||||||
if (!TestHarness::Instance().claimGMTest(name))
|
if (!TestHarness::Instance().claimGMTest(name))
|
||||||
{
|
{
|
||||||
continue; // A different process already drew this gm.
|
continue; // A different process already drew this gm.
|
||||||
}
|
}
|
||||||
gm->onceBeforeDraw();
|
gm->onceBeforeDraw();
|
||||||
|
|
||||||
|
dump_gm(gm.get(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow the testing window to do any cleanup it might want to do
|
||||||
|
// between GMs
|
||||||
|
TestingWindow::Get()->onceAfterGM();
|
||||||
|
|
||||||
dump_gm(gm.get(), name);
|
|
||||||
if (interactive)
|
if (interactive)
|
||||||
{
|
{
|
||||||
// Wait for any key if in interactive mode.
|
// Wait for any key if in interactive mode.
|
||||||
|
|||||||
Reference in New Issue
Block a user