fix(android): Workaround for Adreno VK_ERROR_DEVICE_LOST (#4442)
This commit is contained in:
@@ -52,11 +52,10 @@ static_assert(sizeof(GlobalPaintParamsUBO) == 3 * 16);
|
||||
|
||||
#if MLN_RENDER_BACKEND_VULKAN
|
||||
struct alignas(16) GlobalPlatformParamsUBO {
|
||||
/* 0 */ alignas(16) std::array<float, 2> rotation0;
|
||||
/* 16 */ alignas(16) std::array<float, 2> rotation1;
|
||||
/* 32 */
|
||||
/* 0 */ std::array<float, 4> surfaceRotation;
|
||||
/* 16 */
|
||||
};
|
||||
static_assert(sizeof(GlobalPlatformParamsUBO) == 2 * 16);
|
||||
static_assert(sizeof(GlobalPlatformParamsUBO) == 16);
|
||||
#endif
|
||||
|
||||
enum {
|
||||
|
||||
@@ -109,13 +109,17 @@ layout(set = GLOBAL_SET_INDEX, binding = 0) uniform GlobalPaintParamsUBO {
|
||||
|
||||
#ifdef USE_SURFACE_TRANSFORM
|
||||
layout(set = GLOBAL_SET_INDEX, binding = 1) uniform GlobalPlatformParamsUBO {
|
||||
mat2 rotation;
|
||||
vec4 surfaceRotation;
|
||||
} platformParams;
|
||||
#endif
|
||||
|
||||
void applySurfaceTransform() {
|
||||
#ifdef USE_SURFACE_TRANSFORM
|
||||
gl_Position.xy = platformParams.rotation * gl_Position.xy;
|
||||
const mat2 rotation = {
|
||||
platformParams.surfaceRotation.xy,
|
||||
platformParams.surfaceRotation.zw
|
||||
};
|
||||
gl_Position.xy = rotation * gl_Position.xy;
|
||||
#endif
|
||||
|
||||
gl_Position.y *= -1.0;
|
||||
|
||||
@@ -80,7 +80,11 @@ layout(location = 0) out vec4 out_color;
|
||||
layout(set = DRAWABLE_IMAGE_SET_INDEX, binding = 0) uniform sampler2D image_sampler;
|
||||
|
||||
void main() {
|
||||
out_color = texture(image_sampler, frag_uv);
|
||||
// using lod bias variant for sampling as a workaround for Adreno 600 series
|
||||
out_color = texture(image_sampler, frag_uv, 0.0);
|
||||
// working alternatives
|
||||
//out_color = textureGrad(image_sampler, frag_uv, dFdx(frag_uv), dFdy(frag_uv));
|
||||
//out_color = texelFetch(image_sampler, ivec2(frag_uv * vec2(textureSize(image_sampler, 0))), 0);
|
||||
}
|
||||
)";
|
||||
};
|
||||
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
void beginFrame() override;
|
||||
void endFrame() override;
|
||||
void submitFrame();
|
||||
void waitFrame() const;
|
||||
bool waitFrame() const;
|
||||
|
||||
std::unique_ptr<gfx::CommandEncoder> createCommandEncoder() override;
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ protected:
|
||||
void setDepthFormat(vk::Format format);
|
||||
|
||||
void copySurfaceToReadTexture();
|
||||
void swap() override;
|
||||
|
||||
public:
|
||||
virtual void createPlatformSurface() = 0;
|
||||
@@ -78,6 +77,7 @@ public:
|
||||
|
||||
void init(uint32_t w, uint32_t h);
|
||||
void recreateSwapchain();
|
||||
void swap() override;
|
||||
|
||||
void queueSurfaceRead();
|
||||
std::shared_ptr<PremultipliedImage> readImage();
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#if !defined(NDEBUG) && !defined(__ANDROID__)
|
||||
#define ENABLE_VULKAN_VALIDATION
|
||||
// #define ENABLE_VULKAN_GPU_ASSISTED_VALIDATION
|
||||
// #define ENABLE_VULKAN_VALIDATION_FEATURES
|
||||
// #define ENABLE_VMA_DEBUG
|
||||
#endif
|
||||
|
||||
|
||||
@@ -175,6 +175,7 @@ class NavigationMap : SupportMapFragment(), ProgressChangeListener, MilestoneEve
|
||||
LocationComponentActivationOptions
|
||||
.builder(requireContext(), map.style!!)
|
||||
.useDefaultLocationEngine(false)
|
||||
.useSpecializedLocationLayer(true)
|
||||
.build()
|
||||
)
|
||||
|
||||
|
||||
@@ -154,9 +154,7 @@ GLFWVulkanBackend::GLFWVulkanBackend(GLFWwindow* window_, const bool capFrameRat
|
||||
init();
|
||||
}
|
||||
|
||||
GLFWVulkanBackend::~GLFWVulkanBackend() {
|
||||
context.reset();
|
||||
}
|
||||
GLFWVulkanBackend::~GLFWVulkanBackend() {}
|
||||
|
||||
mbgl::Size GLFWVulkanBackend::getSize() const {
|
||||
return size;
|
||||
|
||||
@@ -867,9 +867,9 @@ protected:
|
||||
|
||||
mapbox::cheap_ruler::CheapRuler ruler;
|
||||
|
||||
mbgl::mat4 translation;
|
||||
mbgl::mat4 projectionCircle;
|
||||
mbgl::mat4 projectionPuck;
|
||||
mbgl::mat4 translation{};
|
||||
mbgl::mat4 projectionCircle{};
|
||||
mbgl::mat4 projectionPuck{};
|
||||
|
||||
bool positionChanged = false;
|
||||
bool radiusChanged = false;
|
||||
@@ -885,7 +885,7 @@ public:
|
||||
std::optional<std::reference_wrapper<gfx::Drawable>> drawable;
|
||||
std::array<vec2, 4> geometry;
|
||||
TextureInfo textureInfo;
|
||||
bool dirty{false};
|
||||
bool dirty{true};
|
||||
|
||||
gfx::Drawable& getDrawable() { return drawable.value().get(); }
|
||||
void reset() { textureInfo.reset(); }
|
||||
@@ -896,7 +896,7 @@ public:
|
||||
std::optional<std::reference_wrapper<gfx::Drawable>> outlineDrawable;
|
||||
std::array<vec2, 73> geometry;
|
||||
TextureInfo textureInfo;
|
||||
bool dirty{false};
|
||||
bool dirty{true};
|
||||
|
||||
gfx::Drawable& getDrawable() { return drawable.value().get(); }
|
||||
void reset() { textureInfo.reset(); }
|
||||
|
||||
@@ -36,9 +36,9 @@ namespace vulkan {
|
||||
// this can be queried at runtime (VkPhysicalDeviceLimits.maxVertexInputBindings)
|
||||
constexpr uint32_t maximumVertexBindingCount = 16;
|
||||
|
||||
constexpr uint32_t globalDescriptorPoolSize = 3 * 4;
|
||||
constexpr uint32_t layerDescriptorPoolSize = 3 * 256;
|
||||
constexpr uint32_t drawableUniformDescriptorPoolSize = 3 * 1024;
|
||||
constexpr uint32_t globalDescriptorPoolSize = 2 * 1;
|
||||
constexpr uint32_t layerDescriptorPoolSize = 2 * 256;
|
||||
constexpr uint32_t drawableUniformDescriptorPoolSize = 2 * 1024;
|
||||
constexpr uint32_t drawableImageDescriptorPoolSize = drawableUniformDescriptorPoolSize / 2;
|
||||
|
||||
namespace {
|
||||
@@ -236,7 +236,7 @@ void Context::requestSurfaceUpdate(bool useDelay) {
|
||||
surfaceUpdateLatency = useDelay ? backend.getMaxFrames() * 3 : 0;
|
||||
}
|
||||
|
||||
void Context::waitFrame() const {
|
||||
bool Context::waitFrame() const {
|
||||
MLN_TRACE_FUNC();
|
||||
MBGL_VERIFY_THREAD(tid);
|
||||
|
||||
@@ -249,7 +249,10 @@ void Context::waitFrame() const {
|
||||
1, &frame.flightFrameFence.get(), VK_TRUE, timeout, dispatcher);
|
||||
if (waitFenceResult != vk::Result::eSuccess) {
|
||||
mbgl::Log::Error(mbgl::Event::Render, "Wait fence failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Context::beginFrame() {
|
||||
@@ -304,7 +307,9 @@ void Context::beginFrame() {
|
||||
auto& frame = frameResources[frameResourceIndex];
|
||||
constexpr uint64_t timeout = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
waitFrame();
|
||||
if (!waitFrame()) {
|
||||
return;
|
||||
}
|
||||
|
||||
frame.runDeletionQueue(*this);
|
||||
|
||||
@@ -323,6 +328,10 @@ void Context::beginFrame() {
|
||||
} else if (acquireImageResult.result == vk::Result::eSuboptimalKHR) {
|
||||
renderableResource.setAcquiredImageIndex(acquireImageResult.value);
|
||||
requestSurfaceUpdate();
|
||||
} else {
|
||||
mbgl::Log::Error(
|
||||
mbgl::Event::Render,
|
||||
"acquireNextImageKHR result: " + std::to_string(static_cast<int>(acquireImageResult.result)));
|
||||
}
|
||||
|
||||
} catch (const vk::OutOfDateKHRError& e) {
|
||||
@@ -521,11 +530,11 @@ void Context::bindGlobalUniformBuffers(gfx::RenderPass& renderPass) const noexce
|
||||
|
||||
auto& renderableResource = renderPassImpl.getDescriptor().renderable.getResource<SurfaceRenderableResource>();
|
||||
if (renderableResource.hasSurfaceTransformSupport()) {
|
||||
float surfaceRotation = renderableResource.getRotation();
|
||||
const float surfaceRotation = renderableResource.getRotation();
|
||||
const float sinRot = std::sin(surfaceRotation);
|
||||
const float cosRot = std::cos(surfaceRotation);
|
||||
|
||||
const shaders::GlobalPlatformParamsUBO platformUBO = {
|
||||
/* .rotation0 = */ {cosf(surfaceRotation), -sinf(surfaceRotation)},
|
||||
/* .rotation1 = */ {sinf(surfaceRotation), cosf(surfaceRotation)}};
|
||||
const shaders::GlobalPlatformParamsUBO platformUBO = {.surfaceRotation = {cosRot, -sinRot, sinRot, cosRot}};
|
||||
context.globalUniformBuffers.createOrUpdate(
|
||||
shaders::idGlobalPlatformParamsUBO, &platformUBO, sizeof(platformUBO), context);
|
||||
}
|
||||
|
||||
@@ -173,15 +173,20 @@ void SurfaceRenderableResource::initSwapchain(uint32_t w, uint32_t h) {
|
||||
setColorFormat(swapchainCreateInfo.imageFormat);
|
||||
extent = swapchainCreateInfo.imageExtent;
|
||||
|
||||
acquireSemaphores.reserve(swapchainImages.size());
|
||||
acquireSemaphores.reserve(backend.getMaxFrames());
|
||||
for (uint32_t index = 0; index < backend.getMaxFrames(); ++index) {
|
||||
acquireSemaphores.emplace_back(device->createSemaphoreUnique({}, nullptr, dispatcher));
|
||||
|
||||
const auto indexStr = std::to_string(index);
|
||||
backend.setDebugName(acquireSemaphores.back().get(), "AcquireSemaphore_" + indexStr);
|
||||
}
|
||||
|
||||
presentSemaphores.reserve(swapchainImages.size());
|
||||
for (uint32_t index = 0; index < swapchainImages.size(); ++index) {
|
||||
acquireSemaphores.emplace_back(device->createSemaphoreUnique({}, nullptr, dispatcher));
|
||||
presentSemaphores.emplace_back(device->createSemaphoreUnique({}, nullptr, dispatcher));
|
||||
|
||||
const auto indexStr = std::to_string(index);
|
||||
backend.setDebugName(acquireSemaphores.back().get(), "PresentSemaphore_" + indexStr);
|
||||
backend.setDebugName(presentSemaphores.back().get(), "AcquireSemaphore_" + indexStr);
|
||||
backend.setDebugName(presentSemaphores.back().get(), "PresentSemaphore_" + indexStr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -437,13 +437,14 @@ void RendererBackend::initInstance() {
|
||||
const auto& debugExtensions = getDebugExtensions();
|
||||
extensions.insert(extensions.end(), debugExtensions.begin(), debugExtensions.end());
|
||||
|
||||
#ifdef ENABLE_VULKAN_GPU_ASSISTED_VALIDATION
|
||||
#ifdef ENABLE_VULKAN_VALIDATION_FEATURES
|
||||
appInfo.setApiVersion(VK_API_VERSION_1_2);
|
||||
|
||||
const std::vector<vk::ValidationFeatureEnableEXT> validationFeatures = {
|
||||
vk::ValidationFeatureEnableEXT::eGpuAssisted,
|
||||
vk::ValidationFeatureEnableEXT::eGpuAssistedReserveBindingSlot,
|
||||
vk::ValidationFeatureEnableEXT::eSynchronizationValidation,
|
||||
vk::ValidationFeatureEnableEXT::eBestPractices,
|
||||
};
|
||||
const vk::ValidationFeaturesEXT validationFeatureInfo(validationFeatures);
|
||||
|
||||
@@ -603,7 +604,7 @@ void RendererBackend::initDevice() {
|
||||
.setPEnabledExtensionNames(extensions)
|
||||
.setPEnabledFeatures(&physicalDeviceFeatures);
|
||||
|
||||
#ifdef ENABLE_VULKAN_GPU_ASSISTED_VALIDATION
|
||||
#ifdef ENABLE_VULKAN_VALIDATION_FEATURES
|
||||
vk::PhysicalDeviceVulkan12Features extraFeatures;
|
||||
extraFeatures.setBufferDeviceAddress(true);
|
||||
extraFeatures.setTimelineSemaphore(true);
|
||||
|
||||
@@ -406,8 +406,8 @@ void Texture2D::createSampler() {
|
||||
auto samplerCreateInfo = vk::SamplerCreateInfo()
|
||||
.setMinFilter(filter)
|
||||
.setMagFilter(filter)
|
||||
.setMinLod(-VK_LOD_CLAMP_NONE)
|
||||
.setMaxLod(VK_LOD_CLAMP_NONE)
|
||||
.setMinLod(0.0f)
|
||||
.setMaxLod(static_cast<float>(getMipLevels() - 1))
|
||||
.setAddressModeU(addressModeU)
|
||||
.setAddressModeV(addressModeV)
|
||||
.setAddressModeW(vk::SamplerAddressMode::eRepeat);
|
||||
@@ -417,7 +417,11 @@ void Texture2D::createSampler() {
|
||||
}
|
||||
|
||||
if (samplerState.maxAnisotropy != 1 && backend.getDeviceFeatures().samplerAnisotropy) {
|
||||
samplerCreateInfo.setAnisotropyEnable(true).setMaxAnisotropy(samplerState.maxAnisotropy);
|
||||
const auto deviceMaxAnisotropy = static_cast<uint8_t>(
|
||||
backend.getDeviceProperties().limits.maxSamplerAnisotropy);
|
||||
const auto maxAnisotropy = std::min(deviceMaxAnisotropy, samplerState.maxAnisotropy);
|
||||
|
||||
samplerCreateInfo.setAnisotropyEnable(true).setMaxAnisotropy(maxAnisotropy);
|
||||
}
|
||||
|
||||
sampler = backend.getDevice()->createSampler(samplerCreateInfo, nullptr, backend.getDispatcher());
|
||||
@@ -506,15 +510,18 @@ void Texture2D::transitionToTransferReadLayout(const vk::UniqueCommandBuffer& bu
|
||||
}
|
||||
|
||||
void Texture2D::transitionToShaderReadLayout(const vk::UniqueCommandBuffer& buffer) {
|
||||
const auto srcAccessMask = imageLayout == vk::ImageLayout::eTransferSrcOptimal ? vk::AccessFlagBits::eTransferRead
|
||||
: vk::AccessFlagBits::eTransferWrite;
|
||||
|
||||
const auto barrier = vk::ImageMemoryBarrier()
|
||||
.setImage(imageAllocation->image)
|
||||
.setOldLayout(imageLayout)
|
||||
.setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal)
|
||||
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
|
||||
.setSrcAccessMask(srcAccessMask)
|
||||
.setDstAccessMask(vk::AccessFlagBits::eShaderRead)
|
||||
.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
|
||||
.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
|
||||
.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1});
|
||||
.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, getMipLevels(), 0, 1});
|
||||
|
||||
buffer->pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
||||
vk::PipelineStageFlagBits::eFragmentShader,
|
||||
@@ -756,6 +763,9 @@ void Texture2D::generateMips(const vk::UniqueCommandBuffer& buffer) {
|
||||
|
||||
const auto& dispatcher = context.getBackend().getDispatcher();
|
||||
|
||||
// transfer top level mip to read
|
||||
transitionToTransferReadLayout(buffer);
|
||||
|
||||
int32_t mipWidth = size.width;
|
||||
int32_t mipHeight = size.height;
|
||||
|
||||
@@ -766,9 +776,26 @@ void Texture2D::generateMips(const vk::UniqueCommandBuffer& buffer) {
|
||||
.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1});
|
||||
|
||||
for (uint32_t i = 1; i < mipLevels; ++i) {
|
||||
barrier.subresourceRange.baseMipLevel = i - 1;
|
||||
barrier.subresourceRange.baseMipLevel = i;
|
||||
|
||||
// flip transfer direction
|
||||
const int32_t nextMipWidth = std::max(1, mipWidth / 2);
|
||||
const int32_t nextMipHeight = std::max(1, mipHeight / 2);
|
||||
|
||||
const auto blit = vk::ImageBlit()
|
||||
.setSrcOffsets({vk::Offset3D{0, 0, 0}, {mipWidth, mipHeight, 1}})
|
||||
.setDstOffsets({vk::Offset3D{0, 0, 0}, {nextMipWidth, nextMipHeight, 1}})
|
||||
.setSrcSubresource({vk::ImageAspectFlagBits::eColor, i - 1, 0, 1})
|
||||
.setDstSubresource({vk::ImageAspectFlagBits::eColor, i, 0, 1});
|
||||
|
||||
buffer->blitImage(imageAllocation->image,
|
||||
vk::ImageLayout::eTransferSrcOptimal,
|
||||
imageAllocation->image,
|
||||
vk::ImageLayout::eTransferDstOptimal,
|
||||
blit,
|
||||
vk::Filter::eLinear,
|
||||
dispatcher);
|
||||
|
||||
// prepare next loop read
|
||||
barrier.setOldLayout(vk::ImageLayout::eTransferDstOptimal)
|
||||
.setNewLayout(vk::ImageLayout::eTransferSrcOptimal)
|
||||
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
|
||||
@@ -782,54 +809,13 @@ void Texture2D::generateMips(const vk::UniqueCommandBuffer& buffer) {
|
||||
barrier,
|
||||
dispatcher);
|
||||
|
||||
const auto blit = vk::ImageBlit()
|
||||
.setSrcOffsets({vk::Offset3D{0, 0, 0}, {mipWidth, mipHeight, 1}})
|
||||
.setDstOffsets({vk::Offset3D{0, 0, 0}, {mipWidth / 2, mipHeight / 2, 1}})
|
||||
.setSrcSubresource({vk::ImageAspectFlagBits::eColor, i - 1, 0, 1})
|
||||
.setDstSubresource({vk::ImageAspectFlagBits::eColor, i, 0, 1});
|
||||
|
||||
buffer->blitImage(imageAllocation->image,
|
||||
barrier.newLayout,
|
||||
imageAllocation->image,
|
||||
barrier.oldLayout,
|
||||
blit,
|
||||
vk::Filter::eLinear,
|
||||
dispatcher);
|
||||
|
||||
barrier.setOldLayout(vk::ImageLayout::eTransferSrcOptimal)
|
||||
.setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal)
|
||||
.setSrcAccessMask(vk::AccessFlagBits::eTransferRead)
|
||||
.setDstAccessMask(vk::AccessFlagBits::eShaderRead);
|
||||
|
||||
buffer->pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
||||
vk::PipelineStageFlagBits::eFragmentShader,
|
||||
{},
|
||||
nullptr,
|
||||
nullptr,
|
||||
barrier,
|
||||
dispatcher);
|
||||
|
||||
mipWidth = std::max(1, mipWidth / 2);
|
||||
mipHeight = std::max(1, mipHeight / 2);
|
||||
mipWidth = nextMipWidth;
|
||||
mipHeight = nextMipHeight;
|
||||
}
|
||||
|
||||
// transition the last mip
|
||||
barrier.subresourceRange.baseMipLevel = mipLevels - 1;
|
||||
|
||||
barrier.setOldLayout(vk::ImageLayout::eTransferDstOptimal)
|
||||
.setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal)
|
||||
.setSrcAccessMask(vk::AccessFlagBits::eTransferRead)
|
||||
.setDstAccessMask(vk::AccessFlagBits::eShaderRead);
|
||||
|
||||
buffer->pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
||||
vk::PipelineStageFlagBits::eFragmentShader,
|
||||
{},
|
||||
nullptr,
|
||||
nullptr,
|
||||
barrier,
|
||||
dispatcher);
|
||||
|
||||
imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
|
||||
// transition image to fragment read
|
||||
imageLayout = vk::ImageLayout::eTransferSrcOptimal;
|
||||
transitionToShaderReadLayout(buffer);
|
||||
}
|
||||
|
||||
} // namespace vulkan
|
||||
|
||||
Reference in New Issue
Block a user