Vulkan SDK for Android 1.1.1 Mali Developer Center
png_swapchain.cpp
1 /* Copyright (c) 2016-2017, ARM Limited and Contributors
2  *
3  * SPDX-License-Identifier: MIT
4  *
5  * Permission is hereby granted, free of charge,
6  * to any person obtaining a copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
14  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
17  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19  */
20 
21 #include "png_swapchain.hpp"
22 #include <assert.h>
23 #include <string.h>
24 
25 #define STB_IMAGE_STATIC
26 #define STB_IMAGE_WRITE_IMPLEMENTATION
27 #include "framework/stb/stb_image_write.h"
28 
29 using namespace MaliSDK;
30 using namespace std;
31 
32 Result PNGSwapchain::init(const char *pBasePath, unsigned swapchainImagesCount)
33 {
34  basePath = pBasePath;
35  this->swapchainImagesCount = swapchainImagesCount;
36 
37  for (unsigned i = 0; i < swapchainImagesCount; i++)
38  vacant.push(i);
39 
40  worker = thread(&PNGSwapchain::threadEntry, this);
41  return RESULT_SUCCESS;
42 }
43 
44 void PNGSwapchain::join()
45 {
46  if (worker.joinable())
47  {
48  lock.lock();
49  dead = true;
50  cond.notify_all();
51  lock.unlock();
52  worker.join();
53  }
54 }
55 
57 {
58  join();
59 }
60 
61 void PNGSwapchain::present(unsigned index, VkDevice device, VkDeviceMemory memory, unsigned width, unsigned height,
62  unsigned numFences, VkFence *fences, bool coherent)
63 {
64  lock_guard<mutex> l{ lock };
65  ready.push({ device, memory, fences, numFences, index, width, height, coherent });
66  cond.notify_all();
67 }
68 
70 {
71  unique_lock<mutex> l{ lock };
72  cond.wait(l, [this] { return !vacant.empty(); });
73  unsigned index = vacant.front();
74  vacant.pop();
75  return index;
76 }
77 
78 void PNGSwapchain::dump(const Command &cmd, unsigned sequenceCount)
79 {
80  char formatted[64];
81  sprintf(formatted, ".%08u.png", sequenceCount);
82  string path = basePath + formatted;
83 
84  LOGI("Writing PNG file to: \"%s\".\n", path.c_str());
85 
86  void *pSrc = nullptr;
87  VK_CHECK(vkMapMemory(cmd.device, cmd.memory, 0, cmd.width * cmd.height * 4, 0, &pSrc));
88 
89  // If our memory is incoherent, make sure that we invalidate the CPU caches
90  // before copying.
91  if (!cmd.coherent)
92  {
93  VkMappedMemoryRange range = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
94  range.memory = cmd.memory;
95  range.size = VK_WHOLE_SIZE;
96  VK_CHECK(vkInvalidateMappedMemoryRanges(cmd.device, 1, &range));
97  }
98 
99  int ret = stbi_write_png(path.c_str(), cmd.width, cmd.height, 4, pSrc, cmd.width * 4);
100  vkUnmapMemory(cmd.device, cmd.memory);
101 
102  if (ret != 0)
103  LOGI("Wrote PNG file: \"%s\".\n", path.c_str());
104  else
105  LOGE("Failed to write PNG file: \"%s\".\n", path.c_str());
106 }
107 
108 void PNGSwapchain::threadEntry()
109 {
110  // Very basic approach. Application will push render requests into a
111  // thread-safe queue.
112  // This thread will wait for the relevant fences to complete, then dump the
113  // PNG to disk.
114  // We then make the buffer that was being scanned out available to the
115  // application.
116  //
117  // We could make the buffer available before we wait for the fences, but we
118  // would then have to provide
119  // a semaphore + fence just like Vulkan WSI, that would be quite complicated
120  // since that generally
121  // would require driver support.
122 
123  unsigned sequenceCount = 0;
124 
125  for (;;)
126  {
127  Command command;
128  {
129  unique_lock<mutex> l{ lock };
130  cond.wait(l, [this] { return !ready.empty() || dead; });
131 
132  if (dead)
133  break;
134 
135  command = ready.front();
136  ready.pop();
137  }
138 
139  vkWaitForFences(command.device, command.numFences, command.fences, true, UINT64_MAX);
140  unsigned nextVacant = displayed;
141 
142  dump(command, sequenceCount++);
143 
144  // Previous buffer is now ready to be rendered into.
145  if (nextVacant != command.index)
146  {
147  lock_guard<mutex> l{ lock };
148  vacant.push(nextVacant);
149  cond.notify_all();
150  }
151 
152  displayed = command.index;
153  }
154 }
Result init(const char *pBasePath, unsigned swapchainImagesCount)
Initialize the swapchain.
~PNGSwapchain()
Destructor.
void present(unsigned index, VkDevice device, VkDeviceMemory memory, unsigned width, unsigned height, unsigned numFences, VkFence *fences, bool coherent)
Dump image for a swapchain index to disk.
unsigned acquire()
Acquire a new swapchain index. When acquire returns the image is ready to be presented into...