Jackalope / jen (public) (License: GPLv3 or later version) (since 2018-10-24) (hash sha1)
----> ABOUT:

3D rendering and computing framework based on Vulkan API.

Libraries:
- simdcpp submodule (see my simdcpp repo)
- jmath submodule (see my jmath repo)
- mesh (constexpr generation of cubes, spheres, icosahedrons subdivisions)
- atlas (1D lines and 2D rectangles cutting)
- jlib submodule (see my jlib repo)
- jrf submodule (see my jrf repo)
- vkw (Vulkan API C++ wrapper)
Modules:
- compute (run compute shaders on gpu)
- graphics (draw models with clustered forward rendering and onscreen text)
- resource manager (load meshes, models, textures, scene data from
files and create related objects in graphics module)

----> INSTALLING:

To download all the parts of this framework it's enough to launch
git clone with recursive flag:

$ git clone —recursive ssh://rocketgit@ssh.rocketgit.com/user/Jackalope/jen

After this look at git tags:

$ git tag

It is recommended to use a tagged version instead of the latest commit,
because the first commit after the tagged one mostly includes incompatible
parts of future changes for the next version.

$ git checkout v0.1.0

----> DEPENDENCIES:

To use JEN as CMake subdirectory and successfully build programs with it
you need to make sure you have all of its dependencies:
- compiler: Clang or GCC, support for C++17. Clang 10+ or GCC 9+ is recommended,
compiling on Windows OS is tricky and requires something like MinGW with MSYS,
there are also some complications to go through to make dependencies work;
- GLFW3 library, supported version is 3.2.1;
- FreeType library, if graphics module will be used;
- Vulkan API headers, and optional validation layers to debug sneaky problems,
you also need Vulkan support in your graphics driver to run compiled programs;
- LibZip can be necessary, if JRF is used to read zip files;
- CMake, for obvious reasons;
- glslangValidator to compile shader for the graphics module.

CMake must be able to find GLFW3, Vulkan and FreeType (for graphics)
with find_package().

----> HOW TO USE IT:

To use JEN, you need to add it as a subdirectory:

add_subdirecroty(${PATH_TO_JEN})

There are several configuration options:
- JEN_MODULE_COMPUTE - turn compute module on for compiling and including;
- JEN_MODULE_GRAPHICS - turn graphics module on ...;
- JEN_MULTITHREADED_DRAW_FRAME - draw_frame function will use thread pool queue
instead of linear executing;
- JEN_MODULE_RESOURCE_MANAGER - resource manager module ON, if graphics is ON;
- JEN_VLK_VALIDATION - enable Vulkan Validation Layers to debug some errors
related to JEN. This will often produce false-positive,
as well as true-positive errors.

Look in CMakeLists.txt at JenExamples repo for details on how to use and
configure JEN automatically:

$ git clone ssh://rocketgit@ssh.rocketgit.com/user/Jackalope/JenExamples

Also I recommend to compile and run examples to make sure it works correctly.

----> SUPPORTED HARDWARE:

JEN has not been tested well, because it requires running it on large amount of
different hardware to do so. It must work with mesa driver and modern
Intel i965 GPUs as well as AMD GPUs.


----> DOCUMENTATION:

You can generate Doxygen documentation, to do so
turn on any of JEN_DOXYGEN_* options and run documentation target in cmake:

$ cmake -G %1 -DJEN_DOXYGEN_HTML=ON -DJEN_DOXYGEN_LATEX=ON
$ cmake —build —target documentation

Resource manager is not documented because it still requires large enhancements.
List of commits:
Subject Hash Author Date (UTC)
compute storage image support eaffc4ea4768fb67f063102793ca5d781bafb0c8 Jackalope 2020-05-04 17:48:33
get size of vkFormat pixel function 41e309467955bc2e1361a1f9689367f961040bf7 Jackalope 2020-05-04 17:48:08
vkw image to buffer copy 57234d0007df737094aef5b6bab4177ca39bb347 Jackalope 2020-05-04 17:47:08
added flag to wait events instead of polling for interactive apps b9cfe8faa84e5bfc00aa842b16819b28f5e2be10 Jackalope 2020-05-04 17:45:34
added compute info validation 7cd15af2afd064302703eccd1094f4353da35c4f Jackalope 2020-04-26 08:05:18
fixed uninitialized value 8313a622987f51e74b068231cddde3ff34a93625 Jackalope 2020-04-26 08:04:48
disable checking present support when graphics module not loaded b2ff208cef191444cd995c40989c6b9414c1fb9f Jackalope 2020-04-26 02:35:19
added debug extension loading when graphics module is disabled 54bc7c5eff1353559ec29e560a4095cfa9ef33d8 Jackalope 2020-04-26 02:32:45
Noise library: removed excess commentary lines 6b518ad33508161bd0cc8790eb13f4d03d827acc TheArtOfGriefing 2020-04-23 20:56:55
Noise library: added forgotten hash function use in gradient computation. bfce031f8de0410107aaf33009b46e455acd5095 TheArtOfGriefing 2020-04-23 18:40:05
Noise library update: +6 hash functions +1D,2D,3D,4D highly optimized simplex noise +1D,2D,3D,4D white noise 731cba496cf3c253ad365355faae9df45e1e714e TheArtOfGriefing 2020-04-23 17:37:19
simd library moved to simdcpp submodule, also updates to match new version 36fa65052847fbb258e0ceaf2a2c3fab40e5c3a7 Jackalope 2020-04-21 23:19:24
jlib update d7d711f8b289b2a84e58c66d20c0871c21d17350 Jackalope 2020-04-15 09:54:57
hiding clang-10 new warnings 6d3a1a1dbc928d9ed75024848f06f541d55e1580 Jackalope 2020-04-15 09:54:39
vkw new Vulkan API result values 0eed6a08e659a2e58ed35d9870022d235f0d87d4 Jackalope 2020-04-15 09:54:04
removed temporary fence, validation layers still complaining ba2b7277f0db6e549e68619f8f61ecc50066343d Jackalope 2020-04-15 09:41:02
device queues had incorrect orders in memory layout 28bf6b0e793ae988c390571baa5e8654200f6b42 Jackalope 2020-04-15 07:10:19
updated new vulkan enum names a7f4e2fdd01884df5469abda3520ca3901a44532 Jackalope 2020-04-15 07:09:55
fix gradient values in simplex noise 75ee283d9f06b69936c9db3d1eabe5c876b418cf Jackalope 2020-04-03 04:00:15
conditional device queues loading based on ModulesMask 952bb3a4d1ae3c867309b136543bbc7a072684bb Jackalope 2020-04-02 02:44:36
Commit eaffc4ea4768fb67f063102793ca5d781bafb0c8 - compute storage image support
Author: Jackalope
Author date (UTC): 2020-05-04 17:48
Committer name: Jackalope
Committer date (UTC): 2020-05-04 17:48
Parent(s): 41e309467955bc2e1361a1f9689367f961040bf7
Signer:
Signing key:
Signing status: N
Tree: 350292525a848f436a6dc14163fa66839505ebdc
File Lines added Lines deleted
src/compute/binding_set.h 17 6
src/compute/bindings.h 101 0
src/compute/compute.cpp 222 53
src/compute/compute.h 28 7
src/compute/pipeline.h 9 5
File src/compute/binding_set.h changed (mode: 100644) (index 9d03ca9..793cd78)
... ... namespace jen::vk
8 8 struct Bindings { struct Bindings {
9 9 jl::rarray<const BindingBufferView> uniform_texel_buffer; jl::rarray<const BindingBufferView> uniform_texel_buffer;
10 10 jl::rarray<const BindingBufferView> storage_texel_buffer; jl::rarray<const BindingBufferView> storage_texel_buffer;
11 jl::rarray<const BindingBuffer> uniform_buffer;
12 jl::rarray<const BindingBuffer> storage_buffer;
11 jl::rarray<const BindingBuffer> uniform_buffer;
12 jl::rarray<const BindingBuffer> storage_buffer;
13 jl::rarray<const BindingImage> storage_image;
13 14 }; };
14 15
15 16 struct BindingsSet struct BindingsSet
 
... ... namespace jen::vk
32 33 put_part(vkw::DescrType::STORAGE_TEXEL_BUFFER, bi.storage_texel_buffer); put_part(vkw::DescrType::STORAGE_TEXEL_BUFFER, bi.storage_texel_buffer);
33 34 put_part(vkw::DescrType::UNIFORM_BUFFER, bi.uniform_buffer); put_part(vkw::DescrType::UNIFORM_BUFFER, bi.uniform_buffer);
34 35 put_part(vkw::DescrType::STORAGE_BUFFER, bi.storage_buffer); put_part(vkw::DescrType::STORAGE_BUFFER, bi.storage_buffer);
36 put_part(vkw::DescrType::STORAGE_IMAGE, bi.storage_image);
35 37
36 38 Result res; Result res;
37 39 res = pool.init(*p_dev, {}, {pool_parts.begin(), numPoolPart}, numSets); res = pool.init(*p_dev, {}, {pool_parts.begin(), numPoolPart}, numSets);
 
... ... namespace jen::vk
45 47 } }
46 48
47 49 auto &set_ = set; auto &set_ = set;
48 auto set_buffers = [&set_, p_dev] (vkw::DescrType dt, auto sets) {
49 for (auto &b : sets)
50 set_.set(*p_dev, b.binding, dt, b.part.range());
51 };
52 50 auto set_views = [&set_, p_dev] (vkw::DescrType dt, auto sets) { auto set_views = [&set_, p_dev] (vkw::DescrType dt, auto sets) {
53 51 for (auto &b : sets) for (auto &b : sets)
54 52 set_.set(*p_dev, b.binding, dt, b.view); set_.set(*p_dev, b.binding, dt, b.view);
55 53 }; };
56 54 set_views(vkw::DescrType::UNIFORM_TEXEL_BUFFER, bi.uniform_texel_buffer); set_views(vkw::DescrType::UNIFORM_TEXEL_BUFFER, bi.uniform_texel_buffer);
57 55 set_views(vkw::DescrType::STORAGE_TEXEL_BUFFER, bi.storage_texel_buffer); set_views(vkw::DescrType::STORAGE_TEXEL_BUFFER, bi.storage_texel_buffer);
56
57 auto set_buffers = [&set_, p_dev] (vkw::DescrType dt, auto sets) {
58 for (auto &b : sets)
59 set_.set(*p_dev, b.binding, dt, b.part.range());
60 };
58 61 set_buffers(vkw::DescrType::UNIFORM_BUFFER, bi.uniform_buffer); set_buffers(vkw::DescrType::UNIFORM_BUFFER, bi.uniform_buffer);
59 62 set_buffers(vkw::DescrType::STORAGE_BUFFER, bi.storage_buffer); set_buffers(vkw::DescrType::STORAGE_BUFFER, bi.storage_buffer);
60 63
64 for (auto &b : bi.storage_image) {
65 vkw::DescrImage des;
66 des.sampler = {};
67 des.imageView = b.p_image->image.view;
68 des.imageLayout = vkw::ImLayout::GENERAL;
69 set_.set(*p_dev, b.binding, vkw::DescrType::STORAGE_IMAGE, des);
70 }
71
61 72 return res; return res;
62 73 } }
63 74 void destroy(Device *p_dev) { void destroy(Device *p_dev) {
File src/compute/bindings.h changed (mode: 100644) (index fa4d675..3ea5792)
1 1 #pragma once #pragma once
2 2
3 3 #include "../device/device.h" #include "../device/device.h"
4 #include "../graphics/draw_stages/gpu_image.h"
4 5
5 6 namespace jen::vk { namespace jen::vk {
6 7 namespace BindingUseFlag { enum { namespace BindingUseFlag { enum {
 
... ... namespace jen::vk {
89 90 } }
90 91 vkw::BufferView view; vkw::BufferView view;
91 92 }; };
93
94
95 namespace ImageUseFlag { enum {
96 TRANSFER_SRC = vkw::ImUsage::TRANSFER_SRC,
97 TRANSFER_DST = vkw::ImUsage::TRANSFER_DST,
98 STORAGE = vkw::ImUsage::STORAGE,
99 }; }
100 using ImageUseMask = uint32_t;
101
102 struct ImageCreateInfo {
103 math::v3u32 extent;
104 uint32_t layer_count;
105 uint32_t mip_level_count;
106 VkFormat format;
107 vkw::ImType type;
108 vkw::Samples samples;
109 ImageUseMask usage;
110 };
111 using ImageCreateInfos = jl::rarray<const ImageCreateInfo>;
112
113 struct Image {
114 [[nodiscard]] Result
115 init(Device *p_d, const ImageCreateInfo &info) {
116 ImageInfo ii; {
117 ii.extent = {info.extent.x, info.extent.y, info.extent.z};
118 ii.layer_count = info.layer_count;
119 ii.mip_level_count = info.mip_level_count;
120 ii.format = info.format;
121 ii.type = info.type;
122 ii.samples = info.samples;
123 ii.usage = info.usage;
124 ii.flags = {};
125 ii.tiling = vkw::Tiling::OPTIMAL;
126 }
127 ViewInfo vi; {
128 vi.type = vkw::ImViewType(info.type);
129 vi.aspect = vkw::ImAspect::COLOR;
130 }
131 Result res = image.init(p_d, &ii, &vi);
132 if (res != VK_SUCCESS)
133 return res;
134
135 VkDeviceSize size = vkw::format_size(ii.format) * ii.extent.volume();
136 size *= ii.layer_count;
137 DevMemUsage mem_use = DevMemUsage::STAGING_STATIC_DST;
138
139 res = p_d->buffer_allocator
140 .allocate(size, 0, mem_use, vkw::BufferUsage::TRANSFER_SRC
141 | vkw::BufferUsage::TRANSFER_DST, true, &staging);
142 if (res != VK_SUCCESS)
143 image.destroy(p_d);
144
145 format = info.format;
146 layout = vkw::ImLayout::UNDEFINED;
147 mip_level_count = info.mip_level_count;
148 layer_count = info.layer_count;
149 return res;
150 }
151 void destroy(Device *p_d) {
152 p_d->buffer_allocator.deallocate(staging);
153 image.destroy(p_d);
154 }
155
156
157 void
158 transitionLayout(vkw::CmdBuffer *p_cmd,
159 vkw::ImLayout layout, vkw::StageMaskChange stages) {
160 vkw::BarrierImMem barrier; {
161 barrier.access_change.src = vkw::AccessMask();
162 barrier.access_change.dst = vkw::AccessMask();
163 barrier.layout_change.src = this->layout;
164 barrier.layout_change.dst = layout;
165 barrier.queueFamily_change.set_both(VK_QUEUE_FAMILY_IGNORED);
166 barrier.image = image.image;
167 barrier.range.mip_levels_offset = 0;
168 barrier.range.mip_levels_count = mip_level_count;
169 barrier.range.layers_offset = 0;
170 barrier.range.layers_count = layer_count;
171 barrier.range.aspect = vkw::ImAspect::COLOR;
172 }
173 p_cmd->cmd_barriers(stages, {}, {}, barrier);
174 }
175
176 GpuImage<GpuImageMode::VIEW> image;
177 VkFormat format;
178 vkw::ImLayout layout;
179 DeviceBufferPart staging;
180 uint32_t mip_level_count;
181 uint32_t layer_count;
182 };
183
184 struct BindingImage {
185 void init(Image *p_image, vkw::BindNo bindingNo) {
186 this->binding = bindingNo;
187 this->p_image = p_image;
188 }
189
190 Image *p_image;
191 vkw::BindNo binding;
192 };
92 193 } }
File src/compute/compute.cpp changed (mode: 100644) (index ec0e748..11623f5)
... ... create_bindings(BindingCreateInfos infos, BindingBuffer *p_dst) {
33 33 res = p_dst[i].init(p_device, infos[i]); res = p_dst[i].init(p_device, infos[i]);
34 34 if (res != VK_SUCCESS) { if (res != VK_SUCCESS) {
35 35 while (i > 0) while (i > 0)
36 p_dst->destroy(p_device);
36 p_dst[i].destroy(p_device);
37 37 return res; return res;
38 38 } }
39 39 } }
 
... ... create_bindings(BindingCreateInfos infos, VkFormat *p_formats,
44 44 BindingBufferView *p_dst) { BindingBufferView *p_dst) {
45 45 Result res; Result res;
46 46 for (uint32_t i = 0; i < infos.count32(); ++i) { for (uint32_t i = 0; i < infos.count32(); ++i) {
47 res = p_dst->init(p_device, infos[i], p_formats[i]);
47 res = p_dst[i].init(p_device, infos[i], p_formats[i]);
48 48 if (res != VK_SUCCESS) { if (res != VK_SUCCESS) {
49 49 while (i > 0) while (i > 0)
50 p_dst->destroy(p_device);
50 p_dst[i].destroy(p_device);
51 return res;
52 }
53 }
54 return VK_SUCCESS;
55 }
56
57 [[nodiscard]] jen::vk::Result jen::vk::ModuleCompute::
58 create_images(ImageCreateInfos infos, Image *p_dst) {
59 Result res;
60 for (uint32_t i = 0; i < infos.count32(); ++i) {
61 res = p_dst[i].init(p_device, infos[i]);
62 if (res != VK_SUCCESS) {
63 while (i > 0)
64 p_dst[i].destroy(p_device);
51 65 return res; return res;
52 66 } }
53 67 } }
 
... ... create_bindingSet(const ComputePipeline &pipeline, const Bindings &bindings,
60 74 } }
61 75
62 76 void check_transfer(const jen::vk::DeviceBufferPart &part, void check_transfer(const jen::vk::DeviceBufferPart &part,
63 const jen::vk::BufferTransfer &info) {
64 jassert(info.offset + info.size <= part.size(),
65 "region exceeds buffer");
77 vkw::DeviceSize offset, vkw::DeviceSize size) {
78 jassert(offset + size <= part.size(), "region exceeds buffer");
66 79 jassert(part.is_mapped(), "cannot access memory"); jassert(part.is_mapped(), "cannot access memory");
67 80 jassert(not part.is_flush_needed(), "flush not supported"); jassert(not part.is_flush_needed(), "flush not supported");
68 81 } }
69 82
70 83 void void
71 write_to_allocation(jen::vk::DeviceBufferPart *p_part,
72 const jen::vk::BufferTransfer &info) {
73 check_transfer(*p_part, info);
74 memcpy(p_part->p_data() + info.offset, info.p_data, info.size);
84 write_to_allocation(void *p_src, jen::vk::DeviceBufferPart *p_dst,
85 vkw::DeviceSize dst_offset, vkw::DeviceSize size) {
86 check_transfer(*p_dst, dst_offset, size);
87 memcpy(p_dst->p_data() + dst_offset, p_src, size);
75 88 } }
76 89
77 90 void void
78 read_from_allocation(const jen::vk::DeviceBufferPart &part,
79 const jen::vk::BufferTransfer &info) {
80 check_transfer(part, info);
81 memcpy(info.p_data, part.p_data() + info.offset, info.size);
91 read_from_allocation(jen::vk::DeviceBufferPart *p_src, void *p_dst,
92 vkw::DeviceSize src_offset, vkw::DeviceSize size) {
93 check_transfer(*p_src, src_offset, size);
94 memcpy(p_dst, p_src->p_data() + src_offset, size);
82 95 } }
83 96
84 97 [[nodiscard]] jen::vk::Result [[nodiscard]] jen::vk::Result
85 proceed_writes(jen::vk::Device *p_device,
86 jen::vk::ComputeCmdUnit *p_cmdUnit,
87 const jen::vk::Transfers &writes)
98 proceed_writes(jen::vk::Device *p_device,
99 jen::vk::ComputeCmdUnit *p_cmdUnit,
100 jen::vk::BufferTransfers buffer_writes,
101 jen::vk::ImagesTransfers images_writes)
88 102 { {
89 for (uint32_t i = 0; i < writes.count; ++i) {
90 auto &write = writes.p[i];
103 auto &cmd = p_cmdUnit->transfer_cmds.primary[0];
104
105 auto begin = [&cmd, &p_cmdUnit]() -> jen::vk::Result {
106 if (not p_cmdUnit->wait_transfer_write) {
107 jen::vk::Result res;
108 res = cmd.begin(vkw::CmdUsage::ONE_TIME_SUBMIT);
109 if (res != VK_SUCCESS)
110 return res;
111 p_cmdUnit->wait_transfer_write = true;
112 }
113 return VK_SUCCESS;
114 };
115
116 for (uint32_t i = 0; i < buffer_writes.count(); ++i) {
117 auto &write = buffer_writes[i];
91 118 auto &buffer = *write.p_buffer; auto &buffer = *write.p_buffer;
92 119
93 120 jen::vk::DeviceBufferPart *p_part; jen::vk::DeviceBufferPart *p_part;
 
... ... proceed_writes(jen::vk::Device *p_device,
96 123 else else
97 124 p_part = &buffer.part; p_part = &buffer.part;
98 125
99 write_to_allocation(p_part, write);
126 write_to_allocation(write.p_data, p_part, write.offset, write.size);
100 127
101 128 if (buffer.use_staging) { if (buffer.use_staging) {
102 129 vkw::BufferChange bs; vkw::BufferChange bs;
 
... ... proceed_writes(jen::vk::Device *p_device,
106 133 region.offsets.src = buffer.staging.offset(); region.offsets.src = buffer.staging.offset();
107 134 region.offsets.dst = buffer.part.offset(); region.offsets.dst = buffer.part.offset();
108 135 region.size = write.size; region.size = write.size;
109 if (not p_cmdUnit->wait_transfer_write) {
110 jen::vk::Result res;
111 res = p_cmdUnit->transfer_cmds.primary[0]
112 .begin(vkw::CmdUsage::ONE_TIME_SUBMIT);
113 if (res != VK_SUCCESS)
114 return res;
115 p_cmdUnit->wait_transfer_write = true;
136 auto res = begin();
137 if (res != VK_SUCCESS)
138 return res;
139 cmd.cmd_cp_buffer(bs, region);
140 }
141 }
142
143 for (uint32_t i = 0; i < images_writes.count(); ++i) {
144 auto res = begin();
145 if (res != VK_SUCCESS)
146 return res;
147
148 auto &w = images_writes[i];
149 auto &im = *w.p_image;
150
151 if (im.layout != vkw::ImLayout::TRANSFER_DST) {
152 vkw::StageMaskChange stages;
153 stages.src = vkw::StageFlag::TOP_OF_PIPE;
154 stages.dst = vkw::StageFlag::TRANSFER;
155 im.transitionLayout(&cmd, vkw::ImLayout::TRANSFER_DST, stages);
156 }
157
158 vkw::DeviceSize offset = 0;
159 for (auto &r : w.transfers) {
160 auto size = r.extent.volume() * vkw::format_size(im.format)
161 * r.layer_count;
162 write_to_allocation(r.p_data, &im.staging, offset, size);
163
164 vkw::BufferAndImageRegion region; {
165 region.bufferOffset = im.staging.offset() + offset;
166 region.bufferRowLength = region.bufferImageHeight = 0;
167 region.imageSubresource = {
168 vkw::ImAspect::COLOR,
169 r.mip_level,
170 r.layer_offset,
171 r.layer_count
172 };
173 region.imageOffset.x = int32_t(r.offset.x);
174 region.imageOffset.y = int32_t(r.offset.y);
175 region.imageOffset.z = int32_t(r.offset.z);
176 region.imageExtent.width = r.extent.x;
177 region.imageExtent.height = r.extent.y;
178 region.imageExtent.depth = r.extent.z;
116 179 } }
117 p_cmdUnit->transfer_cmds.primary[0].cmd_cp_buffer(bs, region);
180 cmd.cmd_cp_buffer_to_image({im.staging.buffer, im.image.image},
181 region, vkw::ImLayout::TRANSFER_DST);
182
183 offset += size;
118 184 } }
119 185 } }
120 186
121 187 if (p_cmdUnit->wait_transfer_write) { if (p_cmdUnit->wait_transfer_write) {
122 188 jen::vk::Result res; jen::vk::Result res;
123 res = p_cmdUnit->transfer_cmds.primary[0].end();
189 res = cmd.end();
124 190 if (res != VK_SUCCESS) if (res != VK_SUCCESS)
125 191 return res; return res;
126 192 vkw::QueueSignal signal(p_cmdUnit->syncs.semaphores[0].p_vk); vkw::QueueSignal signal(p_cmdUnit->syncs.semaphores[0].p_vk);
127 vkw::QueueSubmit submit(p_cmdUnit->transfer_cmds.primary[0], {}, signal);
128 return p_device->queues.transfer.submit_locked(submit);
193 vkw::QueueSubmit submit(cmd, {}, signal);
194 res = p_device->queues.transfer.submit_locked(submit);
195 if (res != VK_SUCCESS)
196 return res;
197
198 for (uint32_t i = 0; i < images_writes.count(); ++i)
199 images_writes[i].p_image->layout = vkw::ImLayout::TRANSFER_DST;
129 200 } }
130 201
131 202 return VK_SUCCESS; return VK_SUCCESS;
 
... ... proceed_writes(jen::vk::Device *p_device,
134 205 [[nodiscard]] jen::vk::Result [[nodiscard]] jen::vk::Result
135 206 proceed_staging_reads(jen::vk::Device *p_device, proceed_staging_reads(jen::vk::Device *p_device,
136 207 jen::vk::ComputeCmdUnit *p_cmdUnit, jen::vk::ComputeCmdUnit *p_cmdUnit,
137 const jen::vk::Transfers &reads) {
138 for (uint32_t i = 0; i < reads.count; ++i) {
139 auto &read = reads.p[i];
208 jen::vk::BufferTransfers buffer_reads,
209 jen::vk::ImagesTransfers images_reads)
210 {
211 auto &cmd = p_cmdUnit->transfer_cmds.primary[1];
212 auto begin = [&cmd, &p_cmdUnit]() -> jen::vk::Result {
213 if (not p_cmdUnit->wait_transfer_read) {
214 jen::vk::Result res;
215 res = cmd.begin(vkw::CmdUsage::ONE_TIME_SUBMIT);
216 if (res != VK_SUCCESS)
217 return res;
218 p_cmdUnit->wait_transfer_read = true;
219 }
220 return VK_SUCCESS;
221 };
222
223 for (uint32_t i = 0; i < buffer_reads.count(); ++i) {
224 auto &read = buffer_reads[i];
140 225 auto &buffer = *read.p_buffer; auto &buffer = *read.p_buffer;
141 226
142 227 if (buffer.use_staging) { if (buffer.use_staging) {
 
... ... proceed_staging_reads(jen::vk::Device *p_device,
147 232 region.offsets.src = buffer.part.offset(); region.offsets.src = buffer.part.offset();
148 233 region.offsets.dst = buffer.staging.offset(); region.offsets.dst = buffer.staging.offset();
149 234 region.size = read.size; region.size = read.size;
150 if (not p_cmdUnit->wait_transfer_read) {
151 jen::vk::Result res;
152 res = p_cmdUnit->transfer_cmds.primary[1]
153 .begin(vkw::CmdUsage::ONE_TIME_SUBMIT);
154 if (res != VK_SUCCESS)
155 return res;
156 p_cmdUnit->wait_transfer_read = true;
235 auto res = begin();
236 if (res != VK_SUCCESS)
237 return res;
238 cmd.cmd_cp_buffer(bs, region);
239 }
240 }
241
242 for (uint32_t i = 0; i < images_reads.count(); ++i) {
243 auto res = begin();
244 if (res != VK_SUCCESS)
245 return res;
246
247 auto &w = images_reads[i];
248 auto &im = *w.p_image;
249
250 if (im.layout != vkw::ImLayout::TRANSFER_SRC) {
251 vkw::StageMaskChange stages;
252 stages.src = vkw::StageFlag::TOP_OF_PIPE;
253 stages.dst = vkw::StageFlag::TRANSFER;
254 im.transitionLayout(&cmd, vkw::ImLayout::TRANSFER_SRC, stages);
255 }
256
257 vkw::DeviceSize offset = 0;
258 for (auto &r : w.transfers) {
259 vkw::BufferAndImageRegion region; {
260 region.bufferOffset = im.staging.offset() + offset;
261 region.bufferRowLength = region.bufferImageHeight = 0;
262 region.imageSubresource = {
263 vkw::ImAspect::COLOR,
264 r.mip_level,
265 r.layer_offset,
266 r.layer_count
267 };
268 region.imageOffset.x = int32_t(r.offset.x);
269 region.imageOffset.y = int32_t(r.offset.y);
270 region.imageOffset.z = int32_t(r.offset.z);
271 region.imageExtent.width = r.extent.x;
272 region.imageExtent.height = r.extent.y;
273 region.imageExtent.depth = r.extent.z;
157 274 } }
158 p_cmdUnit->transfer_cmds.primary[1].cmd_cp_buffer(bs, region);
275 cmd.cmd_cp_image_to_buffer({im.image.image, im.staging.buffer},
276 region, vkw::ImLayout::TRANSFER_SRC);
277
278 offset += r.extent.volume() * vkw::format_size(im.format) * r.layer_count;
159 279 } }
160 280 } }
161 281
 
... ... proceed_staging_reads(jen::vk::Device *p_device,
168 288 vkw::QueueWait wait; vkw::QueueWait wait;
169 289 wait.semaphores = p_cmdUnit->syncs.semaphores[1].p_vk; wait.semaphores = p_cmdUnit->syncs.semaphores[1].p_vk;
170 290 wait.stage_masks = vkw::StageFlag::COMPUTE_SHADER; wait.stage_masks = vkw::StageFlag::COMPUTE_SHADER;
171 vkw::QueueSubmit submit(p_cmdUnit->transfer_cmds.primary[1], wait);
172 return p_device->queues.transfer
173 .submit_locked(submit, p_cmdUnit->syncs.fences[1]);
291 vkw::QueueSubmit submit(cmd, wait);
292 res = p_device->queues.transfer
293 .submit_locked(submit, p_cmdUnit->syncs.fences[1]);
294 if (res != VK_SUCCESS)
295 return res;
296
297 for (uint32_t i = 0; i < images_reads.count(); ++i)
298 images_reads[i].p_image->layout = vkw::ImLayout::TRANSFER_SRC;
174 299 } }
175 300
176 301 return VK_SUCCESS; return VK_SUCCESS;
 
... ... compute(const ComputeInfo &info)
238 363 if (res != VK_SUCCESS) if (res != VK_SUCCESS)
239 364 return res; return res;
240 365
241 res = proceed_writes(p_device, info.p_cmdUnit, info.writes);
366 res = proceed_writes(p_device, info.p_cmdUnit,
367 info.buffer_writes, info.images_writes);
242 368 if (res != VK_SUCCESS) if (res != VK_SUCCESS)
243 369 return res; return res;
244 370
 
... ... compute(const ComputeInfo &info)
252 378 res = cmd.begin(vkw::CmdUsage::ONE_TIME_SUBMIT); res = cmd.begin(vkw::CmdUsage::ONE_TIME_SUBMIT);
253 379 if (res != VK_SUCCESS) if (res != VK_SUCCESS)
254 380 return res; return res;
381
382 for (auto &im : info.p_bindings->storage_image) {
383 auto l = vkw::ImLayout::GENERAL;
384 if (im.p_image->layout == l)
385 continue;
386 vkw::StageMaskChange stages;
387 stages.src = vkw::StageFlag::TOP_OF_PIPE;
388 stages.dst = vkw::StageFlag::COMPUTE_SHADER;
389 im.p_image->transitionLayout(&cmd, l, stages);
390 }
391
255 392 cmd.cmd_set_pipeline(pipeline, vkw::BindPoint::COMPUTE); cmd.cmd_set_pipeline(pipeline, vkw::BindPoint::COMPUTE);
256 393
257 394 cmd.cmd_set_descr_sets(vkw::BindPoint::COMPUTE, pipelineLayout, set, 0); cmd.cmd_set_descr_sets(vkw::BindPoint::COMPUTE, pipelineLayout, set, 0);
 
... ... compute(const ComputeInfo &info)
262 399 return res; return res;
263 400
264 401 bool use_read_semaphore = false; bool use_read_semaphore = false;
265 for (uint32_t i = 0; i < info.reads.count; ++i) {
266 if (info.reads.p[i].p_buffer->use_staging) {
402 if (info.images_reads.count() > 0)
403 use_read_semaphore = true;
404 else for (uint32_t i = 0; i < info.buffer_reads.count(); ++i) {
405 if (info.buffer_reads[i].p_buffer->use_staging) {
267 406 use_read_semaphore = true; use_read_semaphore = true;
268 407 break; break;
269 408 } }
270 409 } }
410
271 411 vkw::QueueWait wait; vkw::QueueWait wait;
272 412 if (info.p_cmdUnit->wait_transfer_write) { if (info.p_cmdUnit->wait_transfer_write) {
273 413 wait.semaphores = syncs.semaphores[0].p_vk; wait.semaphores = syncs.semaphores[0].p_vk;
 
... ... compute(const ComputeInfo &info)
285 425 res = p_device->queues.compute.submit_locked(submit, syncs.fences[0]); res = p_device->queues.compute.submit_locked(submit, syncs.fences[0]);
286 426 if (res != VK_SUCCESS) if (res != VK_SUCCESS)
287 427 return res; return res;
428
429 for (auto &im : info.p_bindings->storage_image) {
430 auto l = vkw::ImLayout::GENERAL;
431 im.p_image->layout = l;
432 }
433
288 434 info.p_cmdUnit->wait_compute = true; info.p_cmdUnit->wait_compute = true;
289 435
290 res = proceed_staging_reads(p_device, info.p_cmdUnit, info.reads);
436 res = proceed_staging_reads(p_device, info.p_cmdUnit,
437 info.buffer_reads, info.images_reads);
291 438 if (res != VK_SUCCESS) if (res != VK_SUCCESS)
292 439 return res; return res;
293 440
 
... ... compute(const ComputeInfo &info)
295 442 } }
296 443
297 444 [[nodiscard]] jen::vk::Result jen::vk::ModuleCompute:: [[nodiscard]] jen::vk::Result jen::vk::ModuleCompute::
298 read(ComputeCmdUnit *p_cmdUnit, jen::vk::Transfers reads)
445 read(ComputeCmdUnit *p_cmdUnit,
446 BufferTransfers buffer_reads,
447 ImagesTransfers images_reads)
299 448 { {
300 449 Result res; Result res;
301 450 res = wait_unit(p_device, p_cmdUnit); res = wait_unit(p_device, p_cmdUnit);
302 451 if (res != VK_SUCCESS) if (res != VK_SUCCESS)
303 452 return res; return res;
304 453
305 for (uint32_t i = 0; i < reads.count; ++i) {
306 auto &read = reads.p[i];
454 for (uint32_t i = 0; i < buffer_reads.count(); ++i) {
455 auto &read = buffer_reads[i];
307 456 auto &buffer = *read.p_buffer; auto &buffer = *read.p_buffer;
308 457
309 458 jen::vk::DeviceBufferPart *p_part; jen::vk::DeviceBufferPart *p_part;
 
... ... read(ComputeCmdUnit *p_cmdUnit, jen::vk::Transfers reads)
312 461 else else
313 462 p_part = &buffer.part; p_part = &buffer.part;
314 463
315 read_from_allocation(*p_part, read);
464 read_from_allocation(p_part, read.p_data, read.offset, read.size);
316 465 } }
466
467 for (uint32_t i = 0; i < images_reads.count(); ++i) {
468 auto &read = images_reads[i];
469 auto &im = *read.p_image;
470 auto p_part = &im.staging;
471
472 vkw::DeviceSize offset = 0;
473 for (auto &r : read.transfers) {
474 auto size = r.extent.volume() * vkw::format_size(im.format)
475 * r.layer_count;
476 read_from_allocation(p_part, r.p_data, offset, size);
477 offset += size;
478 }
479 }
480
317 481 return VK_SUCCESS; return VK_SUCCESS;
318 482 } }
319 483
 
... ... destroy_bindings(BindingBufferView *p_bs, uint32_t count) {
337 501 p_bs[i].destroy(p_device); p_bs[i].destroy(p_device);
338 502 } }
339 503 void jen::vk::ModuleCompute:: void jen::vk::ModuleCompute::
504 destroy_images(Image *p_ims, uint32_t count) {
505 for (uint32_t i = 0; i < count; ++i)
506 p_ims[i].destroy(p_device);
507 }
508 void jen::vk::ModuleCompute::
340 509 destroy_pipeline(ComputePipeline *p_pl) { destroy_pipeline(ComputePipeline *p_pl) {
341 510 p_pl->destroy(p_device->device); p_pl->destroy(p_device->device);
342 511 } }
File src/compute/compute.h changed (mode: 100644) (index 4ac39f6..30cad1c)
6 6
7 7 namespace jen::vk namespace jen::vk
8 8 { {
9 struct ImageTransfer {
10 uint32_t mip_level;
11 uint32_t layer_offset;
12 uint32_t layer_count;
13 math::v3u32 offset;
14 math::v3u32 extent;
15 void *p_data;
16 };
17 struct ImageTransfers {
18 Image *p_image;
19 jl::rarray<const ImageTransfer> transfers;
20 };
21 using ImagesTransfers = jl::rarray<const ImageTransfers>;
22
9 23 struct BufferTransfer { struct BufferTransfer {
10 24 BindingBuffer *p_buffer; BindingBuffer *p_buffer;
11 25 vkw::DeviceSize offset; vkw::DeviceSize offset;
12 26 vkw::DeviceSize size; vkw::DeviceSize size;
13 27 void *p_data; void *p_data;
14 28 }; };
15 struct Transfers {
16 BufferTransfer *p;
17 uint32_t count;
18 };
29 using BufferTransfers = jl::rarray<const BufferTransfer>;
19 30
20 31 struct ComputeInfo { struct ComputeInfo {
21 32 ComputeCmdUnit *p_cmdUnit; ComputeCmdUnit *p_cmdUnit;
22 33 ComputePipeline *p_pipeline; ComputePipeline *p_pipeline;
23 34 BindingsSet *p_bindingsSet; BindingsSet *p_bindingsSet;
35 Bindings *p_bindings;
24 36 math::v3u32 group_count; math::v3u32 group_count;
25 Transfers writes;
26 Transfers reads;
37 BufferTransfers buffer_writes;
38 BufferTransfers buffer_reads;
39 ImagesTransfers images_writes;
40 ImagesTransfers images_reads;
27 41 }; };
28 42
29 43 constexpr static const uint32_t MAX_WORKGROUP_COUNT = 65535; constexpr static const uint32_t MAX_WORKGROUP_COUNT = 65535;
 
... ... namespace jen::vk
44 58 [[nodiscard]] Result [[nodiscard]] Result
45 59 create_bindings(BindingCreateInfos infos, VkFormat *p_formats, create_bindings(BindingCreateInfos infos, VkFormat *p_formats,
46 60 BindingBufferView *p_dst); BindingBufferView *p_dst);
61 [[nodiscard]] Result
62 create_images(ImageCreateInfos infos, Image *p_dst);
63
47 64 [[nodiscard]] Result [[nodiscard]] Result
48 65 create_bindingSet(const ComputePipeline &pipeline, const Bindings &bindings, create_bindingSet(const ComputePipeline &pipeline, const Bindings &bindings,
49 66 BindingsSet *p_dst); BindingsSet *p_dst);
50 67
51 68 [[nodiscard]] Result compute(const ComputeInfo&); [[nodiscard]] Result compute(const ComputeInfo&);
52 69
53 [[nodiscard]] Result read(ComputeCmdUnit *p_cmdUnit, Transfers reads);
70 [[nodiscard]] Result
71 read(ComputeCmdUnit *p_cmdUnit,
72 BufferTransfers buffer_reads,
73 ImagesTransfers image_reads);
54 74
55 75 [[nodiscard]] Result status_compute(ComputeCmdUnit *p_cmd); [[nodiscard]] Result status_compute(ComputeCmdUnit *p_cmd);
56 76
57 77 void destroy_bindingSet(BindingsSet *p_set); void destroy_bindingSet(BindingsSet *p_set);
58 78 void destroy_bindings(BindingBuffer *p_bs, uint32_t count = 1); void destroy_bindings(BindingBuffer *p_bs, uint32_t count = 1);
59 79 void destroy_bindings(BindingBufferView *p_bs, uint32_t count = 1); void destroy_bindings(BindingBufferView *p_bs, uint32_t count = 1);
80 void destroy_images(Image *p_ims, uint32_t count = 1);
60 81 void destroy_pipeline(ComputePipeline *p_pl); void destroy_pipeline(ComputePipeline *p_pl);
61 82 void destroy_cmdUnit(ComputeCmdUnit *p); void destroy_cmdUnit(ComputeCmdUnit *p);
62 83
File src/compute/pipeline.h changed (mode: 100644) (index cdd465d..a1354ce)
... ... namespace jen::vk {
6 6 struct ComputePipeline struct ComputePipeline
7 7 { {
8 8 [[nodiscard]] Result [[nodiscard]] Result
9 init(vkw::Device device, const Bindings &bi, const char *p_shader_filepath,
9 init(vkw::Device device, const Bindings &bi, const char *p_shader_filepath,
10 10 const vkw::ShaderSpecialization *p_specialization) const vkw::ShaderSpecialization *p_specialization)
11 11 { {
12 12 Result res; Result res;
 
... ... namespace jen::vk {
25 25 put_part(vkw::DescrType::STORAGE_TEXEL_BUFFER, bi.storage_texel_buffer); put_part(vkw::DescrType::STORAGE_TEXEL_BUFFER, bi.storage_texel_buffer);
26 26 put_part(vkw::DescrType::UNIFORM_BUFFER, bi.uniform_buffer); put_part(vkw::DescrType::UNIFORM_BUFFER, bi.uniform_buffer);
27 27 put_part(vkw::DescrType::STORAGE_BUFFER, bi.storage_buffer); put_part(vkw::DescrType::STORAGE_BUFFER, bi.storage_buffer);
28 put_part(vkw::DescrType::STORAGE_IMAGE, bi.storage_image);
28 29
29 30 res = setLayout.init(device, {dbinds.begin(), numBinds}); res = setLayout.init(device, {dbinds.begin(), numBinds});
30 31 if (res != VK_SUCCESS) if (res != VK_SUCCESS)
 
... ... namespace jen::vk {
35 36 goto CSL; goto CSL;
36 37
37 38 res = pipeline.init(device, vkw::PipelineCompute{ res = pipeline.init(device, vkw::PipelineCompute{
38 .stage = {vkw::ShaderStage::COMPUTE, shader, p_specialization},
39 .stage = {vkw::ShaderStage::COMPUTE, shader, p_specialization},
39 40 .layout = layout .layout = layout
40 41 }); });
41 42 if (res != VK_SUCCESS) if (res != VK_SUCCESS)
 
... ... namespace jen::vk {
43 44
44 45 return res; return res;
45 46
46 CL: layout.destroy(device);
47 CSL: setLayout.destroy(device);
48 CSH: shader.destroy(device);
47 CL:
48 layout.destroy(device);
49 CSL:
50 setLayout.destroy(device);
51 CSH:
52 shader.destroy(device);
49 53 return res; return res;
50 54 } }
51 55
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/Jackalope/jen

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/Jackalope/jen

Clone this repository using git:
git clone git://git.rocketgit.com/user/Jackalope/jen

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main