summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederico Linhares <fred@linhares.blue>2022-08-30 17:45:40 -0300
committerFrederico Linhares <fred@linhares.blue>2022-08-30 17:45:40 -0300
commitf1f1576543bb4e0f3b9bc4cd0ba4a12a70546c3c (patch)
tree87160fb7462ee5b4385ba6e9b0ccb51657b2f796
parent42e03ddc3b28c41b81fb5410feb72750530ffa13 (diff)
feat Create 2d graphics pipeline
-rw-r--r--Rakefile16
-rw-r--r--glsl/shader.vert38
-rw-r--r--glsl/shader_2d.frag.glsl30
-rw-r--r--glsl/shader_2d.vert.glsl53
-rw-r--r--glsl/shader_3d.frag.glsl (renamed from glsl/shader.frag)16
-rw-r--r--glsl/shader_3d.vert.glsl54
-rw-r--r--src/candy_gear.cpp4
-rw-r--r--src/core.cpp52
-rw-r--r--src/core.hpp8
-rw-r--r--src/main.cpp4
-rw-r--r--src/model.cpp5
-rw-r--r--src/model.hpp3
-rw-r--r--src/rotation3d.hpp6
-rw-r--r--src/sprite.cpp81
-rw-r--r--src/sprite.hpp25
-rw-r--r--src/vector3d.hpp6
-rw-r--r--src/vk/device.cpp18
-rw-r--r--src/vk/device.hpp7
-rw-r--r--src/vk/graphics_pipeline_2d.cpp488
-rw-r--r--src/vk/graphics_pipeline_2d.hpp51
-rw-r--r--src/vk/graphics_pipeline_3d.cpp (renamed from src/vk/graphics_pipeline.cpp)270
-rw-r--r--src/vk/graphics_pipeline_3d.hpp (renamed from src/vk/graphics_pipeline.hpp)26
-rw-r--r--src/vk/model.cpp9
-rw-r--r--src/vk/renderer.cpp245
-rw-r--r--src/vk/renderer.hpp49
-rw-r--r--src/vk/sprite.cpp226
-rw-r--r--src/vk/sprite.hpp55
-rw-r--r--src/vk/uniform_buffer.hpp7
-rw-r--r--src/vk/vertex_3d.hpp (renamed from src/vk/vertex.hpp)8
-rw-r--r--test/src/main.rb2
30 files changed, 1530 insertions, 332 deletions
diff --git a/Rakefile b/Rakefile
index 6e6388e..ee510f5 100644
--- a/Rakefile
+++ b/Rakefile
@@ -33,11 +33,12 @@ RB_LIBS = FileList[
'lib/*.rb'
]
-SPV_FILES = [
- 'glsl/vert.spv',
- 'glsl/frag.spv'
+GLSL_FILES = FileList[
+ 'glsl/*.glsl'
]
+SPV_FILES = GLSL_FILES.ext('.spv')
+
LIBRARIES = [
'SDL2',
'SDL2_image',
@@ -80,18 +81,17 @@ task :pkg do
`tar -czvf pkg/#{name}.tar.gz --transform 's,^,#{name}/,' #{files}`
end
-task :shaders do
- system('glslangValidator -V glsl/shader.vert -o glsl/vert.spv') and
- system('glslangValidator -V glsl/shader.frag -o glsl/frag.spv')
+rule '.spv' => ['.glsl'] do |t|
+ system("glslangValidator -V #{t.source} -o #{t.name}")
end
-task build: CPP_OBJS do
+task build: CPP_OBJS + SPV_FILES do
libs = LIBRARIES.inject('') {_1 + "-l#{_2} "}
`g++ -o #{OBJ} #{CPP_OBJS} #{libs}`
end
-task :install do
+task install: :build do
destdir = ENV['DESTDIR'] || ''
# Install engine
diff --git a/glsl/shader.vert b/glsl/shader.vert
deleted file mode 100644
index 50dcec5..0000000
--- a/glsl/shader.vert
+++ /dev/null
@@ -1,38 +0,0 @@
-#version 450
-#extension GL_ARB_separate_shader_objects : enable
-
-layout(location = 0) in vec3 in_position;
-layout(location = 1) in vec3 in_normal;
-layout(location = 2) in vec2 in_texture_coord;
-
-
-layout(location = 0) out DataTransferObject
-{
- vec4 frag_color;
- vec2 frag_texture_coord;
- vec3 normal;
-} out_dto;
-
-layout(set = 0, binding = 0) uniform UBOViewProjection
-{
- mat4 view;
- mat4 proj;
- vec4 ambient_color;
-} ubo_view_projection;
-
-layout(set = 1, binding = 0) uniform UBOModelInstance
-{
- mat4 model[128];
-} ubo_model_instance;
-
-void
-main()
-{
- gl_Position =
- ubo_view_projection.proj * ubo_view_projection.view *
- ubo_model_instance.model[gl_InstanceIndex] * vec4(in_position, 1.0);
- out_dto.frag_color = ubo_view_projection.ambient_color;
- out_dto.frag_texture_coord = in_texture_coord;
- out_dto.normal = mat3(ubo_model_instance.model[gl_InstanceIndex]) *
- in_normal;
-}
diff --git a/glsl/shader_2d.frag.glsl b/glsl/shader_2d.frag.glsl
new file mode 100644
index 0000000..1edbbbb
--- /dev/null
+++ b/glsl/shader_2d.frag.glsl
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec2 in_texture_coord;
+
+layout(location = 0) out vec4 out_color;
+
+layout(set = 0, binding = 1) uniform sampler2D texture_sampler;
+
+void
+main()
+{
+ out_color = texture(texture_sampler, in_texture_coord);
+}
diff --git a/glsl/shader_2d.vert.glsl b/glsl/shader_2d.vert.glsl
new file mode 100644
index 0000000..6e8ac59
--- /dev/null
+++ b/glsl/shader_2d.vert.glsl
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec2 in_texture_coord;
+
+layout(location = 0) out vec2 out_texture_coord;
+
+layout(set = 0, binding = 0) uniform UBOSpritePositions
+{
+ vec4 positions[128];
+} ubo_sprite_positions;
+
+void
+main()
+{
+ vec2 coordinate;
+ vec4 position = ubo_sprite_positions.positions[gl_InstanceIndex];
+
+ out_texture_coord = in_texture_coord;
+
+ switch(gl_VertexIndex)
+ {
+ case 0:
+ coordinate = vec2(position.x, position.y);
+ break;
+ case 1:
+ coordinate = vec2(position.x, position.w);
+ break;
+ case 2:
+ coordinate = vec2(position.z, position.y);
+ break;
+ case 3:
+ coordinate = vec2(position.z, position.w);
+ break;
+ }
+ gl_Position = vec4(coordinate, 0.0, 1.0);
+}
diff --git a/glsl/shader.frag b/glsl/shader_3d.frag.glsl
index 8ecf883..5c4b857 100644
--- a/glsl/shader.frag
+++ b/glsl/shader_3d.frag.glsl
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#version 450
#extension GL_ARB_separate_shader_objects : enable
diff --git a/glsl/shader_3d.vert.glsl b/glsl/shader_3d.vert.glsl
new file mode 100644
index 0000000..387ad00
--- /dev/null
+++ b/glsl/shader_3d.vert.glsl
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec3 in_position;
+layout(location = 1) in vec3 in_normal;
+layout(location = 2) in vec2 in_texture_coord;
+
+
+layout(location = 0) out DataTransferObject
+{
+ vec4 frag_color;
+ vec2 frag_texture_coord;
+ vec3 normal;
+} out_dto;
+
+layout(set = 0, binding = 0) uniform UBOViewProjection
+{
+ mat4 view;
+ mat4 proj;
+ vec4 ambient_color;
+} ubo_view_projection;
+
+layout(set = 1, binding = 0) uniform UBOModelInstance
+{
+ mat4 instances[128];
+} ubo_model_instance;
+
+void
+main()
+{
+ gl_Position =
+ ubo_view_projection.proj * ubo_view_projection.view *
+ ubo_model_instance.instances[gl_InstanceIndex] * vec4(in_position, 1.0);
+ out_dto.frag_color = ubo_view_projection.ambient_color;
+ out_dto.frag_texture_coord = in_texture_coord;
+ out_dto.normal = mat3(ubo_model_instance.instances[gl_InstanceIndex]) *
+ in_normal;
+}
diff --git a/src/candy_gear.cpp b/src/candy_gear.cpp
index d0922c6..45d105e 100644
--- a/src/candy_gear.cpp
+++ b/src/candy_gear.cpp
@@ -65,7 +65,7 @@ cg_mCandyGear_set_camera_position(mrb_state *mrb, mrb_value self)
std::shared_ptr<glm::vec3> *camera_position;
mrb_get_args(mrb, "d", &camera_position, &cg_vector3d_type);
- cg_core.vk_graphics_pipeline->camera_position = (*camera_position);
+ cg_core.vk_graphics_pipeline_3d->camera_position = (*camera_position);
return self;
}
@@ -76,7 +76,7 @@ cg_mCandyGear_set_camera_rotation(mrb_state *mrb, mrb_value self)
std::shared_ptr<glm::vec3> *camera_rotation;
mrb_get_args(mrb, "d", &camera_rotation, &cg_rotation3d_type);
- cg_core.vk_graphics_pipeline->camera_rotation = (*camera_rotation);
+ cg_core.vk_graphics_pipeline_3d->camera_rotation = (*camera_rotation);
return self;
}
diff --git a/src/core.cpp b/src/core.cpp
index a1c996f..ecf7d47 100644
--- a/src/core.cpp
+++ b/src/core.cpp
@@ -452,22 +452,60 @@ unload_vk_swapchain(void *obj)
}
static void
-load_vk_graphics_pipeline(void *obj)
+load_vk_graphics_pipeline_3d(void *obj)
{
try
{
- cg_core.vk_graphics_pipeline = new VK::GraphicsPipeline();
+ cg_core.vk_graphics_pipeline_3d = new VK::GraphicsPipeline3D();
}
catch(const CommandError &e)
{
- throw CommandError{"Failed to create graphics pipeline."};
+ throw CommandError{"Failed to create 3d graphics pipeline."};
}
}
static void
-unload_vk_graphics_pipeline(void *obj)
+unload_vk_graphics_pipeline_3d(void *obj)
{
- delete cg_core.vk_graphics_pipeline;
+ delete cg_core.vk_graphics_pipeline_3d;
+}
+
+static void
+load_vk_graphics_pipeline_2d(void *obj)
+{
+ try
+ {
+ cg_core.vk_graphics_pipeline_2d = new VK::GraphicsPipeline2D();
+ }
+ catch(const CommandError &e)
+ {
+ throw CommandError{"Failed to create 2d graphics pipeline."};
+ }
+}
+
+static void
+unload_vk_graphics_pipeline_2d(void *obj)
+{
+ delete cg_core.vk_graphics_pipeline_2d;
+}
+
+static void
+load_vk_renderer(void *obj)
+{
+ try
+ {
+ cg_core.vk_renderer = new VK::Renderer();
+ }
+ catch(const CommandError &e)
+ {
+ throw CommandError{"Failed to create renderer."};
+ }
+}
+
+static void
+unload_vk_renderer(void *obj)
+{
+ delete cg_core.vk_renderer;
}
static void
@@ -508,7 +546,9 @@ const CommandChain cg_sCore::loader{
#endif
{&load_vk_devices, &unload_vk_devices},
{&load_vk_swapchain, &unload_vk_swapchain},
- {&load_vk_graphics_pipeline, &unload_vk_graphics_pipeline},
+ {&load_vk_graphics_pipeline_3d, &unload_vk_graphics_pipeline_3d},
+ {&load_vk_graphics_pipeline_2d, &unload_vk_graphics_pipeline_2d},
+ {&load_vk_renderer, &unload_vk_renderer},
{&load_mruby, &unload_mruby},
{&load_mruby_symbols, nullptr}
};
diff --git a/src/core.hpp b/src/core.hpp
index d9e68e8..1f75279 100644
--- a/src/core.hpp
+++ b/src/core.hpp
@@ -42,7 +42,9 @@
#include "log.hpp"
#include "vk/device.hpp"
-#include "vk/graphics_pipeline.hpp"
+#include "vk/graphics_pipeline_3d.hpp"
+#include "vk/graphics_pipeline_2d.hpp"
+#include "vk/renderer.hpp"
#include "vk/swapchain.hpp"
extern std::random_device random_seed;
@@ -103,7 +105,9 @@ struct cg_sCore
std::vector<VK::Device> vk_devices;
VK::Device *vk_device_with_swapchain;
VK::Swapchain *vk_swapchain;
- VK::GraphicsPipeline *vk_graphics_pipeline;
+ VK::GraphicsPipeline3D *vk_graphics_pipeline_3d;
+ VK::GraphicsPipeline2D *vk_graphics_pipeline_2d;
+ VK::Renderer *vk_renderer;
bool quit_game;
};
diff --git a/src/main.cpp b/src/main.cpp
index 61f7967..d82ba1b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -23,6 +23,7 @@
#include "model.hpp"
#include "rotation3d.hpp"
#include "sound.hpp"
+#include "sprite.hpp"
#include "texture.hpp"
#include "vector3d.hpp"
@@ -63,6 +64,7 @@ int main(int argc, char *argv[])
cg_model_init(cg_core.mrb);
cg_rotation3d_init(cg_core.mrb);
cg_sound_init(cg_core.mrb);
+ cg_sprite_init(cg_core.mrb);
cg_texture_init(cg_core.mrb);
cg_vector3d_init(cg_core.mrb);
@@ -119,7 +121,7 @@ int main(int argc, char *argv[])
mrb_funcall_id(cg_core.mrb, main_obj, sym_tick, 0);
if (cg_core.mrb->exc) handle_error(cg_core.mrb);
- cg_core.vk_graphics_pipeline->draw();
+ cg_core.vk_renderer->draw();
// Timer
{
diff --git a/src/model.cpp b/src/model.cpp
index 06ddf65..c0a35d8 100644
--- a/src/model.cpp
+++ b/src/model.cpp
@@ -70,9 +70,8 @@ cg_cModel_draw(mrb_state *mrb, mrb_value self)
instance.position = **position;
instance.rotation = **rotation;
- auto &instances =
- cg_core.vk_graphics_pipeline->models_to_draw[
- cg_core.vk_graphics_pipeline->current_frame][*ptr];
+ auto &instances = cg_core.vk_graphics_pipeline_3d->models_to_draw[
+ cg_core.vk_renderer->current_frame][*ptr];
instances.push_back(instance);
return self;
diff --git a/src/model.hpp b/src/model.hpp
index 414fa30..50e49a1 100644
--- a/src/model.hpp
+++ b/src/model.hpp
@@ -17,10 +17,7 @@
#ifndef CANDY_GEAR_MODEL_H
#define CANDY_GEAR_MODEL_H 1
-#include <memory>
-
#include "core.hpp"
-#include "vk/model.hpp"
void
cg_model_init(mrb_state *mrb);
diff --git a/src/rotation3d.hpp b/src/rotation3d.hpp
index 6b94e89..6d3980d 100644
--- a/src/rotation3d.hpp
+++ b/src/rotation3d.hpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef CANDY_GEAR_ROTATION3D_H
-#define CANDY_GEAR_ROTATION3D_H 1
+#ifndef CANDY_GEAR_ROTATION_3D_H
+#define CANDY_GEAR_ROTATION_3D_H 1
#include "core.hpp"
@@ -24,4 +24,4 @@ extern const struct mrb_data_type cg_rotation3d_type;
void
cg_rotation3d_init(mrb_state *mrb);
-#endif /* CANDY_GEAR_ROTATION3D_H */
+#endif /* CANDY_GEAR_ROTATION_3D_H */
diff --git a/src/sprite.cpp b/src/sprite.cpp
new file mode 100644
index 0000000..2aad6d6
--- /dev/null
+++ b/src/sprite.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sprite.hpp"
+
+#include "vk/sprite.hpp"
+#include "texture.hpp"
+
+void
+cg_free_sprite(mrb_state *mrb, void* obj)
+{
+ auto ptr = static_cast<std::shared_ptr<VK::Sprite>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_sprite_type = { "CG_Sprite", cg_free_sprite };
+
+static mrb_value
+cg_cSprite_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_float x, y, w, h;
+
+ std::shared_ptr<VK::Texture> *texture;
+ std::shared_ptr<VK::Sprite> *ptr;
+
+ mrb_get_args(mrb, "dffff", &texture, &cg_texture_type, &x, &y, &w, &h);
+ ptr = (std::shared_ptr<VK::Sprite>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<VK::Sprite>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<VK::Sprite>));
+
+ glm::vec4 rect{x, y, w, h};
+ new(ptr)std::shared_ptr<VK::Sprite>(
+ std::make_shared<VK::Sprite>(*texture, rect));
+
+ mrb_data_init(self, ptr, &cg_sprite_type);
+ return self;
+}
+
+static mrb_value
+cg_cSprite_draw(mrb_state *mrb, mrb_value self)
+{
+ auto *ptr = (std::shared_ptr<VK::Sprite>*)DATA_PTR(self);
+ mrb_float x, y, w, h;
+
+ mrb_get_args(mrb, "ffff", &x, &y, &w, &h);
+ auto &positions = cg_core.vk_graphics_pipeline_2d->sprites_to_draw[
+ cg_core.vk_renderer->current_frame][*ptr];
+ glm::vec4 position{x, y, w, h};
+ positions.push_back(position);
+
+ return self;
+}
+
+void
+cg_sprite_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cSprite;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cSprite = mrb_define_class_under(mrb, cg_m, "Sprite", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cSprite, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cSprite, "initialize", cg_cSprite_initialize, MRB_ARGS_REQ(5));
+ mrb_define_method(mrb, cg_cSprite, "draw", cg_cSprite_draw, MRB_ARGS_REQ(4));
+}
diff --git a/src/sprite.hpp b/src/sprite.hpp
new file mode 100644
index 0000000..6b21023
--- /dev/null
+++ b/src/sprite.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_SPRITE_H
+#define CANDY_GEAR_SPRITE_H 1
+
+#include "core.hpp"
+
+void
+cg_sprite_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_SPRITE_H */
diff --git a/src/vector3d.hpp b/src/vector3d.hpp
index 2533f4e..fab6f61 100644
--- a/src/vector3d.hpp
+++ b/src/vector3d.hpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef CANDY_GEAR_VECTOR3D_H
-#define CANDY_GEAR_VECTOR3D_H 1
+#ifndef CANDY_GEAR_VECTOR_3D_H
+#define CANDY_GEAR_VECTOR_3D_H 1
#include "core.hpp"
@@ -31,4 +31,4 @@ cg_cVector3D_get_z(mrb_state *mrb, mrb_value self);
void
cg_vector3d_init(mrb_state *mrb);
-#endif /* CANDY_GEAR_VECTOR3D_H */
+#endif /* CANDY_GEAR_VECTOR_3D_H */
diff --git a/src/vk/device.cpp b/src/vk/device.cpp
index b4b47fb..74727f9 100644
--- a/src/vk/device.cpp
+++ b/src/vk/device.cpp
@@ -157,10 +157,14 @@ Device::Device(VkPhysicalDevice vk_physical_device, bool with_swapchain)
// Load Shaders
{
- this->vert_shader_module = create_shader_module(
- this->device, DATA_DIR "/glsl/vert.spv");
- this->frag_shader_module = create_shader_module(
- this->device, DATA_DIR "/glsl/frag.spv");
+ this->vert3d_shader_module = create_shader_module(
+ this->device, DATA_DIR "/glsl/shader_3d.vert.spv");
+ this->frag3d_shader_module = create_shader_module(
+ this->device, DATA_DIR "/glsl/shader_3d.frag.spv");
+ this->vert2d_shader_module = create_shader_module(
+ this->device, DATA_DIR "/glsl/shader_2d.vert.spv");
+ this->frag2d_shader_module = create_shader_module(
+ this->device, DATA_DIR "/glsl/shader_2d.frag.spv");
}
this->queue_families = static_cast<QueueFamily*>(
@@ -194,8 +198,10 @@ Device::~Device()
std::free(this->queue_families);
// Destroy shaders
- vkDestroyShaderModule(this->device, this->vert_shader_module, nullptr);
- vkDestroyShaderModule(this->device, this->frag_shader_module, nullptr);
+ vkDestroyShaderModule(this->device, this->vert3d_shader_module, nullptr);
+ vkDestroyShaderModule(this->device, this->frag3d_shader_module, nullptr);
+ vkDestroyShaderModule(this->device, this->vert2d_shader_module, nullptr);
+ vkDestroyShaderModule(this->device, this->frag2d_shader_module, nullptr);
vkDeviceWaitIdle(this->device);
vkDestroyDevice(this->device, nullptr);
diff --git a/src/vk/device.hpp b/src/vk/device.hpp
index 56bd6e4..cc5df82 100644
--- a/src/vk/device.hpp
+++ b/src/vk/device.hpp
@@ -36,8 +36,11 @@ struct Device
public:
VkDevice device;
VkPhysicalDevice physical_device;
- VkShaderModule vert_shader_module;
- VkShaderModule frag_shader_module;
+
+ VkShaderModule vert3d_shader_module;
+ VkShaderModule frag3d_shader_module;
+ VkShaderModule vert2d_shader_module;
+ VkShaderModule frag2d_shader_module;
bool with_swapchain;
diff --git a/src/vk/graphics_pipeline_2d.cpp b/src/vk/graphics_pipeline_2d.cpp
new file mode 100644
index 0000000..e8fd002
--- /dev/null
+++ b/src/vk/graphics_pipeline_2d.cpp
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "graphics_pipeline_2d.hpp"
+
+#include <array>
+
+#include "../core.hpp"
+#include "sprite.hpp"
+
+namespace
+{
+
+void
+load_descriptor_set_layout_image_sprites(void *obj)
+{
+ auto self = static_cast<VK::GraphicsPipeline2D*>(obj);
+
+ std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{};
+
+ layout_bindings[0].binding = 0;
+ layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ layout_bindings[0].descriptorCount = 1;
+ layout_bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
+ layout_bindings[0].pImmutableSamplers = nullptr;
+
+ layout_bindings[1].binding = 1;
+ layout_bindings[1].descriptorType =
+ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ layout_bindings[1].descriptorCount = 1;
+ layout_bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
+ layout_bindings[1].pImmutableSamplers = nullptr;
+
+ VkDescriptorSetLayoutCreateInfo layout_info{};
+ layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ layout_info.pNext = nullptr;
+ layout_info.flags = 0;
+ layout_info.bindingCount = static_cast<uint32_t>(layout_bindings.size());
+ layout_info.pBindings = layout_bindings.data();
+
+ if(vkCreateDescriptorSetLayout(
+ cg_core.vk_device_with_swapchain->device, &layout_info, nullptr,
+ &self->descriptor_set_layout_sprites) != VK_SUCCESS)
+ throw CommandError{
+ "Failed to create Vulkan descriptor set layout for model instance."};
+}
+
+void
+unload_descriptor_set_layout_image_sprites(void *obj)
+{
+ auto self = static_cast<VK::GraphicsPipeline2D*>(obj);
+
+ vkDestroyDescriptorSetLayout(
+ cg_core.vk_device_with_swapchain->device,
+ self->descriptor_set_layout_sprites, nullptr);
+}
+
+void
+load_pipeline_layout(void *obj)
+{
+ auto self = static_cast<VK::GraphicsPipeline2D*>(obj);
+
+ std::array<VkDescriptorSetLayout, 1> set_layouts{
+ self->descriptor_set_layout_sprites
+ };
+
+ VkPipelineLayoutCreateInfo pipeline_layout_info{};
+ pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipeline_layout_info.setLayoutCount = set_layouts.size();
+ pipeline_layout_info.pSetLayouts = set_layouts.data();
+ pipeline_layout_info.pushConstantRangeCount = 0;
+ pipeline_layout_info.pPushConstantRanges = nullptr;
+
+ if(vkCreatePipelineLayout(
+ cg_core.vk_device_with_swapchain->device,
+ &pipeline_layout_info, nullptr, &self->pipeline_layout) != VK_SUCCESS)
+ throw CommandError{"Failed to create Vulkan pipeline layout."};
+}
+
+void
+unload_pipeline_layout(void *obj)
+{
+ auto self = static_cast<VK::GraphicsPipeline2D*>(obj);
+
+ vkDestroyPipelineLayout(
+ cg_core.vk_device_with_swapchain->device, self->pipeline_layout, nullptr);
+}
+
+void
+load_render_pass(void *obj)
+{
+ auto self = static_cast<VK::GraphicsPipeline2D*>(obj);
+
+ std::array<VkAttachmentDescription, 1> attachments{};
+ // Color attachment.
+ attachments[0].flags = 0;
+ attachments[0].format = cg_core.vk_swapchain->image_format;
+ attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
+ attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+ attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+
+ VkAttachmentReference color_attachment_ref{};
+ color_attachment_ref.attachment = 0;
+ color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+ VkSubpassDescription subpass{};
+ subpass.flags = 0;
+ subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ subpass.inputAttachmentCount = 0;
+ subpass.pInputAttachments = nullptr;
+ subpass.colorAttachmentCount = 1;
+ subpass.pColorAttachments = &color_attachment_ref;
+ subpass.pResolveAttachments = nullptr;
+ subpass.pDepthStencilAttachment = nullptr;
+ subpass.preserveAttachmentCount = 0;
+ subpass.pPreserveAttachments = nullptr;
+
+ VkSubpassDependency dependency{};
+ dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
+ dependency.dstSubpass = 0;
+ dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
+ VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
+ dependency.srcAccessMask = 0;
+ dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
+ VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
+ dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+
+ VkRenderPassCreateInfo render_pass_info{};
+ render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+ render_pass_info.pNext = nullptr;
+ render_pass_info.flags = 0;
+ render_pass_info.attachmentCount = attachments.size();
+ render_pass_info.pAttachments = attachments.data();
+ render_pass_info.subpassCount = 1;
+ render_pass_info.pSubpasses = &subpass;
+ render_pass_info.dependencyCount = 1;
+ render_pass_info.pDependencies = &dependency;
+
+ if(vkCreateRenderPass(
+ cg_core.vk_device_with_swapchain->device, &render_pass_info,
+ nullptr, &self->render_pass) != VK_SUCCESS)
+ throw CommandError{"Failed to create Vulkan Render Pass."};
+}
+
+void
+unload_render_pass(void *obj)
+{
+ auto self = static_cast<VK::GraphicsPipeline2D*>(obj);
+
+ vkDestroyRenderPass(
+ cg_core.vk_device_with_swapchain->device, self->render_pass, nullptr);
+}
+
+void
+load_framebuffer(void *obj)
+{
+ auto self = static_cast<VK::GraphicsPipeline2D*>(obj);
+
+ self->swapchain_framebuffers.resize(cg_core.vk_swapchain->images_count);
+ for (auto i{0}; i < cg_core.vk_swapchain->images_count; i++)
+ {
+ std::array<VkImageView, 1> attachments = {
+ cg_core.vk_swapchain->image_views[i]
+ };
+
+ VkFramebufferCreateInfo framebuffer_info{};
+ framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ framebuffer_info.renderPass = self->render_pass;
+ framebuffer_info.attachmentCount = attachments.size();
+ framebuffer_info.pAttachments = attachments.data();
+ framebuffer_info.width = cg_core.screen_width;
+ framebuffer_info.height = cg_core.screen_height;
+ framebuffer_info.layers = 1;
+
+ if(vkCreateFramebuffer(
+ cg_core.vk_device_with_swapchain->device, &framebuffer_info, nullptr,
+ &self->swapchain_framebuffers[i]) != VK_SUCCESS)
+ throw CommandError{"Failed to create Vulkan Framebuffer."};
+ }
+}
+
+void
+unload_framebuffer(void *obj)
+{
+ auto self = static_cast<VK::GraphicsPipeline2D*>(obj);
+
+ for(auto framebuffer: self->swapchain_framebuffers)
+ vkDestroyFramebuffer(
+ cg_core.vk_device_with_swapchain->device, framebuffer, nullptr);
+}
+
+void
+load_pipeline(void *obj)
+{
+ auto self = static_cast<VK::GraphicsPipeline2D*>(obj);
+
+ VkPipelineShaderStageCreateInfo vert_shader_stage_info{};
+ vert_shader_stage_info.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ vert_shader_stage_info.pNext = nullptr;
+ vert_shader_stage_info.flags = 0;
+ vert_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
+ vert_shader_stage_info.module =
+ cg_core.vk_device_with_swapchain->vert2d_shader_module;
+ vert_shader_stage_info.pName = "main";
+ vert_shader_stage_info.pSpecializationInfo = nullptr;
+
+ VkPipelineShaderStageCreateInfo frag_shader_stage_info{};
+ frag_shader_stage_info.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ frag_shader_stage_info.pNext = nullptr;
+ frag_shader_stage_info.flags = 0;
+ frag_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
+ frag_shader_stage_info.module =
+ cg_core.vk_device_with_swapchain->frag2d_shader_module;
+ frag_shader_stage_info.pName = "main";
+ frag_shader_stage_info.pSpecializationInfo = nullptr;
+
+ VkPipelineShaderStageCreateInfo shader_stages[] = {
+ vert_shader_stage_info,
+ frag_shader_stage_info
+ };
+
+ VkVertexInputBindingDescription vertex_input_binding{};
+ vertex_input_binding.binding = 0;
+ vertex_input_binding.stride = sizeof(glm::vec2);
+ vertex_input_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+
+ std::array<VkVertexInputAttributeDescription, 1> vertex_attribute{};
+ // Texture coordinate.
+ vertex_attribute[0].location = 0;
+ vertex_attribute[0].binding = 0;
+ vertex_attribute[0].format = VK_FORMAT_R32G32_SFLOAT;
+ vertex_attribute[0].offset = 0;
+
+ VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
+ vertex_input_info.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vertex_input_info.pNext = nullptr;
+ vertex_input_info.flags = 0;
+ vertex_input_info.vertexBindingDescriptionCount = 1;
+ vertex_input_info.pVertexBindingDescriptions = &vertex_input_binding;
+ vertex_input_info.vertexAttributeDescriptionCount =
+ static_cast<uint32_t>(vertex_attribute.size());
+ vertex_input_info.pVertexAttributeDescriptions = vertex_attribute.data();
+
+ VkPipelineInputAssemblyStateCreateInfo input_assembly = {};
+ input_assembly.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ input_assembly.pNext = nullptr;
+ input_assembly.flags = 0;
+ input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
+ input_assembly.primitiveRestartEnable = VK_FALSE;
+
+ VkViewport viewport = {};
+ viewport.x = 0.0f;
+ viewport.y = 0.0f;
+ viewport.width = static_cast<float>(cg_core.screen_width);
+ viewport.height = static_cast<float>(cg_core.screen_height);
+ viewport.minDepth = 0.0f;
+ viewport.maxDepth = 1.0f;
+
+ VkRect2D scissor = {};
+ scissor.offset = {0, 0};
+ scissor.extent = {cg_core.screen_width, cg_core.screen_height};
+
+ VkPipelineViewportStateCreateInfo viewport_state = {};
+ viewport_state.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ viewport_state.pNext = nullptr;
+ viewport_state.flags = 0;
+ viewport_state.viewportCount = 1;
+ viewport_state.pViewports = &viewport;
+ viewport_state.scissorCount = 1;
+ viewport_state.pScissors = &scissor;
+
+ VkPipelineRasterizationStateCreateInfo rasterizer = {};
+ rasterizer.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ rasterizer.pNext = nullptr;
+ rasterizer.flags = 0;
+ rasterizer.depthClampEnable = VK_FALSE;
+ rasterizer.rasterizerDiscardEnable = VK_FALSE;
+ rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
+ rasterizer.cullMode = VK_CULL_MODE_NONE;
+ rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ rasterizer.depthBiasEnable = VK_FALSE;
+ rasterizer.depthBiasConstantFactor = 0.0f;
+ rasterizer.depthBiasClamp = 0.0f;
+ rasterizer.depthBiasSlopeFactor = 0.0f;
+ rasterizer.lineWidth = 1.0f;
+
+ VkPipelineMultisampleStateCreateInfo multisampling = {};
+ multisampling.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+ multisampling.sampleShadingEnable = VK_FALSE;
+ multisampling.minSampleShading = 1.0f;
+ multisampling.pSampleMask = nullptr;
+ multisampling.alphaToCoverageEnable = VK_FALSE;
+ multisampling.alphaToOneEnable = VK_FALSE;
+
+ VkPipelineColorBlendAttachmentState color_blend_attachment = {};
+ color_blend_attachment.blendEnable = VK_FALSE;
+ color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
+ color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
+ color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
+ color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
+ color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
+ color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
+ color_blend_attachment.colorWriteMask =
+ VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
+ VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+
+ VkPipelineColorBlendStateCreateInfo color_blending = {};
+ color_blending.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ color_blending.pNext = nullptr;
+ color_blending.flags = 0;
+ color_blending.logicOpEnable = VK_FALSE;
+ color_blending.logicOp = VK_LOGIC_OP_COPY;
+ color_blending.attachmentCount = 1;
+ color_blending.pAttachments = &color_blend_attachment;
+ color_blending.blendConstants[0] = 0.0f;
+ color_blending.blendConstants[1] = 0.0f;
+ color_blending.blendConstants[2] = 0.0f;
+ color_blending.blendConstants[3] = 0.0f;
+
+ VkDynamicState dynamic_states[] = {
+ VK_DYNAMIC_STATE_VIEWPORT,
+ VK_DYNAMIC_STATE_LINE_WIDTH
+ };
+
+ VkPipelineDynamicStateCreateInfo dynamic_state_info = {};
+ dynamic_state_info.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+ dynamic_state_info.dynamicStateCount = 2;
+ dynamic_state_info.pDynamicStates = dynamic_states;
+
+ VkGraphicsPipelineCreateInfo pipeline_info{};
+ pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+ pipeline_info.pNext = nullptr;
+ pipeline_info.flags = 0;
+ pipeline_info.stageCount = 2;
+ pipeline_info.pStages = shader_stages;
+ pipeline_info.pVertexInputState = &vertex_input_info;
+ pipeline_info.pInputAssemblyState = &input_assembly;
+ pipeline_info.pTessellationState = nullptr;
+ pipeline_info.pViewportState = &viewport_state;
+ pipeline_info.pRasterizationState = &rasterizer;
+ pipeline_info.pMultisampleState = &multisampling;
+ pipeline_info.pDepthStencilState = nullptr;
+ pipeline_info.pColorBlendState = &color_blending;
+ pipeline_info.pDynamicState = &dynamic_state_info;
+ pipeline_info.layout = self->pipeline_layout;
+ pipeline_info.renderPass = self->render_pass;
+ pipeline_info.subpass = 0;
+ pipeline_info.basePipelineHandle = VK_NULL_HANDLE;
+ pipeline_info.basePipelineIndex = -1;
+
+ if(vkCreateGraphicsPipelines(
+ cg_core.vk_device_with_swapchain->device, VK_NULL_HANDLE, 1,
+ &pipeline_info, nullptr, &self->graphic_pipeline) != VK_SUCCESS)
+ throw CommandError{"Failed to create graphics pipeline."};
+}
+
+void
+unload_pipeline(void *obj)
+{
+ auto self = static_cast<VK::GraphicsPipeline2D*>(obj);
+
+ vkDestroyPipeline(
+ cg_core.vk_device_with_swapchain->device, self->graphic_pipeline, nullptr);
+}
+
+const CommandChain loader{
+ {&load_descriptor_set_layout_image_sprites,
+ &unload_descriptor_set_layout_image_sprites},
+ {&load_pipeline_layout, &unload_pipeline_layout},
+ {&load_render_pass, &unload_render_pass},
+ {&load_framebuffer, &unload_framebuffer},
+ {&load_pipeline, &unload_pipeline}
+};
+
+}
+
+namespace VK
+{
+
+GraphicsPipeline2D::GraphicsPipeline2D():
+ sprites_to_draw{cg_core.vk_swapchain->images_count}
+{
+ loader.execute(this);
+}
+
+GraphicsPipeline2D::~GraphicsPipeline2D()
+{
+ loader.revert(this);
+}
+
+void
+GraphicsPipeline2D::draw(
+ const VkCommandBuffer draw_command_buffer, const size_t current_frame,
+ const size_t next_frame, const uint32_t image_index)
+{
+ // Load command
+ {
+ VkRenderPassBeginInfo render_pass_begin{};
+ render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+ render_pass_begin.pNext = nullptr;
+ render_pass_begin.renderPass = this->render_pass;
+ render_pass_begin.framebuffer = this->swapchain_framebuffers[image_index];
+ render_pass_begin.renderArea.offset = {0, 0};
+ render_pass_begin.renderArea.extent = {
+ cg_core.screen_width, cg_core.screen_height};
+ render_pass_begin.clearValueCount = 0;
+ render_pass_begin.pClearValues = nullptr;
+
+ vkCmdBeginRenderPass(
+ draw_command_buffer, &render_pass_begin,VK_SUBPASS_CONTENTS_INLINE);
+
+ VkViewport vk_viewport{};
+ vk_viewport.width = static_cast<float>(cg_core.screen_width);
+ vk_viewport.height = static_cast<float>(cg_core.screen_height);
+ vk_viewport.minDepth = 0.0f;
+ vk_viewport.maxDepth = 1.0f;
+ vkCmdSetViewport(draw_command_buffer, 0, 1, &vk_viewport);
+
+ VkRect2D vk_scissor{};
+ vk_scissor.extent.width = cg_core.screen_width;
+ vk_scissor.extent.height = cg_core.screen_height;
+ vk_scissor.offset.x = 0;
+ vk_scissor.offset.y = 0;
+ vkCmdSetScissor(draw_command_buffer, 0, 1, &vk_scissor);
+
+ // Draw sprites
+ for(auto& [sprite, positions]: this->sprites_to_draw[current_frame])
+ {
+ // Commands
+ {
+ VkDeviceSize offsets[]{0};
+
+ vkCmdBindDescriptorSets(
+ draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ this->pipeline_layout, 0, 1, &sprite->descriptor_sets[image_index],
+ 0, nullptr);
+ vkCmdBindPipeline(
+ draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ this->graphic_pipeline);
+ vkCmdBindVertexBuffers(
+ draw_command_buffer, 0, 1, &sprite->vertex_buffer->buffer, offsets);
+ vkCmdDraw(
+ draw_command_buffer, Sprite::vertex_count, positions.size(), 0, 0);
+ }
+
+ VK::UBOSpritePositions ubo_sprite_positions;
+ for(auto i{0}; i < positions.size(); i++)
+ ubo_sprite_positions.positions[i] = positions[i];
+ sprite->ub_sprite_positions[image_index].copy_data(
+ &ubo_sprite_positions);
+ }
+ }
+ vkCmdEndRenderPass(draw_command_buffer);
+
+ // Prepare for the next frame.
+ this->sprites_to_draw[next_frame].clear();
+}
+
+}
diff --git a/src/vk/graphics_pipeline_2d.hpp b/src/vk/graphics_pipeline_2d.hpp
new file mode 100644
index 0000000..f6a1b47
--- /dev/null
+++ b/src/vk/graphics_pipeline_2d.hpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_VK_GRAPHICS_PIPELINE_2D_H
+#define CANDY_GEAR_VK_GRAPHICS_PIPELINE_2D_H 1
+
+#include <vector>
+
+#include "core.hpp"
+#include "sprite.hpp"
+
+namespace VK
+{
+
+struct GraphicsPipeline2D
+{
+ VkDescriptorSetLayout descriptor_set_layout_sprites;
+ VkPipelineLayout pipeline_layout;
+
+ VkRenderPass render_pass;
+ std::vector<VkFramebuffer> swapchain_framebuffers;
+ VkPipeline graphic_pipeline;
+
+ std::vector<
+ std::unordered_map<std::shared_ptr<Sprite>, std::vector<glm::vec4>>>
+ sprites_to_draw;
+
+ GraphicsPipeline2D();
+ ~GraphicsPipeline2D();
+
+ void
+ draw(const VkCommandBuffer draw_command_buffer, const size_t current_frame,
+ const size_t next_frame, const uint32_t image_index);
+};
+
+}
+
+#endif /* CANDY_GEAR_VK_GRAPHICS_PIPELINE_2D_H */
diff --git a/src/vk/graphics_pipeline.cpp b/src/vk/graphics_pipeline_3d.cpp
index 3956a99..04a2adb 100644
--- a/src/vk/graphics_pipeline.cpp
+++ b/src/vk/graphics_pipeline_3d.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "graphics_pipeline.hpp"
+#include "graphics_pipeline_3d.hpp"
#include <array>
#include <stdexcept>
@@ -23,7 +23,7 @@
#include "core.hpp"
#include "image.hpp"
#include "uniform_buffer.hpp"
-#include "vertex.hpp"
+#include "vertex_3d.hpp"
namespace
{
@@ -31,7 +31,7 @@ namespace
void
load_descriptor_set_layout_model_instance(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{};
@@ -65,7 +65,7 @@ load_descriptor_set_layout_model_instance(void *obj)
void
unload_descriptor_set_layout_model_instance(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
vkDestroyDescriptorSetLayout(
cg_core.vk_device_with_swapchain->device,
@@ -75,7 +75,7 @@ unload_descriptor_set_layout_model_instance(void *obj)
void
load_descriptor_set_layout_world_view(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
std::array<VkDescriptorSetLayoutBinding, 2> set_layouts{};
set_layouts[0].binding = 0;
@@ -107,7 +107,7 @@ load_descriptor_set_layout_world_view(void *obj)
void
unload_descriptor_set_layout_world_view(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
vkDestroyDescriptorSetLayout(
cg_core.vk_device_with_swapchain->device,
@@ -117,7 +117,7 @@ unload_descriptor_set_layout_world_view(void *obj)
void
load_pipeline_layout(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
std::array<VkDescriptorSetLayout, 2> set_layouts{
self->descriptor_set_layout_world_view,
@@ -139,7 +139,7 @@ load_pipeline_layout(void *obj)
void
unload_pipeline_layout(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
vkDestroyPipelineLayout(
cg_core.vk_device_with_swapchain->device, self->pipeline_layout, nullptr);
@@ -148,7 +148,7 @@ unload_pipeline_layout(void *obj)
void
load_view_projection_uniform_buffer(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
try
{
@@ -166,7 +166,7 @@ load_view_projection_uniform_buffer(void *obj)
void
unload_view_projection_uniform_buffer(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
self->ub_view_projection.clear();
}
@@ -174,7 +174,7 @@ unload_view_projection_uniform_buffer(void *obj)
void
load_directional_light_uniform_buffer(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
try
{
@@ -192,7 +192,7 @@ load_directional_light_uniform_buffer(void *obj)
void
unload_directional_light_uniform_buffer(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
self->ub_directional_light.clear();
}
@@ -200,7 +200,7 @@ unload_directional_light_uniform_buffer(void *obj)
void
load_descriptor_pool(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
VkDescriptorPoolSize descriptor_pool_size{};
descriptor_pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
@@ -225,7 +225,7 @@ load_descriptor_pool(void *obj)
void
unload_descriptor_pool(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
vkDestroyDescriptorPool(
cg_core.vk_device_with_swapchain->device, self->descriptor_pool, nullptr);
@@ -234,7 +234,7 @@ unload_descriptor_pool(void *obj)
void
load_world_view_descriptor_sets(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
std::vector<VkDescriptorSetLayout> layouts(
cg_core.vk_swapchain->images_count,
@@ -257,7 +257,7 @@ load_world_view_descriptor_sets(void *obj)
void
load_resources_to_descriptor_sets(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
for(auto i{0}; i < self->ub_view_projection.size(); i++)
{
@@ -301,7 +301,7 @@ load_resources_to_descriptor_sets(void *obj)
void
load_depth_image(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
VkExtent3D extent3d{};
extent3d.width = cg_core.screen_width;
@@ -332,7 +332,7 @@ load_depth_image(void *obj)
void
unload_depth_image(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
vkDestroyImage(
cg_core.vk_device_with_swapchain->device, self->depth_image, nullptr);
@@ -344,7 +344,7 @@ unload_depth_image(void *obj)
void
load_depth_image_view(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
try
{
@@ -364,7 +364,7 @@ load_depth_image_view(void *obj)
void
unload_depth_image_view(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
vkDestroyImageView(
cg_core.vk_device_with_swapchain->device, self->depth_image_view, nullptr);
@@ -373,7 +373,7 @@ unload_depth_image_view(void *obj)
void
load_render_pass(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
std::array<VkAttachmentDescription, 2> attachments{};
// Color attachment.
@@ -385,7 +385,7 @@ load_render_pass(void *obj)
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
- attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
// Depth attachment.
attachments[1].flags = 0;
attachments[1].format = VK_FORMAT_D32_SFLOAT;
@@ -450,7 +450,7 @@ load_render_pass(void *obj)
void
unload_render_pass(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
vkDestroyRenderPass(
cg_core.vk_device_with_swapchain->device, self->render_pass, nullptr);
@@ -459,7 +459,7 @@ unload_render_pass(void *obj)
void
load_framebuffer(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
self->swapchain_framebuffers.resize(cg_core.vk_swapchain->images_count);
for (auto i{0}; i < cg_core.vk_swapchain->images_count; i++)
@@ -489,7 +489,7 @@ load_framebuffer(void *obj)
void
unload_framebuffer(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
for(auto framebuffer: self->swapchain_framebuffers)
vkDestroyFramebuffer(
@@ -499,7 +499,7 @@ unload_framebuffer(void *obj)
void
load_pipeline(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
VkPipelineShaderStageCreateInfo vert_shader_stage_info = {};
vert_shader_stage_info.sType =
@@ -508,7 +508,7 @@ load_pipeline(void *obj)
vert_shader_stage_info.flags = 0;
vert_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
vert_shader_stage_info.module =
- cg_core.vk_device_with_swapchain->vert_shader_module;
+ cg_core.vk_device_with_swapchain->vert3d_shader_module;
vert_shader_stage_info.pName = "main";
vert_shader_stage_info.pSpecializationInfo = nullptr;
@@ -519,7 +519,7 @@ load_pipeline(void *obj)
frag_shader_stage_info.flags = 0;
frag_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
frag_shader_stage_info.module =
- cg_core.vk_device_with_swapchain->frag_shader_module;
+ cg_core.vk_device_with_swapchain->frag3d_shader_module;
frag_shader_stage_info.pName = "main";
frag_shader_stage_info.pSpecializationInfo = nullptr;
@@ -530,7 +530,7 @@ load_pipeline(void *obj)
VkVertexInputBindingDescription vertex_input_binding{};
vertex_input_binding.binding = 0;
- vertex_input_binding.stride = sizeof(VK::Vertex);
+ vertex_input_binding.stride = sizeof(VK::Vertex3D);
vertex_input_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
std::array<VkVertexInputAttributeDescription, 3> vertex_attribute{};
@@ -538,17 +538,17 @@ load_pipeline(void *obj)
vertex_attribute[0].location = 0;
vertex_attribute[0].binding = 0;
vertex_attribute[0].format = VK_FORMAT_R32G32B32_SFLOAT;
- vertex_attribute[0].offset = offsetof(VK::Vertex, position);
+ vertex_attribute[0].offset = offsetof(VK::Vertex3D, position);
// Normal.
vertex_attribute[1].location = 1;
vertex_attribute[1].binding = 0;
vertex_attribute[1].format = VK_FORMAT_R32G32B32_SFLOAT;
- vertex_attribute[1].offset = offsetof(VK::Vertex, normal);
+ vertex_attribute[1].offset = offsetof(VK::Vertex3D, normal);
// Texture coordinate.
vertex_attribute[2].location = 2;
vertex_attribute[2].binding = 0;
vertex_attribute[2].format = VK_FORMAT_R32G32_SFLOAT;
- vertex_attribute[2].offset = offsetof(VK::Vertex, texture_coord);
+ vertex_attribute[2].offset = offsetof(VK::Vertex3D, texture_coord);
VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
vertex_input_info.sType =
@@ -697,122 +697,12 @@ load_pipeline(void *obj)
void
unload_pipeline(void *obj)
{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
+ auto self = static_cast<VK::GraphicsPipeline3D*>(obj);
vkDestroyPipeline(
cg_core.vk_device_with_swapchain->device, self->graphic_pipeline, nullptr);
}
-void
-load_frame_sync(void *obj)
-{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
-
- self->image_available_semaphores.resize(self->max_frames_in_flight);
- self->render_finished_semaphores.resize(self->max_frames_in_flight);
- self->in_flight_fences.resize(self->max_frames_in_flight);
-
- VkSemaphoreCreateInfo semaphore_info = {};
- semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
- semaphore_info.pNext = nullptr;
- semaphore_info.flags = 0;
-
- VkFenceCreateInfo fence_info = {};
- fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
- fence_info.pNext = nullptr;
- fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
-
- // FIXME: if this loop fails, it will not destroy the semaphores.
- for(auto i{0}; i < self->max_frames_in_flight; i++)
- {
- if(vkCreateSemaphore(
- cg_core.vk_device_with_swapchain->device, &semaphore_info,
- nullptr, &self->image_available_semaphores[i]) != VK_SUCCESS ||
- vkCreateSemaphore(
- cg_core.vk_device_with_swapchain->device, &semaphore_info,
- nullptr, &self->render_finished_semaphores[i]) != VK_SUCCESS ||
- vkCreateFence(cg_core.vk_device_with_swapchain->device, &fence_info,
- nullptr, &self->in_flight_fences[i]) != VK_SUCCESS)
- throw CommandError{"Failed to create semaphores."};
- }
-}
-
-void
-unload_frame_sync(void *obj)
-{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
-
- vkDeviceWaitIdle(cg_core.vk_device_with_swapchain->device);
-
- for(auto i{0}; i < self->max_frames_in_flight; i++)
- {
- vkDestroySemaphore(cg_core.vk_device_with_swapchain->device,
- self->render_finished_semaphores[i], nullptr);
- vkDestroySemaphore(cg_core.vk_device_with_swapchain->device,
- self->image_available_semaphores[i], nullptr);
- vkDestroyFence(cg_core.vk_device_with_swapchain->device,
- self->in_flight_fences[i], nullptr);
- }
-}
-
-void
-load_queue_family(void *obj)
-{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
-
- self->queue_family =
- cg_core.vk_device_with_swapchain->get_queue_family_with_presentation();
-}
-
-void
-load_command_pool(void *obj)
-{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
-
- VkCommandPoolCreateInfo create_info{};
- create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
- create_info.pNext = nullptr;
- create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
- create_info.queueFamilyIndex = self->queue_family->family_index;
-
- vkCreateCommandPool(
- self->queue_family->device->device, &create_info, nullptr,
- &self->command_pool);
-}
-
-void
-unload_command_pool(void *obj)
-{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
-
- vkWaitForFences(cg_core.vk_device_with_swapchain->device, 2,
- self->in_flight_fences.data(), VK_TRUE,
- std::numeric_limits<uint64_t>::max());
- vkDestroyCommandPool(
- self->queue_family->device->device, self->command_pool, nullptr);
-}
-
-void
-load_draw_command_buffer(void *obj)
-{
- auto self = static_cast<VK::GraphicsPipeline*>(obj);
-
- // FIXME: 3 is a magical number, triple buffering.
- self->draw_command_buffers.resize(3);
-
- VkCommandBufferAllocateInfo allocate_info{};
- allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
- allocate_info.pNext = nullptr;
- allocate_info.commandPool = self->command_pool;
- allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
- allocate_info.commandBufferCount = self->draw_command_buffers.size();
-
- if(vkAllocateCommandBuffers(
- self->queue_family->device->device, &allocate_info,
- self->draw_command_buffers.data()) != VK_SUCCESS)
- throw CommandError{"Vulkan draw command buffers could not be allocated."};
-}
-
const CommandChain loader{
{&load_descriptor_set_layout_model_instance,
&unload_descriptor_set_layout_model_instance},
@@ -831,11 +721,7 @@ const CommandChain loader{
{&load_depth_image_view, &unload_depth_image_view},
{&load_render_pass, &unload_render_pass},
{&load_framebuffer, &unload_framebuffer},
- {&load_pipeline, &unload_pipeline},
- {&load_frame_sync, &unload_frame_sync},
- {&load_queue_family, nullptr},
- {&load_command_pool, &unload_command_pool},
- {&load_draw_command_buffer, nullptr}
+ {&load_pipeline, &unload_pipeline}
};
}
@@ -843,8 +729,7 @@ const CommandChain loader{
namespace VK
{
-GraphicsPipeline::GraphicsPipeline():
- current_frame{0},
+GraphicsPipeline3D::GraphicsPipeline3D():
camera_position{std::make_shared<glm::vec3>(0.0f, 0.0f, 0.0f)},
camera_rotation{std::make_shared<glm::vec3>(0.0f, 0.0f, 0.0f)},
models_to_draw{cg_core.vk_swapchain->images_count}
@@ -852,41 +737,18 @@ GraphicsPipeline::GraphicsPipeline():
loader.execute(this);
}
-GraphicsPipeline::~GraphicsPipeline()
+GraphicsPipeline3D::~GraphicsPipeline3D()
{
loader.revert(this);
}
void
-GraphicsPipeline::draw()
+GraphicsPipeline3D::draw(
+ const VkCommandBuffer draw_command_buffer, const size_t current_frame,
+ const size_t next_frame, const uint32_t image_index)
{
- vkWaitForFences(cg_core.vk_device_with_swapchain->device, 1,
- &this->in_flight_fences[this->current_frame], VK_TRUE,
- std::numeric_limits<uint64_t>::max());
- vkResetFences(cg_core.vk_device_with_swapchain->device, 1,
- &this->in_flight_fences[this->current_frame]);
-
- uint32_t image_index;
- vkAcquireNextImageKHR(
- cg_core.vk_device_with_swapchain->device, cg_core.vk_swapchain->swapchain,
- std::numeric_limits<uint64_t>::max(),
- this->image_available_semaphores[this->current_frame],
- VK_NULL_HANDLE, &image_index);
-
- auto &draw_command_buffer = this->draw_command_buffers[this->current_frame];
- vkResetCommandBuffer(draw_command_buffer, 0);
-
// Load command.
{
- VkCommandBufferBeginInfo begin_info{};
- begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
- begin_info.flags = 0;
- begin_info.pInheritanceInfo = nullptr;
- if (vkBeginCommandBuffer(draw_command_buffer, &begin_info) != VK_SUCCESS)
- {
- throw std::runtime_error{"Failed to beggin draw command buffer."};
- }
-
// Dark gray blue.
std::array<VkClearValue, 2> clear_values{};
clear_values[0].color = {0.12f, 0.12f, 0.18f, 1.0f};
@@ -921,7 +783,7 @@ GraphicsPipeline::draw()
vkCmdSetScissor(draw_command_buffer, 0, 1, &vk_scissor);
// Draw models
- for(auto& [model, instances] : this->models_to_draw[this->current_frame])
+ for(auto& [model, instances] : this->models_to_draw[current_frame])
{
// Commands
{
@@ -962,15 +824,13 @@ GraphicsPipeline::draw()
instance_matrix = glm::rotate(
instance_matrix, instances[i].rotation.z, glm::vec3{0.0, 0.0, 1.0});
- ubo_model_instance.model[i] = instance_matrix;
+ ubo_model_instance.instances[i] = instance_matrix;
}
model->ub_model_instance[image_index].copy_data(&ubo_model_instance);
}
vkCmdEndRenderPass(draw_command_buffer);
- if(vkEndCommandBuffer(draw_command_buffer) != VK_SUCCESS)
- throw std::runtime_error{"Failed to end draw command buffer."};
}
// Update view projection uniform buffers
@@ -1013,52 +873,8 @@ GraphicsPipeline::draw()
this->ub_directional_light[image_index].copy_data(&ubo_directional_light);
}
- // Submit drawing command.
- {
- auto queue{this->queue_family->get_queue()};
-
- VkSemaphore wait_semaphores[]{
- this->image_available_semaphores[this->current_frame]};
- VkPipelineStageFlags wait_stages[] =
- {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
- VkSemaphore signal_semaphores[]{
- this->render_finished_semaphores[this->current_frame]};
-
- VkSubmitInfo submit_info{};
- submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
- submit_info.pNext = nullptr;
- submit_info.waitSemaphoreCount = 1;
- submit_info.pWaitSemaphores = wait_semaphores;
- submit_info.pWaitDstStageMask = wait_stages;
- submit_info.commandBufferCount = 1;
- submit_info.pCommandBuffers = &draw_command_buffer;
- submit_info.signalSemaphoreCount = 1;
- submit_info.pSignalSemaphores = signal_semaphores;
-
- if(vkQueueSubmit(
- queue.queue, 1, &submit_info,
- this->in_flight_fences[this->current_frame]) != VK_SUCCESS)
- throw std::runtime_error{"Failed to submit draw command buffer."};
-
- VkSwapchainKHR swap_chains[]{cg_core.vk_swapchain->swapchain};
-
- VkPresentInfoKHR present_info{};
- present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
- present_info.pNext = nullptr;
- present_info.waitSemaphoreCount = 1;
- present_info.pWaitSemaphores = signal_semaphores;
- present_info.swapchainCount = 1;
- present_info.pSwapchains = swap_chains;
- present_info.pImageIndices = &image_index;
- present_info.pResults = nullptr;
-
- vkQueuePresentKHR(queue.queue, &present_info);
-
- // Prepare for the next frame.
- this->current_frame =
- (this->current_frame + 1) % this->max_frames_in_flight;
- this->models_to_draw[this->current_frame].clear();
- }
+ // Prepare for the next frame.
+ this->models_to_draw[next_frame].clear();
}
}
diff --git a/src/vk/graphics_pipeline.hpp b/src/vk/graphics_pipeline_3d.hpp
index b4a282a..2efbc00 100644
--- a/src/vk/graphics_pipeline.hpp
+++ b/src/vk/graphics_pipeline_3d.hpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef CANDY_GEAR_VK_GRAPHICS_PIPELINE_H
-#define CANDY_GEAR_VK_GRAPHICS_PIPELINE_H 1
+#ifndef CANDY_GEAR_VK_GRAPHICS_PIPELINE_3D_H
+#define CANDY_GEAR_VK_GRAPHICS_PIPELINE_3D_H 1
#include <memory>
#include <unordered_map>
@@ -30,7 +30,7 @@
namespace VK
{
-struct GraphicsPipeline
+struct GraphicsPipeline3D
{
VkDescriptorSetLayout descriptor_set_layout_model_instance;
VkDescriptorSetLayout descriptor_set_layout_world_view;
@@ -52,30 +52,20 @@ struct GraphicsPipeline
std::vector<VkFramebuffer> swapchain_framebuffers;
VkPipeline graphic_pipeline;
- QueueFamily *queue_family;
- VkCommandPool command_pool;
- std::vector<VkCommandBuffer> draw_command_buffers;
-
- // Buffering control.
- static const int max_frames_in_flight{2};
- size_t current_frame;
- std::vector<VkSemaphore> image_available_semaphores;
- std::vector<VkSemaphore> render_finished_semaphores;
- std::vector<VkFence> in_flight_fences;
-
std::shared_ptr<glm::vec3> camera_position;
std::shared_ptr<glm::vec3> camera_rotation;
std::vector<std::unordered_map<
std::shared_ptr<Model>, std::vector<ModelInstance>>>
models_to_draw;
- GraphicsPipeline();
- ~GraphicsPipeline();
+ GraphicsPipeline3D();
+ ~GraphicsPipeline3D();
void
- draw();
+ draw(const VkCommandBuffer draw_command_buffer, const size_t current_frame,
+ const size_t next_frame, const uint32_t image_index);
};
}
-#endif /* CANDY_GEAR_VK_GRAPHICS_PIPELINE_H */
+#endif /* CANDY_GEAR_VK_GRAPHICS_PIPELINE_3D_H */
diff --git a/src/vk/model.cpp b/src/vk/model.cpp
index 5cf96b6..240ad13 100644
--- a/src/vk/model.cpp
+++ b/src/vk/model.cpp
@@ -21,7 +21,7 @@
#include "../command.hpp"
#include "../core.hpp"
-#include "vertex.hpp"
+#include "vertex_3d.hpp"
namespace
{
@@ -82,12 +82,11 @@ load_mesh(void *obj)
self->model->queue_family =
cg_core.vk_device_with_swapchain->get_queue_family_with_graphics();
- VK::Queue transfer_queue{self->model->queue_family->get_queue()};
// Load vertexes.
{
auto vertex_count{read_uint32_from_file(input_file)};
- std::vector<VK::Vertex> vertexes{vertex_count};
+ std::vector<VK::Vertex3D> vertexes{vertex_count};
for(auto i{0}; i < vertex_count; i++)
{
@@ -133,7 +132,7 @@ unload_mesh(void *obj)
delete self->model->source_vertex_buffer;
}
-auto
+void
load_uniform_buffers(void *obj)
{
auto self = static_cast<ModelBuilder*>(obj);
@@ -196,7 +195,7 @@ load_descriptor_sets(void *obj)
std::vector<VkDescriptorSetLayout> layouts(
cg_core.vk_swapchain->images_count,
- cg_core.vk_graphics_pipeline->descriptor_set_layout_model_instance);
+ cg_core.vk_graphics_pipeline_3d->descriptor_set_layout_model_instance);
VkDescriptorSetAllocateInfo alloc_info{};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
diff --git a/src/vk/renderer.cpp b/src/vk/renderer.cpp
new file mode 100644
index 0000000..33a4bf7
--- /dev/null
+++ b/src/vk/renderer.cpp
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "renderer.hpp"
+
+#include "../core.hpp"
+
+namespace
+{
+
+void
+load_frame_sync(void *obj)
+{
+ auto self = static_cast<VK::Renderer*>(obj);
+
+ self->image_available_semaphores.resize(self->max_frames_in_flight);
+ self->render_finished_semaphores.resize(self->max_frames_in_flight);
+ self->in_flight_fences.resize(self->max_frames_in_flight);
+
+ VkSemaphoreCreateInfo semaphore_info = {};
+ semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ semaphore_info.pNext = nullptr;
+ semaphore_info.flags = 0;
+
+ VkFenceCreateInfo fence_info = {};
+ fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+ fence_info.pNext = nullptr;
+ fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
+
+ // FIXME: if this loop fails, it will not destroy the semaphores.
+ for(auto i{0}; i < self->max_frames_in_flight; i++)
+ {
+ if(vkCreateSemaphore(
+ cg_core.vk_device_with_swapchain->device, &semaphore_info,
+ nullptr, &self->image_available_semaphores[i]) != VK_SUCCESS ||
+ vkCreateSemaphore(
+ cg_core.vk_device_with_swapchain->device, &semaphore_info,
+ nullptr, &self->render_finished_semaphores[i]) != VK_SUCCESS ||
+ vkCreateFence(cg_core.vk_device_with_swapchain->device, &fence_info,
+ nullptr, &self->in_flight_fences[i]) != VK_SUCCESS)
+ throw CommandError{"Failed to create semaphores."};
+ }
+}
+
+void
+unload_frame_sync(void *obj)
+{
+ auto self = static_cast<VK::Renderer*>(obj);
+
+ vkDeviceWaitIdle(cg_core.vk_device_with_swapchain->device);
+
+ for(auto i{0}; i < self->max_frames_in_flight; i++)
+ {
+ vkDestroySemaphore(cg_core.vk_device_with_swapchain->device,
+ self->render_finished_semaphores[i], nullptr);
+ vkDestroySemaphore(cg_core.vk_device_with_swapchain->device,
+ self->image_available_semaphores[i], nullptr);
+ vkDestroyFence(cg_core.vk_device_with_swapchain->device,
+ self->in_flight_fences[i], nullptr);
+ }
+}
+
+void
+load_queue_family(void *obj)
+{
+ auto self = static_cast<VK::Renderer*>(obj);
+
+ self->queue_family =
+ cg_core.vk_device_with_swapchain->get_queue_family_with_presentation();
+}
+
+void
+load_command_pool(void *obj)
+{
+ auto self = static_cast<VK::Renderer*>(obj);
+
+ VkCommandPoolCreateInfo create_info{};
+ create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+ create_info.pNext = nullptr;
+ create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+ create_info.queueFamilyIndex = self->queue_family->family_index;
+
+ vkCreateCommandPool(
+ self->queue_family->device->device, &create_info, nullptr,
+ &self->command_pool);
+}
+
+void
+unload_command_pool(void *obj)
+{
+ auto self = static_cast<VK::Renderer*>(obj);
+
+ vkWaitForFences(cg_core.vk_device_with_swapchain->device, 2,
+ self->in_flight_fences.data(), VK_TRUE,
+ std::numeric_limits<uint64_t>::max());
+ vkDestroyCommandPool(
+ self->queue_family->device->device, self->command_pool, nullptr);
+}
+
+void
+load_draw_command_buffer(void *obj)
+{
+ auto self = static_cast<VK::Renderer*>(obj);
+
+ // FIXME: 3 is a magical number, triple buffering.
+ self->draw_command_buffers.resize(3);
+
+ VkCommandBufferAllocateInfo allocate_info{};
+ allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ allocate_info.pNext = nullptr;
+ allocate_info.commandPool = self->command_pool;
+ allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ allocate_info.commandBufferCount = self->draw_command_buffers.size();
+
+ if(vkAllocateCommandBuffers(
+ self->queue_family->device->device, &allocate_info,
+ self->draw_command_buffers.data()) != VK_SUCCESS)
+ throw CommandError{"Vulkan draw command buffers could not be allocated."};
+}
+
+const CommandChain loader{
+ {&load_frame_sync, &unload_frame_sync},
+ {&load_queue_family, nullptr},
+ {&load_command_pool, &unload_command_pool},
+ {&load_draw_command_buffer, nullptr}
+};
+
+}
+
+namespace VK
+{
+
+Renderer::Renderer():
+ current_frame{0}
+{
+ loader.execute(this);
+}
+
+Renderer::~Renderer()
+{
+ loader.revert(this);
+}
+
+void
+Renderer::draw()
+{
+ vkWaitForFences(cg_core.vk_device_with_swapchain->device, 1,
+ &this->in_flight_fences[this->current_frame], VK_TRUE,
+ std::numeric_limits<uint64_t>::max());
+ vkResetFences(cg_core.vk_device_with_swapchain->device, 1,
+ &this->in_flight_fences[this->current_frame]);
+
+ uint32_t image_index;
+ vkAcquireNextImageKHR(
+ cg_core.vk_device_with_swapchain->device, cg_core.vk_swapchain->swapchain,
+ std::numeric_limits<uint64_t>::max(),
+ this->image_available_semaphores[this->current_frame],
+ VK_NULL_HANDLE, &image_index);
+
+ VkCommandBuffer draw_command_buffer =
+ this->draw_command_buffers[this->current_frame];
+ vkResetCommandBuffer(draw_command_buffer, 0);
+
+ auto next_frame = this->current_frame + 1;
+ if(next_frame == this->max_frames_in_flight) next_frame = 0;
+
+ // Begin command buffer.
+ {
+ VkCommandBufferBeginInfo begin_info{};
+ begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ begin_info.flags = 0;
+ begin_info.pInheritanceInfo = nullptr;
+ if (vkBeginCommandBuffer(draw_command_buffer, &begin_info) != VK_SUCCESS)
+ throw std::runtime_error{"Failed to beggin draw command buffer."};
+ }
+
+ cg_core.vk_graphics_pipeline_3d->draw(
+ draw_command_buffer, current_frame, next_frame, image_index);
+ cg_core.vk_graphics_pipeline_2d->draw(
+ draw_command_buffer, current_frame, next_frame, image_index);
+
+ // End command buffer.
+ if(vkEndCommandBuffer(draw_command_buffer) != VK_SUCCESS)
+ throw std::runtime_error{"Failed to end draw command buffer."};
+
+ // Submit drawing command.
+ {
+ auto queue{this->queue_family->get_queue()};
+
+ VkSemaphore wait_semaphores[]{
+ this->image_available_semaphores[this->current_frame]};
+ VkPipelineStageFlags wait_stages[] =
+ {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
+ VkSemaphore signal_semaphores[]{
+ this->render_finished_semaphores[this->current_frame]};
+
+ VkSubmitInfo submit_info{};
+ submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submit_info.pNext = nullptr;
+ submit_info.waitSemaphoreCount = 1;
+ submit_info.pWaitSemaphores = wait_semaphores;
+ submit_info.pWaitDstStageMask = wait_stages;
+ submit_info.commandBufferCount = 1;
+ submit_info.pCommandBuffers = &draw_command_buffer;
+ submit_info.signalSemaphoreCount = 1;
+ submit_info.pSignalSemaphores = signal_semaphores;
+
+ if(vkQueueSubmit(
+ queue.queue, 1, &submit_info,
+ this->in_flight_fences[this->current_frame]) != VK_SUCCESS)
+ throw std::runtime_error{"Failed to submit draw command buffer."};
+
+ VkSwapchainKHR swap_chains[]{cg_core.vk_swapchain->swapchain};
+
+ VkPresentInfoKHR present_info{};
+ present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+ present_info.pNext = nullptr;
+ present_info.waitSemaphoreCount = 1;
+ present_info.pWaitSemaphores = signal_semaphores;
+ present_info.swapchainCount = 1;
+ present_info.pSwapchains = swap_chains;
+ present_info.pImageIndices = &image_index;
+ present_info.pResults = nullptr;
+
+ vkQueuePresentKHR(queue.queue, &present_info);
+ }
+
+ // Prepare for the next frame.
+ this->current_frame = next_frame;
+}
+
+}
diff --git a/src/vk/renderer.hpp b/src/vk/renderer.hpp
new file mode 100644
index 0000000..4b6c91d
--- /dev/null
+++ b/src/vk/renderer.hpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_VK_RENDERER_H
+#define CANDY_GEAR_VK_RENDERER_H 1
+
+#include <vector>
+
+#include "core.hpp"
+#include "queue_family.hpp"
+
+namespace VK
+{
+
+struct Renderer
+{
+ static const int max_frames_in_flight{2};
+ size_t current_frame;
+ std::vector<VkSemaphore> image_available_semaphores;
+ std::vector<VkSemaphore> render_finished_semaphores;
+ std::vector<VkFence> in_flight_fences;
+
+ QueueFamily *queue_family;
+ VkCommandPool command_pool;
+ std::vector<VkCommandBuffer> draw_command_buffers;
+
+ Renderer();
+ ~Renderer();
+
+ void
+ draw();
+};
+
+}
+
+#endif /* CANDY_GEAR_VK_RENDERER_H */
diff --git a/src/vk/sprite.cpp b/src/vk/sprite.cpp
new file mode 100644
index 0000000..e00fcbe
--- /dev/null
+++ b/src/vk/sprite.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sprite.hpp"
+
+#include <array>
+
+#include "../core.hpp"
+#include "sprite.hpp"
+
+namespace
+{
+
+struct SpriteBuilder
+{
+ VK::Sprite *sprite;
+ glm::vec4 rect;
+
+ SpriteBuilder(VK::Sprite *sprite, glm::vec4 rect);
+};
+
+SpriteBuilder::SpriteBuilder(VK::Sprite *sprite, glm::vec4 rect):
+ sprite{sprite},
+ rect{rect}
+{
+}
+
+void
+load_mesh(void *obj)
+{
+ auto self = static_cast<SpriteBuilder*>(obj);
+
+ self->sprite->queue_family =
+ cg_core.vk_device_with_swapchain->get_queue_family_with_graphics();
+
+ glm::vec2 rect[VK::Sprite::vertex_count]{
+ glm::vec2{self->rect.x, self->rect.y},
+ glm::vec2{self->rect.x, self->rect.w},
+ glm::vec2{self->rect.z, self->rect.y},
+ glm::vec2{self->rect.z, self->rect.w}
+ };
+
+ void *vertexes_data{&rect};
+ static const size_t vertexes_size =
+ sizeof(glm::vec2) * VK::Sprite::vertex_count;
+ self->sprite->source_buffer = new VK::SourceBuffer{
+ self->sprite->queue_family->device, vertexes_data, vertexes_size};
+ self->sprite->vertex_buffer = new VK::DestinationBuffer{
+ self->sprite->queue_family, self->sprite->source_buffer,
+ VK_BUFFER_USAGE_VERTEX_BUFFER_BIT};
+}
+
+void
+unload_mesh(void *obj)
+{
+ auto self = static_cast<SpriteBuilder*>(obj);
+
+ delete self->sprite->vertex_buffer;
+ delete self->sprite->source_buffer;
+}
+
+void
+load_uniform_buffers(void *obj)
+{
+ auto self = static_cast<SpriteBuilder*>(obj);
+
+ self->sprite->ub_sprite_positions.reserve(
+ cg_core.vk_swapchain->images_count);
+ for(auto i{0}; i < cg_core.vk_swapchain->images_count; i++)
+ self->sprite->ub_sprite_positions.emplace_back(
+ cg_core.vk_device_with_swapchain, sizeof(VK::UBOSpritePositions));
+}
+
+void
+unload_uniform_buffers(void *obj)
+{
+ auto self = static_cast<SpriteBuilder*>(obj);
+
+ self->sprite->ub_sprite_positions.clear();
+}
+
+void
+load_descriptor_set_pool(void *obj)
+{
+ auto self = static_cast<SpriteBuilder*>(obj);
+
+ std::array<VkDescriptorPoolSize, 2> descriptor_pool_sizes{};
+ descriptor_pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ descriptor_pool_sizes[0].descriptorCount =
+ self->sprite->ub_sprite_positions.size();
+ descriptor_pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ descriptor_pool_sizes[1].descriptorCount =
+ self->sprite->ub_sprite_positions.size();
+
+ VkDescriptorPoolCreateInfo pool_info{};
+ pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+ pool_info.pNext = nullptr;
+ pool_info.flags = 0;
+ pool_info.maxSets = self->sprite->ub_sprite_positions.size();
+ pool_info.poolSizeCount = descriptor_pool_sizes.size();
+ pool_info.pPoolSizes = descriptor_pool_sizes.data();
+
+ if(vkCreateDescriptorPool(
+ self->sprite->queue_family->device->device, &pool_info, nullptr,
+ &self->sprite->descriptor_pool) != VK_SUCCESS)
+ throw CommandError{"Failed to create a Vulkan descriptor pool."};
+
+}
+
+void
+unload_descriptor_set_pool(void *obj)
+{
+ auto self = static_cast<SpriteBuilder*>(obj);
+
+ vkDestroyDescriptorPool(
+ self->sprite->queue_family->device->device, self->sprite->descriptor_pool,
+ nullptr);
+}
+
+void
+load_descriptor_sets(void *obj)
+{
+ auto self = static_cast<SpriteBuilder*>(obj);
+
+ std::vector<VkDescriptorSetLayout> layouts(
+ cg_core.vk_swapchain->images_count,
+ cg_core.vk_graphics_pipeline_2d->descriptor_set_layout_sprites);
+
+ VkDescriptorSetAllocateInfo alloc_info{};
+ alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ alloc_info.descriptorPool = self->sprite->descriptor_pool;
+ alloc_info.descriptorSetCount = layouts.size();
+ alloc_info.pSetLayouts = layouts.data();
+
+ self->sprite->descriptor_sets.resize(layouts.size());
+ if(vkAllocateDescriptorSets(
+ self->sprite->queue_family->device->device, &alloc_info,
+ self->sprite->descriptor_sets.data()) != VK_SUCCESS)
+ CommandError{"Failed to create Vulkan descriptor set."};
+}
+
+void
+load_buffers_to_descriptor_sets(void *obj)
+{
+ auto self = static_cast<SpriteBuilder*>(obj);
+
+ for(auto i{0}; i < self->sprite->ub_sprite_positions.size(); i++)
+ {
+ VkDescriptorBufferInfo buffer_info{};
+ buffer_info.buffer = self->sprite->ub_sprite_positions[i].buffer;
+ buffer_info.offset = 0;
+ buffer_info.range = sizeof(VK::UBOSpritePositions);
+
+ VkDescriptorImageInfo image_info{};
+ image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ image_info.imageView = self->sprite->texture->view;
+ image_info.sampler = self->sprite->texture->sampler;
+
+ std::array<VkWriteDescriptorSet, 2> write_descriptors{};
+ write_descriptors[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write_descriptors[0].dstSet = self->sprite->descriptor_sets[i];
+ write_descriptors[0].dstBinding = 0;
+ write_descriptors[0].dstArrayElement = 0;
+ write_descriptors[0].descriptorCount = 1;
+ write_descriptors[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ write_descriptors[0].pBufferInfo = &buffer_info;
+ write_descriptors[0].pImageInfo = nullptr;
+ write_descriptors[0].pTexelBufferView = nullptr;
+
+ write_descriptors[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write_descriptors[1].dstSet = self->sprite->descriptor_sets[i];
+ write_descriptors[1].dstBinding = 1;
+ write_descriptors[1].dstArrayElement = 0;
+ write_descriptors[1].descriptorCount = 1;
+ write_descriptors[1].descriptorType =
+ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ write_descriptors[1].pBufferInfo = nullptr;
+ write_descriptors[1].pImageInfo = &image_info;
+ write_descriptors[1].pTexelBufferView = nullptr;
+
+ vkUpdateDescriptorSets(
+ cg_core.vk_device_with_swapchain->device, write_descriptors.size(),
+ write_descriptors.data(), 0, nullptr);
+ }
+}
+
+static const CommandChain loader{
+ {&load_mesh, &unload_mesh},
+ {&load_uniform_buffers, &unload_uniform_buffers},
+ {&load_descriptor_set_pool, &unload_descriptor_set_pool},
+ {&load_descriptor_sets, nullptr},
+ {&load_buffers_to_descriptor_sets, nullptr},
+};
+
+}
+
+namespace VK
+{
+
+Sprite::Sprite(std::shared_ptr<Texture> texture, glm::vec4 rect):
+ texture{texture}
+{
+ SpriteBuilder sprite_builder(this, rect);
+ loader.execute(&sprite_builder);
+}
+
+Sprite::~Sprite()
+{
+ SpriteBuilder sprite_builder(this, glm::vec4());
+ loader.revert(&sprite_builder);
+}
+
+}
diff --git a/src/vk/sprite.hpp b/src/vk/sprite.hpp
new file mode 100644
index 0000000..b0c0183
--- /dev/null
+++ b/src/vk/sprite.hpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_VK_SPRITE_H
+#define CANDY_GEAR_VK_SPRITE_H 1
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+#include "core.hpp"
+#include "destination_buffer.hpp"
+#include "queue_family.hpp"
+#include "uniform_buffer.hpp"
+#include "texture.hpp"
+
+namespace VK
+{
+
+struct Sprite
+{
+ static const uint32_t vertex_count{4};
+
+ QueueFamily *queue_family;
+
+ SourceBuffer *source_buffer;
+ DestinationBuffer *vertex_buffer;
+
+ std::vector<UniformBuffer> ub_sprite_positions;
+
+ VkDescriptorPool descriptor_pool;
+ std::vector<VkDescriptorSet> descriptor_sets;
+
+ std::shared_ptr<Texture> texture;
+
+ Sprite(std::shared_ptr<Texture> texture, glm::vec4 rect);
+ ~Sprite();
+};
+
+}
+
+#endif /* CANDY_GEAR_VK_SPRITE_H */
diff --git a/src/vk/uniform_buffer.hpp b/src/vk/uniform_buffer.hpp
index 14deead..50759b8 100644
--- a/src/vk/uniform_buffer.hpp
+++ b/src/vk/uniform_buffer.hpp
@@ -26,9 +26,14 @@
namespace VK
{
+struct UBOSpritePositions
+{
+ glm::vec4 positions[128];
+};
+
struct UBOModelInstance
{
- glm::mat4 model[128];
+ glm::mat4 instances[128];
};
struct UBOViewProjection
diff --git a/src/vk/vertex.hpp b/src/vk/vertex_3d.hpp
index 07332a8..8bb8675 100644
--- a/src/vk/vertex.hpp
+++ b/src/vk/vertex_3d.hpp
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-#ifndef CANDY_GEAR_VK_VERTEX_H
-#define CANDY_GEAR_VK_VERTEX_H 1
+#ifndef CANDY_GEAR_VK_VERTEX_3D_H
+#define CANDY_GEAR_VK_VERTEX_3D_H 1
#include "core.hpp"
namespace VK
{
-struct Vertex
+struct Vertex3D
{
glm::vec3 position;
glm::vec3 normal;
@@ -31,4 +31,4 @@ struct Vertex
}
-#endif /* CANDY_GEAR_VK_VERTEX_H */
+#endif /* CANDY_GEAR_VK_VERTEX_3D_H */
diff --git a/test/src/main.rb b/test/src/main.rb
index 2c8bdcc..6ac0dde 100644
--- a/test/src/main.rb
+++ b/test/src/main.rb
@@ -18,6 +18,7 @@ TRANSLATION_SPEED = 0.5;
def init()
$texture = CandyGear::Texture.from_image("textures/color_texture.png");
+ $sprite = CandyGear::Sprite.new($texture, 0, 0, 1.0, 1.0);
$model = CandyGear::Model.new("models/cube.cgmodel", $texture);
$instances = [
@@ -67,6 +68,7 @@ end
def quit() = CandyGear.quit();
def tick()
+ $sprite.draw(-1.0, -1.0, -0.70, -0.5);
$instances_rotation.rotate(0.0, BOX_ROTATION_SPEED);
$instances.each do |i|
$model.draw(i, $instances_rotation);