vkCmdBlitImage to Frambuffer, then draw UI: UI is blinking - vulkan

I'm making a Vulkan (LWJGL) app that will draw directly to framebuffer images. No vertex or fragment shader, just some compute shader to build an image, which I blit directly into a FrameBuffer image. It's working fine.
Now I try to render an UI (imgui) on top of that, but I think I have some synchronization trouble : the UI is blinking sometimes (the blit is maybe not yet finished ?). The render would be perfect for some seconds, but sooner or later, the UI disappears during maybe one frame. I only build the render pass once, I don't record the command buffers more than once for this test, so I think the problem happen when I record the command buffer.
Maybe the problem is that the vkCmdBlitImage is still running when the UI starts to draw ?
I tried to add some barriers, between the blit and the UI render, but the problem is still here.
I think I really miss something, so I'm requesting your help. I put here the code I think relevant, but don't hesitate to ask more code.
The record of the CommandBuffer:
for (int i = 0; i < commandBuffers.size(); i++)
{
RenderCommandBuffer commandBuffer = commandBuffers.get(i);
ImageView imageView = configuration.imageViewManager.getImageViews().get(i);
commandBuffer.startCommand();
copyPixelBufferToFB(commandBuffer, imageView);
commandBuffer.startRenderPass();
imGui.drawFrame(commandBuffer.getVkCommandBuffer());
commandBuffer.endRenderPass();
commandBuffer.endCommand();
}
The copyPixelBufferToFB method:
Extent2D extent = context.swapChainManager.getExtent();
ImageView dstImageView = context.imageViewManager.getImageViews().get(commandBuffer.id);
// Intend to blit from this image, set the layout accordingly
// Prepare transfer from Image to Frambuffer
ImageBarrier barrier = new ImageBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT);
barrier.addImageBarrier(srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_ACCESS_TRANSFER_READ_BIT);
barrier.addImageBarrier(dstImageView.getImageId(), dstImageView.getImageFormat(), 1, 0,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT);
barrier.execute(commandBuffer.getVkCommandBuffer());
long bltSrcImage = srcImage.getId();
long bltDstImage = dstImageView.getImageId();
VkImageBlit.Buffer region = VkImageBlit.calloc(1);
region.srcSubresource().aspectMask(VK_IMAGE_ASPECT_COLOR_BIT);
region.srcSubresource().mipLevel(0);
region.srcSubresource().baseArrayLayer(0);
region.srcSubresource().layerCount(1);
region.srcOffsets(0).x(0);
region.srcOffsets(0).y(0);
region.srcOffsets(0).z(0);
region.srcOffsets(1).x(srcImage.getWidth());
region.srcOffsets(1).y(srcImage.getHeight());
region.srcOffsets(1).z(1);
region.dstSubresource().aspectMask(VK_IMAGE_ASPECT_COLOR_BIT);
region.dstSubresource().mipLevel(0);
region.dstSubresource().baseArrayLayer(0);
region.dstSubresource().layerCount(1);
region.dstOffsets(0).x(0);
region.dstOffsets(0).y(0);
region.dstOffsets(0).z(0);
region.dstOffsets(1).x(extent.getWidth());
region.dstOffsets(1).y(extent.getHeight());
region.dstOffsets(1).z(1);
vkCmdBlitImage(commandBuffer.getVkCommandBuffer(), bltSrcImage,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, bltDstImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region, VK_FILTER_NEAREST);
// Change layout again before render pass.
ImageBarrier barrierEnd = new ImageBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
barrierEnd.addImageBarrier(srcImage, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT);
barrierEnd.addImageBarrier(dstImageView.getImageId(), dstImageView.getImageFormat(), 1,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_WRITE_BIT);
barrierEnd.execute(commandBuffer.getVkCommandBuffer());
How I submit the CommandBuffer, something like that:
bWaitSemaphores.put(waitSemaphore.getId());
bwaitStage.put(VK_PIPELINE_STAGE_TRANSFER_BIT);
pCommandBuffers.put(commandBuffer.getVkCommandBuffer());
submitInfo = VkSubmitInfo.calloc();
submitInfo.sType(VK_STRUCTURE_TYPE_SUBMIT_INFO);
submitInfo.waitSemaphoreCount(bWaitSemaphores.size());
submitInfo.pWaitSemaphores(bWaitSemaphores);
submitInfo.pWaitDstStageMask(bwaitStage);
submitInfo.pCommandBuffers(pCommandBuffers);
[...]
vkQueueSubmit(queue, submitInfo, VK_NULL_HANDLE);
The creation of the RenderPass:
VkAttachmentDescription colorAttachment = VkAttachmentDescription.calloc();
colorAttachment.format(context.swapChainManager.getColorDomain().getColorFormat());
colorAttachment.samples(VK_SAMPLE_COUNT_1_BIT);
colorAttachment.loadOp(VK_ATTACHMENT_LOAD_OP_LOAD);
colorAttachment.storeOp(VK_ATTACHMENT_STORE_OP_STORE);
colorAttachment.stencilLoadOp(VK_ATTACHMENT_LOAD_OP_DONT_CARE);
colorAttachment.stencilStoreOp(VK_ATTACHMENT_STORE_OP_DONT_CARE);
colorAttachment.initialLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
colorAttachment.finalLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
VkAttachmentReference.Buffer colorAttachmentRef = VkAttachmentReference.calloc(1);
colorAttachmentRef.attachment(0);
colorAttachmentRef.layout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
VkSubpassDescription.Buffer subpass = VkSubpassDescription.calloc(1);
subpass.pipelineBindPoint(VK_PIPELINE_BIND_POINT_GRAPHICS);
subpass.colorAttachmentCount(1);
subpass.pColorAttachments(colorAttachmentRef);
VkSubpassDependency.Buffer dependency = VkSubpassDependency.calloc(1);
dependency.srcSubpass(VK_SUBPASS_EXTERNAL);
dependency.dstSubpass(0);
dependency.srcStageMask(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
dependency.dstStageMask(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
dependency.srcAccessMask(0);
dependency.dstAccessMask(
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
int attachmentCount = 1;
VkAttachmentDescription.Buffer attachments = VkAttachmentDescription
.calloc(attachmentCount);
attachments.put(colorAttachment);
attachments.flip();
VkRenderPassCreateInfo renderPassInfo = VkRenderPassCreateInfo.calloc();
renderPassInfo.sType(VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO);
renderPassInfo.pAttachments(attachments);
renderPassInfo.pSubpasses(subpass);
renderPassInfo.pDependencies(dependency);
long[] aRenderPass = new long[1];
if (vkCreateRenderPass(logicalDevice.getVkDevice(), renderPassInfo, null,
aRenderPass) != VK_SUCCESS)
{
throw new AssertionError("Failed to create render pass!");
}
renderPass = aRenderPass[0];
Edit 12/12/2018:
The imGui.drawFrame() in the code above is now irrelevant, I changed the gui from ImGui to Nuklear, and completely rewrote the gui pipeline. Unfortunately, the same problem appears.
However, I'm still using a vkCmdDrawIndexed to draw it.

Related

Camera position cause objects to disappear

I'm developing an app with blazor client-side and I need to render a 3D scene.
I have an issue and I guess it is material-related.
I have a composition of parallelepipeds where one of them is fully opaque and the others are transparent.
Depending on the camera angle, the transparent ones completely disappear:
Example where everything is visible:
Example with 2 missing:
Example with all missing:
Code for transparent parallelepipeds
var geometry = new THREE.CubeGeometry(item.xDimension * _scene.normalizer, item.yDimension * _scene.normalizer, item.zDimension * _scene.normalizer);
var material = new THREE.MeshBasicMaterial();
var box = new THREE.Mesh(geometry, material);
box.material.color = new THREE.Color("gray");
box.material.opacity = 0.8;
box.material.transparent = true;
Code for the camera:
var camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 100);
camera.position.set(1.3, 1.3, 1.3);
camera.lookAt(0, 0, 0);
I'm using OrbitControls and every object size is between 0 an 1 (_scene.normalizer is for that purpose)
Do you know why this is happening?
Edit:
I found it being a material depth function issue. Do you know which should I use?
Thanks,
Transparency is tricky with WebGL because a transparent object writes to the depthmap, and then the renderer assumes that subsequent objects behind it are occluded so it skips drawing them. You could avoid this by playing with the material's .depthTest and .depthWrite attributes (see the docs):
box.material.color = new THREE.Color("gray");
box.material.opacity = 0.8;
box.material.transparent = true;
box.material.depthWrite = false;

Render target write/ shader read synchronization between different render passes

Here is a Vulkan rendering setup:
Submit CB1 - contains render pass A with X draw calls.
Submit CB2 - contains render pass B with Y draw calls where one of the draw calls samples from image which is render target of render pass A.
During submission of CB2, a semaphore, which is shared with some external GPU API, signaling inserted to make sure CB2 execution is done before the render target of render pass B is used further (By CUDA in this case).This step is set correct and it is clear to me how it works.
All this happens on the same queue and in the above specified order. Render pass A and B share MSAA image,but each has unique color attachment into which MSAA is resolved.
My question is what is the best way to synchronize between CB1 finishing writing to render pass A RT,and one or more draw calls in CB2 sampling from that RT in shader during render pass B execution?
Based on some suggestion I received on Khronos Vulkan Slack group I tried synchronization via subpass dependencies.
Render pass A dependency setup :
VkSubpassDependency dependencies[2] = {};
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
VkAttachmentReference colorReferences[2] = {};
colorReferences[0] = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; //this one for MSAA attachment
colorReferences[1] = { 1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
Render pass B dependency setup :
VkSubpassDependency dependencies[2] = {};
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
VkAttachmentReference colorReferences[2] = {};
colorReferences[0] = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
colorReferences[1] = { 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
The above solution seems to be working. But I am not sure I have implicit synchronization guarantee about it. In this discussion one of the answers states
The only images where that isn’t the case is dependencies for render
targets. And those are treated specially by subpasses anyway. Each
subpass says what it is writing and to where, so the system has the
information to build those memory dependencies explicitly.
Also in this article the author writes:
NOTE: Frame buffer operations inside a render pass happen in
API-order, of course. This is a special exception which the spec calls
out.
But in this SO question, the answer is that a sync between command buffers must be done, with events in that case.
So another option I have tried was to insert pipeline memory barrier during CB2 recording for images which are render targets in previously submitted CB (render pass A),before beginning of render pass B recording:
CB2 recording:
BeginCommandBuffer(commandBuffer);
...
if (vulkanTex->isRenderTarget)//for each sampler in this pass
{
VkImageMemoryBarrier imageMemoryBarrier = CreateImageBarrier();
imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageMemoryBarrier.image = vulkanTex->image;
VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
imageMemoryBarrier.subresourceRange = range;
vkCmdPipelineBarrier(
commandBuffer,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0,
0, nullptr,
0, nullptr,
1, &imageMemoryBarrier);
}
VkRenderPassBeginInfo renderPassBeginInfo = {};
...
.....
Of course in this scenario, I can set subpass dependency same for all render passes to be like Render pass B dependency setup.This one also works. But it requires from me recording more commands into CBs. So,which way is correct(given subpass dependency option is valid) and most optimal in terms of hardware efficiency?
The above solution seems to be working.
Your dependencies make no sense. The renderpass B dependency in particular is decidedly weird, given your description of the actual dependency: "where one of the draw calls samples from image which is render target of render pass A." That would represent a dependency between the framebuffer writes within A and the texture reads in B. Your dependency in your example creates a dependency between framebuffer writes within A and framebuffer writes within B. Which makes no sense and is unrelated to what you says you're actually doing.
Also, your srcAccessMask makes no sense, as you state that the prior source is reading memory, when you are trying to synchronize with something that is writing memory.
Now, it may be that your semaphore dependency happens to be covering it, or that the semaphore is interfering with Vulkan layers' attempts to detect dependency problems (you are using layers, right?). But the code you've shown simply doesn't make sense.
An external dependency on renderpass B is the right way to go (it's not clear why renderpass A needs an external dependency here), but it needs to actually make sense. If renderpass B is indeed sampling from an image written to by renderpass A, then it would look something like this:
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; //Assuming you're reading the image in the fragment shader. Insert other shader stage(s) if otherwise.
dependencies[0].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; //renderpass A wrote to the image as an attachment.
dependencies[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; //renderpass B is reading from the image in a shader.
dependencies[0].dependencyFlags = 0; //By region dependencies make no sense here, since they're not part of the same renderpass.

Vulkan: Problems rendering to an offscreen framebuffer and reading the results

I am writing a game for Android using Vulkan. For the game, I need to write the depth buffer information to an offscreen framebuffer. I eventually want to read this depth information with the CPU, but for now I was drawing it on a quad to debug it. I was only getting the clear depth value of 1.0 in the image, so I tried to simplify the problem. First, I decided to use a color and depth attachment in case there was an issue with just the depth attachment. Then I did the following:
(1) set up a render pass which uses color and depth attachments and clears them to green and {1.0, 0} respectively,
(2) use a framebuffer that does not use the swapchain images or normal depth buffer, but images created specifically for this offscreen framebuffer,
(3) transition the layouts so that they can be written to by the render pass,
(4) draw a blue quad which takes up the entire screen to the framebuffer,
(5) end the command buffer and wait for the graphics queue is idle,
(6) transition the layout of the color attachment so that it can be read by a shader,
(7) start a new render pass using the swapchain images in a different command buffer,
(8) draw a quad using for its texture, the results of the previous render pass (color attachment),
(9) end the command buffer and submit to the graphics queue.
What I see is a green rectangle (the clear color from the first render pass) with some blue squares inside of it (blue is the color of the quad drawn in the first render pass). I expected to see the whole screen taken up by a single blue quad.
I feel like I am missing a barrier or fence or semaphore somewhere. But I can't figure out where. I wait on the graphics queue after each command submission (for debugging), I added a semaphore to have each set of commands signal the next set that it was done. So, one would think that there was something wrong in the render pass, but I use this render pass for my normal draw opperations (that are done in the swapchain images and sent to the present queue) without any problem.
Let me know which part of the code you need to see. It is very long.
I enabled the Vulkan validation layers and am getting no complaints from it. I made sure it is working by forcing it to complain.
I did the same thing in OpenGL and it works fine.
My load and store ops are as follows:
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
For the layout transitions I am doing the following transition for the color attachment image before the draw:
vkBeginCommandBuffer(...);
VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = colorImage;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
vkCmdPipelineBarrier(
cmdBuffer,
sourceStage, destinationStage,
0,
0, nullptr,
0, nullptr,
1, &barrier
);
vkEndCommandBuffer(...);
vkQueueSubmit(...);
vkQueueWaitIdle(graphicsQueue);
In the render pass, I transition the layout to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL so that I can read it from the shader.
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
The subpass and its dependencies:
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
subpass.pDepthStencilAttachment = &depthAttachmentRef;
VkSubpassDependency dependency = {};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstAccessMask =
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
My final goal is to have a render pass with no color attachment, only a depth attachment. The color attachment is there for debug purposes only.
My thought on this problem is that it is a driver bug. The code works fine on a couple of different Linux PCs and on a Pixel 4XL android device. I put a test in to see if I get the correct result for a known model. If the answer comes back incorrect, I fall back to OpenGL.

Create js clone a Shape with dynamically drawn graphics

I have a Image on a stage and I'm drawing over it and erasing it. Using the following method
http://jsfiddle.net/lannymcnie/ZNYPD/
Now i want to take a clone of the user drawings but its not working. I tried
var drawingAreaClone = drawingArea.clone(true);
but its not working .
Is there a way to clone it. kindly Help
The demo you posted doesn't clear the stage, but instead clears the graphics each frame. If you clone the shape, it will have no instructions.
#Catalin's answer is right if you just need a visual -- but another option is to use the Graphics store() method instead of clearing the graphics: http://createjs.com/docs/easeljs/classes/Graphics.html#method_store
Essentially this method just sets an internal pointer to where the graphics draw from. By storing after each draw, only future draw calls are made. This will have the same application as the demo you posted, only you can call unstore() later to reset the Graphics to draw from the beginning. If you clone it this way, it should work.
var erase = document.getElementById("toggle").checked;
wrapper.updateCache(erase?"destination-out":"source-over");
//drawing.graphics.clear();
drawing.graphics.store(); // Don't draw anything before this point
// Later
var newGraphics = drawing.graphics.clone();
newGraphics.unstore(); // Might be redundant
var shape = new Shape(newGraphics);
Note that cloning Graphics doesn't recreate the entire graphics tree, and simply clones the array that stores the instructions. Modifying the individual instructions after the fact would impact any clones of that Graphics object.
Hope that helps.
If the drawn line shape is a child of the drawingAreaClone then the clone should work properly.
However, if for some reason you can't make it work with that, you can take a snapshot of the canvas and save it as an img type varaible like this:
var snapshot = new Image();
snapshot.src = canvas.toDataURL();
Also, if you don't want to snapshot the whole canvas, after you saved the initial image, you can limit the drawing area to a rectangle with these extra instructions:
var ctx = canvas.getContext('2d');
canvas.width = snapshot.width;
canvas.height = snapshot.height;
ctx.drawImage(snapshot, rectangle.x, rectangle.y, rectangle.width, rectangle.height, 0, 0, rectangle.width, rectangle.height);
snapshot.src = canvas.toDataURL();

while I scroll between the layout it takes too long to be able to scroll between the gallerie's pictures. Is there any way to reduce this time?

this is my first question here, though I've being reading this forum for quite a while. Most of the answers to my doubts are from here :)
Getting back on topic. I'm developing an Android application. I'm drawing a dynamic layout that are basically Galleries, inside a LinearLayout, inside a ScrollView, inside a RelativeLayout. The ScrollView is a must, because I'm drawing a dynamic amount of galleries that most probably will not fit on the screen.
When I scroll inside the layout, I have to wait 3/4 seconds until the ScrollView "deactivates" to be able to scroll inside the galleries. What I want to do is to reduce this time to a minimum. Preferably I would like to be able to scroll inside the galleries as soon as I lift my finger from the screen, though anything lower than 2 seconds would be great as well.
I've being googling around for a solution but all I could find until now where layout tutorials that didn't tackle this particular issue. I was hoping someone here knows if this is possible and if so to give me some hints on how to do so.
I would prefer not to do my own ScrollView to solve this. But if that is the only way I would appreciate some help because I'm not really sure how would I solve this issue by doing that.
this is my layout:
public class PicturesL extends Activity implements OnClickListener,
OnItemClickListener, OnItemLongClickListener {
private ArrayList<ImageView> imageView = new ArrayList<ImageView>();
private StringBuilder PicsDate = new StringBuilder();
private CaWaApplication application;
private long ListID;
private ArrayList<Gallery> gallery = new ArrayList<Gallery>();
private ArrayList<Bitmap> Thumbails = new ArrayList<Bitmap>();
private String idioma;
private ArrayList<Long> Days = new ArrayList<Long>();
private long oldDay;
private long oldThumbsLoaded;
private ArrayList<Long> ThumbailsDays = new ArrayList<Long>();
private ArrayList<ArrayList<Long>> IDs = new ArrayList<ArrayList<Long>>();
#Override
public void onCreate(Bundle savedInstancedState) {
super.onCreate(savedInstancedState);
RelativeLayout layout = new RelativeLayout(this);
ScrollView scroll = new ScrollView(this);
LinearLayout realLayout = new LinearLayout(this);
ArrayList<TextView> texts = new ArrayList<TextView>();
Button TakePic = new Button(this);
idioma = com.mateloft.cawa.prefs.getLang(this);
if (idioma.equals("en")) {
TakePic.setText("Take Picture");
} else if (idioma.equals("es")) {
TakePic.setText("Sacar Foto");
}
RelativeLayout.LayoutParams scrollLP = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT,
RelativeLayout.LayoutParams.FILL_PARENT);
layout.addView(scroll, scrollLP);
realLayout.setOrientation(LinearLayout.VERTICAL);
realLayout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));
scroll.addView(realLayout);
TakePic.setId(67);
TakePic.setOnClickListener(this);
application = (CaWaApplication) getApplication();
ListID = getIntent().getExtras().getLong("listid");
getAllThumbailsOfID();
LinearLayout.LayoutParams TakeLP = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
realLayout.addView(TakePic);
oldThumbsLoaded = 0;
int galler = 100;
for (int z = 0; z < Days.size(); z++) {
ThumbailsManager croppedThumbs = new ThumbailsManager(Thumbails,
oldThumbsLoaded,
ThumbailsDays.get(z));
oldThumbsLoaded = ThumbailsDays.get(z);
texts.add(new TextView(this));
texts.get(z).setText("Day " + Days.get(z).toString());
gallery.add(new Gallery(this));
gallery.get(z).setAdapter(new ImageAdapter(this, croppedThumbs.getGallery(), 250, 175, true,
ListID));
gallery.get(z).setOnItemClickListener(this);
gallery.get(z).setOnItemLongClickListener(this);
gallery.get(z).setId(galler);
galler++;
realLayout.addView(texts.get(z));
realLayout.addView(gallery.get(z));
}
Log.d("PicturesL", "ListID: " + ListID);
setContentView(layout);
}
private void getAllThumbailsOfID() {
ArrayList<ModelPics> Pictures = new ArrayList<ModelPics>();
ArrayList<String> ThumbailsPath = new ArrayList<String>();
Pictures = application.dataManager.selectAllPics();
long thumbpathloaded = 0;
int currentID = 0;
for (int x = 0; x < Pictures.size(); x++) {
if (Pictures.get(x).walkname == ListID) {
if (Days.size() == 0) { Days.add(Pictures.get(x).day); oldDay = Pictures.get(x).day;
IDs.add(new ArrayList<Long>()); currentID = 0; }
if (oldDay != Pictures.get(x).day) {
oldDay = Pictures.get(x).day;
ThumbailsDays.add(thumbpathloaded);
Days.add(Pictures.get(x).day);
IDs.add(new ArrayList<Long>());
currentID++;
}
StringBuilder tpath = new StringBuilder();
tpath.append(Pictures.get(x).path.substring(0,
Pictures.get(x).path.length() - 4));
tpath.append("-t.jpg");
IDs.get(currentID).add(Pictures.get(x).id);
ThumbailsPath.add(tpath.toString());
thumbpathloaded++;
if (x == Pictures.size() - 1) {
Log.d("PicturesL", "El ultimo de los arrays, tamaño: " + Days.size());
ThumbailsDays.add(thumbpathloaded);
}
}
}
for (int y = 0; y < ThumbailsPath.size(); y++) {
Thumbails.add(BitmapFactory.decodeFile(ThumbailsPath.get(y)));
}
}
I had a memory leak on another activity when screen orientation changed that was making it slower, now it is working better. The scroller is not locking up. But sometimes, when it stops scrolling, it takes a few seconds (2/3) to disable itself. I just want it to be a little more dynamic, is there any way to override the listener and make it stop scrolling ON_ACTION_UP or something like that?
I don't want to use the listview because I want to have each gallery separated by other views, now I just have text, but I will probably separate them with images with a different size than the galleries.
I'm not really sure if this is possible with a listadapter and a listview, I assumed that a view can only handle only one type of object, so I'm using a scrollview of a layout, if I'm wrong please correct me :)
Also this activity works as a preview or selecting the pictures you want to view in full size and manage their values. So its working only with thumbnails. Each one weights 40 kb. Guessing that is very unlikely that a user gets more than 1000~1500 pictures in this view, i thought that the activity wouldn't use more than 40~50 mb of ram in this case, adding 10 more if I open the fullsized view. So I guessed as well most devices are able to display this view in full size. If it doesn't work on low-end devices my plan was to add an option in the app preferences to let user chop this view according to some database values.
And a last reason is that during most of this activity "life-cycle" (the app has pics that are relevant to the view, when it ends the value that selects which pictures are displayed has to change and no more pictures are added inside this instance of this activity); the view will be unpopulated, so most of the time showing everything wont cost much, just at the end of its cycle
That was more or less what I thought at the time i created this layout. I'm open to any sort of suggestion or opinion, I just created this layout a few days ago and I'm trying to see if it can work right, because it suits my app needs. Though if there is a better way i would love to hear it
PD: I've being toying to see how much it can actually draw. I managed to draw 530 galleries with 600 thumbnails on the same layout, the app is using 42 mb, and I don't have access to more memory to start my full sized view afterwards :( .I tried to do the same with 1000 but it throws OutOfMemory error, bitmap size exceeds vm budget. Is there any way to make more memory availeable on high-end devices? Or should I try to figure out a way not to draw everything at once? Would the listview work for more than one type of object?
Thanks
Mateo
Why not make your galleries items of a listview? That way you won't have to inflate all of them immediately and can the advantage of the listview recyling mechanism. Some of the slowness you are seeing might be coming from very heavy memory usage. Galleries aren't exactly light widgets so you might be seeing pauses from heavy GC activity once you stop scrolling. ListView with an adapter would also allow you to lazily bind the Galleries to the list, such that during a fling you aren't doing the expensive binding of the galleries.