Skip to content

Commit

Permalink
Virt Swapchain: improvements, esp. for standalone (#211)
Browse files Browse the repository at this point in the history
Some improvements to the Virtual Swapchain layer, especially for standalone use (i.e. outside replay).

* Support Android properties instead of environment variables on Android.
* Add `VIRTUAL_SWAPCHAIN_SURFACE_EXTENT` parameter to set the default surface extent (width and height); without this, apps are likely to request a swapchain with `UINT_MAX` width and height, which will fail.
* `vkQueuePresentKHR`: set `pPresentInfo->pResults` if needed; without this, apps may think `vkQueuePresentKHR` always fails.
* Add some logging on failures; uses `__android_log_print` on Android, and `std::cerr` otherwise.
  • Loading branch information
paulthomson authored Apr 21, 2020
1 parent 3a4bd74 commit f3b6ffd
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 44 deletions.
1 change: 1 addition & 0 deletions core/vulkan/vk_virtual_swapchain/cc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ cc_library(
"//conditions:default": [
"-ldl",
"-lm",
"-llog",
],
}),
visibility = ["//visibility:public"],
Expand Down
56 changes: 54 additions & 2 deletions core/vulkan/vk_virtual_swapchain/cc/layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,75 @@
* limitations under the License.
*/

#include <cstdio>
#include "layer.h"

#include <cstring>
#include <mutex>
#include <type_traits>
#include <unordered_map>
#include <vector>

#include <vulkan/vk_layer.h>
#include <vulkan/vulkan.h>

#include "swapchain.h"

#if defined(__ANDROID__)
#include <android/log.h>
#include <sys/system_properties.h>
#else
#include <iostream>
#endif

#define LAYER_NAME "VirtualSwapchain"

#define LAYER_NAME_FUNCTION(fn) VirtualSwapchain##fn

namespace swapchain {

namespace {

#if defined(__ANDROID__)
bool GetAndroidProperty(const char* const property, std::string* value) {
char buff[PROP_VALUE_MAX];
if (__system_property_get(property, buff) <= 0) {
return false;
}
*value = buff;
return true;
}
#endif

} // namespace

#if defined(__ANDROID__)
void write_warning(const char* message) {
__android_log_print(ANDROID_LOG_WARN, "VirtualSwapchainLayer", "%s", message);
}
#else
void write_warning(const char* message) {
std::cerr << "VirtualSwapchainLayer: " << message << std::endl;
}
#endif

void write_warning(const std::string& message) {
write_warning(message.c_str());
}

bool GetParameter(const char* const env_var_name,
const char* const android_prop_name,
std::string* param_value) {
#if defined(__ANDROID__)
return GetAndroidProperty(android_prop_name, param_value);
#else
const char* const env_var_value = std::getenv(env_var_name);
if (!env_var_value) {
return false;
}
*param_value = env_var_value;
return true;
#endif
}

Context& GetGlobalContext() {
// We rely on C++11 static initialization rules here.
// kContext will get allocated on first use, and freed in the
Expand Down
18 changes: 18 additions & 0 deletions core/vulkan/vk_virtual_swapchain/cc/layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,27 @@

#include <unordered_map>
#include <vector>

#include "vulkan/vulkan.h"

#include "threading.h"

#define EXPECT_SUCCESS(fn) \
[&]() { \
auto r = fn; \
if (VK_SUCCESS != r) { \
swapchain::write_warning(__FILE__ ":" + std::to_string(__LINE__) + \
": " #fn " RETURNED: " + std::to_string(r)); \
} \
return r; \
}()

namespace swapchain {

void write_warning(const char* message);

void write_warning(const std::string& message);

// Sets the key of the dispatch tables used in lower layers of the parent
// dispatchable handle to the new child dispatchable handle. This is necessary
// as lower layers may use that key to find the dispatch table, and a child
Expand Down Expand Up @@ -278,6 +293,9 @@ struct Context {

Context& GetGlobalContext();

bool GetParameter(const char* env_var_name, const char* android_prop_name,
std::string* param_value);

} // namespace swapchain

#endif // VK_VIRTUAL_SWAPCHAIN_LAYER_H
87 changes: 80 additions & 7 deletions core/vulkan/vk_virtual_swapchain/cc/swapchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,52 @@
*/

#include "swapchain.h"

#include <cassert>
#include <sstream>
#include <vector>

#include "virtual_swapchain.h"

namespace swapchain {

// Used to set the value of VkSurfaceCapabilitiesKHR->currentExtent
// returned from vkGetPhysicalDeviceSurfaceCapabilitiesKHR.
// E.g. VIRTUAL_SWAPCHAIN_SURFACE_EXTENT="1960 1080"
// If unset then the current extent will be the "special value"
// {0xFFFFFFFF, 0xFFFFFFFF}, which some apps don't handle well.
// I.e. they will try to create a swapchain with this maximum extent size and we
// will then fail to create a buffer of this size.
const char* kOverrideSurfaceExtentEnv = "VIRTUAL_SWAPCHAIN_SURFACE_EXTENT";
// Android property names must be under 32 characters in Android N and below.
const char* kOverrideSurfaceExtentAndroidProp = "debug.vsc.surface_extent";

namespace {

void OverrideCurrentExtentIfNecessary(VkExtent2D* current_extent) {
std::string overridden_extent;
if (GetParameter(kOverrideSurfaceExtentEnv, kOverrideSurfaceExtentAndroidProp,
&overridden_extent)) {
std::istringstream ss(overridden_extent);
VkExtent2D extent;
ss >> extent.width;
if (ss.fail()) {
write_warning("Failed to parse surface extent parameter: " +
overridden_extent);
return;
}
ss >> extent.height;
if (ss.fail()) {
write_warning("Failed to parse surface extent parameter: " +
overridden_extent);
return;
}
*current_extent = extent;
}
}

} // namespace

void RegisterInstance(VkInstance instance, const InstanceData& data) {
uint32_t num_devices = 0;
data.vkEnumeratePhysicalDevices(instance, &num_devices, nullptr);
Expand All @@ -45,13 +85,16 @@ void RegisterInstance(VkInstance instance, const InstanceData& data) {
// vkCreateXXXSurface calls.
struct VirtualSurface {
bool always_return_given_surface_formats_and_present_modes;
VkExtent2D current_extent;
};

VKAPI_ATTR VkResult VKAPI_CALL vkCreateVirtualSurface(
VkInstance instance, const CreateNext* pCreateInfo,
const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
auto* surf = new VirtualSurface();
surf->always_return_given_surface_formats_and_present_modes = false;
surf->current_extent = {0xFFFFFFFF, 0xFFFFFFFF};

if (pCreateInfo != nullptr) {
for (const CreateNext* pNext =
static_cast<const CreateNext*>(pCreateInfo->pNext);
Expand All @@ -62,6 +105,9 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateVirtualSurface(
}
}
}

OverrideCurrentExtentIfNecessary(&surf->current_extent);

*pSurface = reinterpret_cast<VkSurfaceKHR>(surf);
return VK_SUCCESS;
}
Expand Down Expand Up @@ -108,9 +154,11 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
.GetPhysicalDeviceData(physicalDevice)
->physical_device_properties_;

VirtualSurface* suf = reinterpret_cast<VirtualSurface*>(surface);

pSurfaceCapabilities->minImageCount = 1;
pSurfaceCapabilities->maxImageCount = 0;
pSurfaceCapabilities->currentExtent = {0xFFFFFFFF, 0xFFFFFFFF};
pSurfaceCapabilities->currentExtent = suf->current_extent;
pSurfaceCapabilities->minImageExtent = {1, 1};
pSurfaceCapabilities->maxImageExtent = {
properties.limits.maxImageDimension2D,
Expand Down Expand Up @@ -335,11 +383,16 @@ vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo) {
// We submit to the queue the commands set up by the virtual swapchain.
// This will start a copy operation from the image to the swapchain
// buffers.
uint32_t res = VK_SUCCESS;

VkResult res = VK_SUCCESS;

std::vector<VkPipelineStageFlags> pipeline_stages(
pPresentInfo->waitSemaphoreCount, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
for (size_t i = 0; i < pPresentInfo->swapchainCount; ++i) {

size_t i = 0;
for (; i < pPresentInfo->swapchainCount; ++i) {
uint32_t image_index = pPresentInfo->pImageIndices[i];

VirtualSwapchain* swp =
reinterpret_cast<VirtualSwapchain*>(pPresentInfo->pSwapchains[i]);

Expand All @@ -355,13 +408,33 @@ vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo) {
nullptr // pSemaphores
};

res |= GetGlobalContext().GetQueueData(queue)->vkQueueSubmit(
queue, 1, &submitInfo, swp->GetFence(image_index));
res |= swp->PresentToSurface(queue, image_index);
res = EXPECT_SUCCESS(GetGlobalContext().GetQueueData(queue)->vkQueueSubmit(
queue, 1, &submitInfo, swp->GetFence(image_index)));

if (res != VK_SUCCESS) {
break;
}

res = swp->PresentToSurface(queue, image_index);
if (res != VK_SUCCESS) {
break;
}

swp->NotifySubmitted(image_index);

if (pPresentInfo->pResults) {
pPresentInfo->pResults[i] = VK_SUCCESS;
}
}

return VkResult(res);
// If we left the above loop early, then set the remaining results as errors.
if (pPresentInfo->pResults) {
for (; i < pPresentInfo->swapchainCount; ++i) {
pPresentInfo->pResults[i] = res;
}
}

return res;
}

VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit(VkQueue queue,
Expand Down
Loading

0 comments on commit f3b6ffd

Please sign in to comment.