Fix resize desync between Android surface and renderable (#4328)
This commit is contained in:
@@ -43,6 +43,9 @@ protected:
|
||||
void initSwapchain(uint32_t w, uint32_t h);
|
||||
|
||||
void initDepthStencil();
|
||||
void initRenderPass();
|
||||
void setColorFormat(vk::Format format);
|
||||
void setDepthFormat(vk::Format format);
|
||||
|
||||
void swap() override;
|
||||
|
||||
|
||||
@@ -77,8 +77,7 @@ std::vector<const char*> AndroidVulkanRendererBackend::getInstanceExtensions() {
|
||||
return extensions;
|
||||
}
|
||||
|
||||
void AndroidVulkanRendererBackend::resizeFramebuffer(int width, int height) {
|
||||
size = {static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
|
||||
void AndroidVulkanRendererBackend::resizeFramebuffer(int, int) {
|
||||
if (context) {
|
||||
static_cast<vulkan::Context&>(*context).requestSurfaceUpdate();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.maplibre.android.testapp.activity.surfaceview
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import org.maplibre.android.maps.*
|
||||
import org.maplibre.android.testapp.R
|
||||
@@ -14,6 +14,8 @@ import org.maplibre.android.testapp.styles.TestStyles
|
||||
*/
|
||||
class SurfaceViewResizeActivity : AppCompatActivity() {
|
||||
private lateinit var mapView: MapView
|
||||
private var animatedValue: ValueAnimator? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_surfaceview_resize)
|
||||
@@ -37,18 +39,61 @@ class SurfaceViewResizeActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun setupMap(maplibreMap: MapLibreMap) {
|
||||
maplibreMap.setStyle(TestStyles.AWS_OPEN_DATA_STANDARD_LIGHT)
|
||||
maplibreMap.setStyle(TestStyles.getPredefinedStyleWithFallback("Streets"))
|
||||
}
|
||||
|
||||
private fun setupFab() {
|
||||
val fabDebug = findViewById<FloatingActionButton>(R.id.fabResize)
|
||||
fabDebug.setOnClickListener { view: View? ->
|
||||
val parent = findViewById<View>(R.id.coordinator_layout)
|
||||
val params = mapView.layoutParams
|
||||
|
||||
if (animatedValue != null) {
|
||||
animatedValue?.cancel()
|
||||
animatedValue = null
|
||||
|
||||
params.width = parent.width
|
||||
params.height = parent.height
|
||||
}
|
||||
|
||||
if (this::mapView.isInitialized) {
|
||||
val parent = findViewById<View>(R.id.coordinator_layout)
|
||||
val width = if (parent.width == mapView.width) parent.width / 2 else parent.width
|
||||
val height =
|
||||
if (parent.height == mapView.height) parent.height / 2 else parent.height
|
||||
mapView.layoutParams = CoordinatorLayout.LayoutParams(width, height)
|
||||
params.width = if (parent.width == params.width) parent.width / 2 else parent.width
|
||||
params.height =
|
||||
if (parent.height == params.height) parent.height / 2 else parent.height
|
||||
}
|
||||
|
||||
mapView.requestLayout()
|
||||
}
|
||||
|
||||
val fabAnimatedDebug = findViewById<FloatingActionButton>(R.id.fabAnimatedResize)
|
||||
fabAnimatedDebug.setOnClickListener { view: View? ->
|
||||
val parent = findViewById<View>(R.id.coordinator_layout)
|
||||
|
||||
if (animatedValue != null) {
|
||||
animatedValue?.cancel()
|
||||
animatedValue = null
|
||||
|
||||
mapView.layoutParams.width = parent.width
|
||||
mapView.layoutParams.height = parent.height
|
||||
mapView.requestLayout()
|
||||
} else if (this::mapView.isInitialized) {
|
||||
val minValue = parent.height / 2
|
||||
val maxValue = parent.height
|
||||
|
||||
animatedValue = ValueAnimator.ofInt(maxValue, minValue)
|
||||
animatedValue?.apply {
|
||||
duration = 1000
|
||||
repeatCount = ValueAnimator.INFINITE
|
||||
repeatMode = ValueAnimator.REVERSE
|
||||
|
||||
addUpdateListener { animator ->
|
||||
val height = animator.animatedValue as Int
|
||||
mapView.layoutParams.height = height
|
||||
mapView.requestLayout()
|
||||
}
|
||||
|
||||
start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.maplibre.android.testapp.activity.textureview
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@@ -14,6 +15,8 @@ import org.maplibre.android.testapp.styles.TestStyles
|
||||
*/
|
||||
class TextureViewResizeActivity : AppCompatActivity() {
|
||||
private lateinit var mapView: MapView
|
||||
private var animatedValue: ValueAnimator? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_textureview_resize)
|
||||
@@ -43,12 +46,55 @@ class TextureViewResizeActivity : AppCompatActivity() {
|
||||
private fun setupFab() {
|
||||
val fabDebug = findViewById<FloatingActionButton>(R.id.fabResize)
|
||||
fabDebug.setOnClickListener { view: View? ->
|
||||
val parent = findViewById<View>(R.id.coordinator_layout)
|
||||
val params = mapView.layoutParams
|
||||
|
||||
if (animatedValue != null) {
|
||||
animatedValue?.cancel()
|
||||
animatedValue = null
|
||||
|
||||
params.width = parent.width
|
||||
params.height = parent.height
|
||||
}
|
||||
|
||||
if (this::mapView.isInitialized) {
|
||||
val parent = findViewById<View>(R.id.coordinator_layout)
|
||||
val width = if (parent.width == mapView.width) parent.width / 2 else parent.width
|
||||
val height =
|
||||
if (parent.height == mapView.height) parent.height / 2 else parent.height
|
||||
mapView.layoutParams = CoordinatorLayout.LayoutParams(width, height)
|
||||
params.width = if (parent.width == params.width) parent.width / 2 else parent.width
|
||||
params.height =
|
||||
if (parent.height == params.height) parent.height / 2 else parent.height
|
||||
}
|
||||
|
||||
mapView.requestLayout()
|
||||
}
|
||||
|
||||
val fabAnimatedDebug = findViewById<FloatingActionButton>(R.id.fabAnimatedResize)
|
||||
fabAnimatedDebug.setOnClickListener { view: View? ->
|
||||
val parent = findViewById<View>(R.id.coordinator_layout)
|
||||
|
||||
if (animatedValue != null) {
|
||||
animatedValue?.cancel()
|
||||
animatedValue = null
|
||||
|
||||
mapView.layoutParams.width = parent.width
|
||||
mapView.layoutParams.height = parent.height
|
||||
mapView.requestLayout()
|
||||
} else if (this::mapView.isInitialized) {
|
||||
val minValue = parent.height / 2
|
||||
val maxValue = parent.height
|
||||
|
||||
animatedValue = ValueAnimator.ofInt(maxValue, minValue)
|
||||
animatedValue?.apply {
|
||||
duration = 1000
|
||||
repeatCount = ValueAnimator.INFINITE
|
||||
repeatMode = ValueAnimator.REVERSE
|
||||
|
||||
addUpdateListener { animator ->
|
||||
val height = animator.animatedValue as Int
|
||||
mapView.layoutParams.height = height
|
||||
mapView.requestLayout()
|
||||
}
|
||||
|
||||
start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,17 @@
|
||||
android:layout_height="match_parent"
|
||||
app:maplibre_renderTextureMode="false"/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fabAnimatedResize"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|left"
|
||||
android:layout_marginBottom="@dimen/fab_margin"
|
||||
android:layout_marginLeft="@dimen/fab_margin"
|
||||
android:src="@drawable/ic_animate_coordinates"
|
||||
app:backgroundTint="@color/blueAccent"
|
||||
app:layout_anchorGravity="bottom"/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fabResize"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -14,6 +14,17 @@
|
||||
android:layout_height="match_parent"
|
||||
app:maplibre_renderTextureMode="true"/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fabAnimatedResize"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|left"
|
||||
android:layout_marginBottom="@dimen/fab_margin"
|
||||
android:layout_marginLeft="@dimen/fab_margin"
|
||||
android:src="@drawable/ic_animate_coordinates"
|
||||
app:backgroundTint="@color/blueAccent"
|
||||
app:layout_anchorGravity="bottom"/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fabResize"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -18,7 +18,7 @@ import java.net.URL
|
||||
*/
|
||||
|
||||
// Define the validation layer version, with a default value.
|
||||
var vvlVersion = "1.4.341.0"
|
||||
var vvlVersion = "1.4.350.0"
|
||||
if (extra.has("vvl_version")) {
|
||||
vvlVersion = extra["vvl_version"] as String
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ void SurfaceRenderableResource::initColor(uint32_t w, uint32_t h) {
|
||||
colorAllocations.reserve(imageCount);
|
||||
swapchainImages.reserve(imageCount);
|
||||
|
||||
colorFormat = vk::Format::eR8G8B8A8Unorm;
|
||||
setColorFormat(vk::Format::eR8G8B8A8Unorm);
|
||||
extent = vk::Extent2D(w, h);
|
||||
|
||||
const auto imageUsage = vk::ImageUsageFlags() | vk::ImageUsageFlagBits::eColorAttachment |
|
||||
@@ -156,7 +156,7 @@ void SurfaceRenderableResource::initSwapchain(uint32_t w, uint32_t h) {
|
||||
swapchain = device->createSwapchainKHRUnique(swapchainCreateInfo, nullptr, dispatcher);
|
||||
swapchainImages = device->getSwapchainImagesKHR(swapchain.get(), dispatcher);
|
||||
|
||||
colorFormat = swapchainCreateInfo.imageFormat;
|
||||
setColorFormat(swapchainCreateInfo.imageFormat);
|
||||
extent = swapchainCreateInfo.imageExtent;
|
||||
|
||||
acquireSemaphores.reserve(swapchainImages.size());
|
||||
@@ -193,7 +193,7 @@ void SurfaceRenderableResource::initDepthStencil() {
|
||||
return;
|
||||
}
|
||||
|
||||
depthFormat = *formatIt;
|
||||
setDepthFormat(*formatIt);
|
||||
|
||||
const auto imageUsage = vk::ImageUsageFlags() | vk::ImageUsageFlagBits::eDepthStencilAttachment |
|
||||
vk::ImageUsageFlagBits::eTransientAttachment;
|
||||
@@ -242,6 +242,94 @@ void SurfaceRenderableResource::initDepthStencil() {
|
||||
backend.setDebugName(depthAllocation->imageView.get(), "SwapchainDepthImageView");
|
||||
}
|
||||
|
||||
void SurfaceRenderableResource::initRenderPass() {
|
||||
// The current render pass should be invalidated if:
|
||||
// - color/depth format changes (use setColorFormat/setDepthFormat)
|
||||
// - renderable resource type changes
|
||||
// - this is done currently only by inheritance and it can't change during runtime
|
||||
|
||||
if (renderPass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& device = backend.getDevice();
|
||||
const auto& dispatcher = backend.getDispatcher();
|
||||
const auto colorLayout = surface ? vk::ImageLayout::ePresentSrcKHR : vk::ImageLayout::eTransferSrcOptimal;
|
||||
|
||||
const std::array<vk::AttachmentDescription, 2> attachments = {
|
||||
vk::AttachmentDescription()
|
||||
.setFormat(colorFormat)
|
||||
.setSamples(vk::SampleCountFlagBits::e1)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||
.setStencilLoadOp(vk::AttachmentLoadOp::eDontCare)
|
||||
.setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
|
||||
.setInitialLayout(vk::ImageLayout::eUndefined)
|
||||
.setFinalLayout(colorLayout),
|
||||
|
||||
vk::AttachmentDescription()
|
||||
.setFormat(depthFormat)
|
||||
.setSamples(vk::SampleCountFlagBits::e1)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStoreOp(vk::AttachmentStoreOp::eDontCare)
|
||||
.setStencilLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
|
||||
.setInitialLayout(vk::ImageLayout::eUndefined)
|
||||
.setFinalLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal)};
|
||||
|
||||
const vk::AttachmentReference colorAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal);
|
||||
const vk::AttachmentReference depthAttachmentRef(1, vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||||
|
||||
const auto subpass = vk::SubpassDescription()
|
||||
.setPipelineBindPoint(vk::PipelineBindPoint::eGraphics)
|
||||
.setColorAttachmentCount(1)
|
||||
.setColorAttachments(colorAttachmentRef)
|
||||
.setPDepthStencilAttachment(&depthAttachmentRef);
|
||||
|
||||
const std::array<vk::SubpassDependency, 2> dependencies = {
|
||||
vk::SubpassDependency()
|
||||
.setSrcSubpass(VK_SUBPASS_EXTERNAL)
|
||||
.setDstSubpass(0)
|
||||
.setSrcStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput)
|
||||
.setDstStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput)
|
||||
.setSrcAccessMask({})
|
||||
.setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite),
|
||||
|
||||
vk::SubpassDependency()
|
||||
.setSrcSubpass(VK_SUBPASS_EXTERNAL)
|
||||
.setDstSubpass(0)
|
||||
.setSrcStageMask(vk::PipelineStageFlagBits::eEarlyFragmentTests |
|
||||
vk::PipelineStageFlagBits::eLateFragmentTests)
|
||||
.setDstStageMask(vk::PipelineStageFlagBits::eEarlyFragmentTests |
|
||||
vk::PipelineStageFlagBits::eLateFragmentTests)
|
||||
.setSrcAccessMask({})
|
||||
.setDstAccessMask(vk::AccessFlagBits::eDepthStencilAttachmentWrite),
|
||||
};
|
||||
|
||||
const auto renderPassCreateInfo =
|
||||
vk::RenderPassCreateInfo().setAttachments(attachments).setSubpasses(subpass).setDependencies(dependencies);
|
||||
|
||||
renderPass = device->createRenderPassUnique(renderPassCreateInfo, nullptr, dispatcher);
|
||||
}
|
||||
|
||||
void SurfaceRenderableResource::setColorFormat(vk::Format format) {
|
||||
if (colorFormat == format) {
|
||||
return;
|
||||
}
|
||||
|
||||
renderPass.reset();
|
||||
colorFormat = format;
|
||||
}
|
||||
|
||||
void SurfaceRenderableResource::setDepthFormat(vk::Format format) {
|
||||
if (depthFormat == format) {
|
||||
return;
|
||||
}
|
||||
|
||||
renderPass.reset();
|
||||
depthFormat = format;
|
||||
}
|
||||
|
||||
void SurfaceRenderableResource::swap() {
|
||||
auto& context = static_cast<Context&>(backend.getContext());
|
||||
context.submitFrame();
|
||||
@@ -327,62 +415,7 @@ void SurfaceRenderableResource::init(uint32_t w, uint32_t h) {
|
||||
initDepthStencil();
|
||||
|
||||
// create render pass
|
||||
const auto colorLayout = surface ? vk::ImageLayout::ePresentSrcKHR : vk::ImageLayout::eTransferSrcOptimal;
|
||||
|
||||
const std::array<vk::AttachmentDescription, 2> attachments = {
|
||||
vk::AttachmentDescription()
|
||||
.setFormat(colorFormat)
|
||||
.setSamples(vk::SampleCountFlagBits::e1)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||
.setStencilLoadOp(vk::AttachmentLoadOp::eDontCare)
|
||||
.setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
|
||||
.setInitialLayout(vk::ImageLayout::eUndefined)
|
||||
.setFinalLayout(colorLayout),
|
||||
|
||||
vk::AttachmentDescription()
|
||||
.setFormat(depthFormat)
|
||||
.setSamples(vk::SampleCountFlagBits::e1)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStoreOp(vk::AttachmentStoreOp::eDontCare)
|
||||
.setStencilLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
|
||||
.setInitialLayout(vk::ImageLayout::eUndefined)
|
||||
.setFinalLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal)};
|
||||
|
||||
const vk::AttachmentReference colorAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal);
|
||||
const vk::AttachmentReference depthAttachmentRef(1, vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||||
|
||||
const auto subpass = vk::SubpassDescription()
|
||||
.setPipelineBindPoint(vk::PipelineBindPoint::eGraphics)
|
||||
.setColorAttachmentCount(1)
|
||||
.setColorAttachments(colorAttachmentRef)
|
||||
.setPDepthStencilAttachment(&depthAttachmentRef);
|
||||
|
||||
const std::array<vk::SubpassDependency, 2> dependencies = {
|
||||
vk::SubpassDependency()
|
||||
.setSrcSubpass(VK_SUBPASS_EXTERNAL)
|
||||
.setDstSubpass(0)
|
||||
.setSrcStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput)
|
||||
.setDstStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput)
|
||||
.setSrcAccessMask({})
|
||||
.setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite),
|
||||
|
||||
vk::SubpassDependency()
|
||||
.setSrcSubpass(VK_SUBPASS_EXTERNAL)
|
||||
.setDstSubpass(0)
|
||||
.setSrcStageMask(vk::PipelineStageFlagBits::eEarlyFragmentTests |
|
||||
vk::PipelineStageFlagBits::eLateFragmentTests)
|
||||
.setDstStageMask(vk::PipelineStageFlagBits::eEarlyFragmentTests |
|
||||
vk::PipelineStageFlagBits::eLateFragmentTests)
|
||||
.setSrcAccessMask({})
|
||||
.setDstAccessMask(vk::AccessFlagBits::eDepthStencilAttachmentWrite),
|
||||
};
|
||||
|
||||
const auto renderPassCreateInfo =
|
||||
vk::RenderPassCreateInfo().setAttachments(attachments).setSubpasses(subpass).setDependencies(dependencies);
|
||||
|
||||
renderPass = device->createRenderPassUnique(renderPassCreateInfo, nullptr, dispatcher);
|
||||
initRenderPass();
|
||||
|
||||
// create swapchain framebuffers
|
||||
swapchainFramebuffers.reserve(swapchainImageViews.size());
|
||||
@@ -408,7 +441,6 @@ void SurfaceRenderableResource::recreateSwapchain() {
|
||||
backend.getDevice()->waitIdle(backend.getDispatcher());
|
||||
|
||||
swapchainFramebuffers.clear();
|
||||
renderPass.reset();
|
||||
swapchainImageViews.clear();
|
||||
swapchainImages.clear();
|
||||
acquireSemaphores.clear();
|
||||
|
||||
Reference in New Issue
Block a user