From f1f1576543bb4e0f3b9bc4cd0ba4a12a70546c3c Mon Sep 17 00:00:00 2001 From: Frederico Linhares Date: Tue, 30 Aug 2022 17:45:40 -0300 Subject: feat Create 2d graphics pipeline --- Rakefile | 16 +- glsl/shader.frag | 38 -- glsl/shader.vert | 38 -- glsl/shader_2d.frag.glsl | 30 ++ glsl/shader_2d.vert.glsl | 53 ++ glsl/shader_3d.frag.glsl | 54 ++ glsl/shader_3d.vert.glsl | 54 ++ src/candy_gear.cpp | 4 +- src/core.cpp | 52 +- src/core.hpp | 8 +- src/main.cpp | 4 +- src/model.cpp | 5 +- src/model.hpp | 3 - src/rotation3d.hpp | 6 +- src/sprite.cpp | 81 +++ src/sprite.hpp | 25 + src/vector3d.hpp | 6 +- src/vk/device.cpp | 18 +- src/vk/device.hpp | 7 +- src/vk/graphics_pipeline.cpp | 1064 --------------------------------------- src/vk/graphics_pipeline.hpp | 81 --- src/vk/graphics_pipeline_2d.cpp | 488 ++++++++++++++++++ src/vk/graphics_pipeline_2d.hpp | 51 ++ src/vk/graphics_pipeline_3d.cpp | 880 ++++++++++++++++++++++++++++++++ src/vk/graphics_pipeline_3d.hpp | 71 +++ src/vk/model.cpp | 9 +- src/vk/renderer.cpp | 245 +++++++++ src/vk/renderer.hpp | 49 ++ src/vk/sprite.cpp | 226 +++++++++ src/vk/sprite.hpp | 55 ++ src/vk/uniform_buffer.hpp | 7 +- src/vk/vertex.hpp | 34 -- src/vk/vertex_3d.hpp | 34 ++ test/src/main.rb | 2 + 34 files changed, 2498 insertions(+), 1300 deletions(-) delete mode 100644 glsl/shader.frag delete mode 100644 glsl/shader.vert create mode 100644 glsl/shader_2d.frag.glsl create mode 100644 glsl/shader_2d.vert.glsl create mode 100644 glsl/shader_3d.frag.glsl create mode 100644 glsl/shader_3d.vert.glsl create mode 100644 src/sprite.cpp create mode 100644 src/sprite.hpp delete mode 100644 src/vk/graphics_pipeline.cpp delete mode 100644 src/vk/graphics_pipeline.hpp create mode 100644 src/vk/graphics_pipeline_2d.cpp create mode 100644 src/vk/graphics_pipeline_2d.hpp create mode 100644 src/vk/graphics_pipeline_3d.cpp create mode 100644 src/vk/graphics_pipeline_3d.hpp create mode 100644 src/vk/renderer.cpp create mode 100644 src/vk/renderer.hpp create mode 100644 src/vk/sprite.cpp create mode 100644 src/vk/sprite.hpp delete mode 100644 src/vk/vertex.hpp create mode 100644 src/vk/vertex_3d.hpp 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.frag b/glsl/shader.frag deleted file mode 100644 index 8ecf883..0000000 --- a/glsl/shader.frag +++ /dev/null @@ -1,38 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -struct DataTransferObject -{ - vec4 frag_color; - vec2 frag_texture_coord; - vec3 normal; -}; - -layout(location = 0) in DataTransferObject in_dto; - -layout(location = 0) out vec4 out_color; - -layout(set = 0, binding = 1) uniform UBODirectionalLight -{ - vec3 direction; - vec4 color; -} ubo_directional_light; - -layout(set = 1, binding = 1) uniform sampler2D texture_sampler; - -void -main() -{ - float diffuse_factor = - max(dot(in_dto.normal, -ubo_directional_light.direction), 0.0); - - vec4 diff_samp = texture(texture_sampler, in_dto.frag_texture_coord); - vec4 ambient = vec4(vec3(in_dto.frag_color), diff_samp.a); - vec4 diffuse = - vec4(vec3(ubo_directional_light.color * diffuse_factor), diff_samp.a); - - diffuse *= diff_samp; - ambient *= diff_samp; - - out_color = (ambient + diffuse); -} 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_3d.frag.glsl b/glsl/shader_3d.frag.glsl new file mode 100644 index 0000000..5c4b857 --- /dev/null +++ b/glsl/shader_3d.frag.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 + +struct DataTransferObject +{ + vec4 frag_color; + vec2 frag_texture_coord; + vec3 normal; +}; + +layout(location = 0) in DataTransferObject in_dto; + +layout(location = 0) out vec4 out_color; + +layout(set = 0, binding = 1) uniform UBODirectionalLight +{ + vec3 direction; + vec4 color; +} ubo_directional_light; + +layout(set = 1, binding = 1) uniform sampler2D texture_sampler; + +void +main() +{ + float diffuse_factor = + max(dot(in_dto.normal, -ubo_directional_light.direction), 0.0); + + vec4 diff_samp = texture(texture_sampler, in_dto.frag_texture_coord); + vec4 ambient = vec4(vec3(in_dto.frag_color), diff_samp.a); + vec4 diffuse = + vec4(vec3(ubo_directional_light.color * diffuse_factor), diff_samp.a); + + diffuse *= diff_samp; + ambient *= diff_samp; + + out_color = (ambient + diffuse); +} 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 *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 *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_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 - #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*>(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 *texture; + std::shared_ptr *ptr; + + mrb_get_args(mrb, "dffff", &texture, &cg_texture_type, &x, &y, &w, &h); + ptr = (std::shared_ptr*)DATA_PTR(self); + if(ptr) mrb_free(mrb, ptr); + ptr = (std::shared_ptr*)mrb_malloc( + mrb, sizeof(std::shared_ptr)); + + glm::vec4 rect{x, y, w, h}; + new(ptr)std::shared_ptr( + std::make_shared(*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*)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( @@ -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.cpp b/src/vk/graphics_pipeline.cpp deleted file mode 100644 index 3956a99..0000000 --- a/src/vk/graphics_pipeline.cpp +++ /dev/null @@ -1,1064 +0,0 @@ -/* - * 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.hpp" - -#include -#include - -#include "../core.hpp" -#include "core.hpp" -#include "image.hpp" -#include "uniform_buffer.hpp" -#include "vertex.hpp" - -namespace -{ - -void -load_descriptor_set_layout_model_instance(void *obj) -{ - auto self = static_cast(obj); - - std::array 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(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_model_instance) != VK_SUCCESS) - throw CommandError{ - "Failed to create Vulkan descriptor set layout for model instance."}; -} - -void -unload_descriptor_set_layout_model_instance(void *obj) -{ - auto self = static_cast(obj); - - vkDestroyDescriptorSetLayout( - cg_core.vk_device_with_swapchain->device, - self->descriptor_set_layout_model_instance, nullptr); -} - -void -load_descriptor_set_layout_world_view(void *obj) -{ - auto self = static_cast(obj); - - std::array set_layouts{}; - set_layouts[0].binding = 0; - set_layouts[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - set_layouts[0].descriptorCount = 1; - set_layouts[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - set_layouts[0].pImmutableSamplers = nullptr; - - set_layouts[1].binding = 1; - set_layouts[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - set_layouts[1].descriptorCount = 1; - set_layouts[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - set_layouts[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 = set_layouts.size(); - layout_info.pBindings = set_layouts.data(); - - if(vkCreateDescriptorSetLayout( - cg_core.vk_device_with_swapchain->device, &layout_info, nullptr, - &self->descriptor_set_layout_world_view) != VK_SUCCESS) - throw CommandError{ - "Failed to create Vulkan descriptor set layout for world view."}; -} - -void -unload_descriptor_set_layout_world_view(void *obj) -{ - auto self = static_cast(obj); - - vkDestroyDescriptorSetLayout( - cg_core.vk_device_with_swapchain->device, - self->descriptor_set_layout_world_view, nullptr); -} - -void -load_pipeline_layout(void *obj) -{ - auto self = static_cast(obj); - - std::array set_layouts{ - self->descriptor_set_layout_world_view, - self->descriptor_set_layout_model_instance}; - - 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(obj); - - vkDestroyPipelineLayout( - cg_core.vk_device_with_swapchain->device, self->pipeline_layout, nullptr); -} - -void -load_view_projection_uniform_buffer(void *obj) -{ - auto self = static_cast(obj); - - try - { - self->ub_view_projection.reserve(cg_core.vk_swapchain->images_count); - for(auto i{0}; i < cg_core.vk_swapchain->images_count; i++) - self->ub_view_projection.emplace_back( - cg_core.vk_device_with_swapchain, sizeof(VK::UBOViewProjection)); - } - catch(const std::exception& e) - { - throw CommandError{e.what()}; - } -} - -void -unload_view_projection_uniform_buffer(void *obj) -{ - auto self = static_cast(obj); - - self->ub_view_projection.clear(); -} - -void -load_directional_light_uniform_buffer(void *obj) -{ - auto self = static_cast(obj); - - try - { - self->ub_view_projection.reserve(cg_core.vk_swapchain->images_count); - for(auto i{0}; i < cg_core.vk_swapchain->images_count; i++) - self->ub_directional_light.emplace_back( - cg_core.vk_device_with_swapchain, sizeof(VK::UBODirectionalLight)); - } - catch(const std::exception& e) - { - throw CommandError{e.what()}; - } -} - -void -unload_directional_light_uniform_buffer(void *obj) -{ - auto self = static_cast(obj); - - self->ub_directional_light.clear(); -} - -void -load_descriptor_pool(void *obj) -{ - auto self = static_cast(obj); - - VkDescriptorPoolSize descriptor_pool_size{}; - descriptor_pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptor_pool_size.descriptorCount = - self->ub_view_projection.size() + self->ub_directional_light.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->ub_view_projection.size() + self->ub_directional_light.size(); - pool_info.poolSizeCount = 1; - pool_info.pPoolSizes = &descriptor_pool_size; - - if(vkCreateDescriptorPool( - cg_core.vk_device_with_swapchain->device, - &pool_info, nullptr, &self->descriptor_pool) != VK_SUCCESS) - throw CommandError{"Failed to create a Vulkan descriptor pool."}; -} - -void -unload_descriptor_pool(void *obj) -{ - auto self = static_cast(obj); - - vkDestroyDescriptorPool( - cg_core.vk_device_with_swapchain->device, self->descriptor_pool, nullptr); -} - -void -load_world_view_descriptor_sets(void *obj) -{ - auto self = static_cast(obj); - - std::vector layouts( - cg_core.vk_swapchain->images_count, - self->descriptor_set_layout_world_view); - - VkDescriptorSetAllocateInfo alloc_info{}; - alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - alloc_info.descriptorPool = self->descriptor_pool; - alloc_info.descriptorSetCount = self->ub_view_projection.size(); - alloc_info.pSetLayouts = layouts.data(); - - self->world_view_descriptor_sets.resize( - cg_core.vk_swapchain->images_count); - if(vkAllocateDescriptorSets( - cg_core.vk_device_with_swapchain->device, &alloc_info, - self->world_view_descriptor_sets.data()) != VK_SUCCESS) - throw CommandError{"Failed to create Vulkan world view descriptor set."}; -} - -void -load_resources_to_descriptor_sets(void *obj) -{ - auto self = static_cast(obj); - - for(auto i{0}; i < self->ub_view_projection.size(); i++) - { - VkDescriptorBufferInfo view_projection_info{}; - view_projection_info.buffer = self->ub_view_projection[i].buffer; - view_projection_info.offset = 0; - view_projection_info.range = sizeof(VK::UBOViewProjection); - - VkDescriptorBufferInfo directional_light_info{}; - directional_light_info.buffer = self->ub_directional_light[i].buffer; - directional_light_info.offset = 0; - directional_light_info.range = sizeof(VK::UBODirectionalLight); - - std::array write_descriptors{}; - write_descriptors[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - write_descriptors[0].dstSet = self->world_view_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 = &view_projection_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->world_view_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_UNIFORM_BUFFER; - write_descriptors[1].pBufferInfo = &directional_light_info; - write_descriptors[1].pImageInfo = nullptr; - write_descriptors[1].pTexelBufferView = nullptr; - - vkUpdateDescriptorSets( - cg_core.vk_device_with_swapchain->device, write_descriptors.size(), - write_descriptors.data(), 0, nullptr); - } -} - -void -load_depth_image(void *obj) -{ - auto self = static_cast(obj); - - VkExtent3D extent3d{}; - extent3d.width = cg_core.screen_width; - extent3d.height = cg_core.screen_height; - extent3d.depth = 1; - - try - { - VK::Image::create( - cg_core.vk_device_with_swapchain, - &self->depth_image, - &self->depth_image_memory, - VK_FORMAT_D32_SFLOAT, - extent3d, - 1, - VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT - ); - } - catch(VK::Image::Error error) - { - std::string error_message{"Failed to create depth image → "}; - error_message += error.what(); - throw CommandError{error_message}; - } -} - -void -unload_depth_image(void *obj) -{ - auto self = static_cast(obj); - - vkDestroyImage( - cg_core.vk_device_with_swapchain->device, self->depth_image, nullptr); - vkFreeMemory( - cg_core.vk_device_with_swapchain->device, self->depth_image_memory, - nullptr); -} - -void -load_depth_image_view(void *obj) -{ - auto self = static_cast(obj); - - try - { - VK::Image::create_view( - cg_core.vk_device_with_swapchain, &self->depth_image_view, - self->depth_image, - VK_FORMAT_D32_SFLOAT, VK_IMAGE_ASPECT_DEPTH_BIT); - } - catch(VK::Image::Error error) - { - std::string error_message{"Failed to create depth image view → "}; - error_message += error.what(); - throw CommandError{error_message}; - } -} - -void -unload_depth_image_view(void *obj) -{ - auto self = static_cast(obj); - - vkDestroyImageView( - cg_core.vk_device_with_swapchain->device, self->depth_image_view, nullptr); -} - -void -load_render_pass(void *obj) -{ - auto self = static_cast(obj); - - std::array 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_CLEAR; - 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_UNDEFINED; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - // Depth attachment. - attachments[1].flags = 0; - attachments[1].format = VK_FORMAT_D32_SFLOAT; - attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[1].finalLayout = - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkAttachmentReference color_attachment_ref{}; - color_attachment_ref.attachment = 0; - color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkAttachmentReference depth_attachment_ref{}; - depth_attachment_ref.attachment = 1; - depth_attachment_ref.layout = - VK_IMAGE_LAYOUT_DEPTH_STENCIL_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 = &depth_attachment_ref; - 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(obj); - - vkDestroyRenderPass( - cg_core.vk_device_with_swapchain->device, self->render_pass, nullptr); -} - -void -load_framebuffer(void *obj) -{ - auto self = static_cast(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 attachments = { - cg_core.vk_swapchain->image_views[i], - self->depth_image_view - }; - - 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(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(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->vert_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->frag_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(VK::Vertex); - vertex_input_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - - std::array vertex_attribute{}; - // Position. - 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); - // 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); - // 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); - - 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(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_LIST; - input_assembly.primitiveRestartEnable = VK_FALSE; - - VkViewport viewport = {}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = static_cast(cg_core.screen_width); - viewport.height = static_cast(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; - - VkPipelineDepthStencilStateCreateInfo depth_stencil = {}; - depth_stencil.sType = - VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - depth_stencil.depthTestEnable = VK_TRUE; - depth_stencil.depthWriteEnable = VK_TRUE; - depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS; - depth_stencil.depthBoundsTestEnable = VK_FALSE; - depth_stencil.minDepthBounds = 0.0f; - depth_stencil.maxDepthBounds = 1.0f; - depth_stencil.stencilTestEnable = VK_FALSE; - depth_stencil.front = {}; - depth_stencil.back = {}; - - 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 = &depth_stencil; - 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(obj); - - vkDestroyPipeline( - cg_core.vk_device_with_swapchain->device, self->graphic_pipeline, nullptr); -} - -void -load_frame_sync(void *obj) -{ - auto self = static_cast(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(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(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(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(obj); - - vkWaitForFences(cg_core.vk_device_with_swapchain->device, 2, - self->in_flight_fences.data(), VK_TRUE, - std::numeric_limits::max()); - vkDestroyCommandPool( - self->queue_family->device->device, self->command_pool, nullptr); -} - -void -load_draw_command_buffer(void *obj) -{ - auto self = static_cast(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}, - {&load_descriptor_set_layout_world_view, - &unload_descriptor_set_layout_world_view}, - {&load_pipeline_layout, &unload_pipeline_layout}, - {&load_view_projection_uniform_buffer, - &unload_view_projection_uniform_buffer}, - {&load_directional_light_uniform_buffer, - &unload_directional_light_uniform_buffer}, - {&load_descriptor_pool, &unload_descriptor_pool}, - // By destroying the pool the sets are also destroyed. - {&load_world_view_descriptor_sets, nullptr}, - {&load_resources_to_descriptor_sets, nullptr}, - {&load_depth_image, &unload_depth_image}, - {&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} -}; - -} - -namespace VK -{ - -GraphicsPipeline::GraphicsPipeline(): - current_frame{0}, - camera_position{std::make_shared(0.0f, 0.0f, 0.0f)}, - camera_rotation{std::make_shared(0.0f, 0.0f, 0.0f)}, - models_to_draw{cg_core.vk_swapchain->images_count} -{ - loader.execute(this); -} - -GraphicsPipeline::~GraphicsPipeline() -{ - loader.revert(this); -} - -void -GraphicsPipeline::draw() -{ - vkWaitForFences(cg_core.vk_device_with_swapchain->device, 1, - &this->in_flight_fences[this->current_frame], VK_TRUE, - std::numeric_limits::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::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 clear_values{}; - clear_values[0].color = {0.12f, 0.12f, 0.18f, 1.0f}; - clear_values[1].depthStencil = {1.0f, 0}; - - 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 = clear_values.size(); - render_pass_begin.pClearValues = clear_values.data(); - - vkCmdBeginRenderPass( - draw_command_buffer, &render_pass_begin,VK_SUBPASS_CONTENTS_INLINE); - - VkViewport vk_viewport{}; - vk_viewport.width = static_cast(cg_core.screen_width); - vk_viewport.height = static_cast(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 models - for(auto& [model, instances] : this->models_to_draw[this->current_frame]) - { - // Commands - { - std::array vk_descriptor_sets{ - this->world_view_descriptor_sets[image_index], - model->descriptor_sets[image_index]}; - VkBuffer vertex_buffers[]{model->vertex_buffer->buffer}; - VkDeviceSize offsets[]{0}; - - vkCmdBindDescriptorSets( - draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - this->pipeline_layout, 0, vk_descriptor_sets.size(), - vk_descriptor_sets.data(), 0, nullptr); - vkCmdBindPipeline( - draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - this->graphic_pipeline); - vkCmdBindVertexBuffers( - draw_command_buffer, 0, 1, vertex_buffers, offsets); - vkCmdBindIndexBuffer( - draw_command_buffer, model->index_buffer->buffer, 0, - VK_INDEX_TYPE_UINT32); - vkCmdDrawIndexed( - draw_command_buffer, model->index_count, instances.size(), 0, 0, 0); - } - - VK::UBOModelInstance ubo_model_instance; - - for(int i{0}; i < instances.size(); i++) - { - // Object matrix. - glm::mat4 instance_matrix{1.0f}; - instance_matrix = glm::translate( - instance_matrix, instances[i].position); - instance_matrix = glm::rotate( - instance_matrix, instances[i].rotation.x, glm::vec3{1.0, 0.0, 0.0}); - instance_matrix = glm::rotate( - instance_matrix, instances[i].rotation.y, glm::vec3{0.0, 1.0, 0.0}); - 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; - } - - 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 - { - VK::UBOViewProjection ubo_view_projection{}; - - // View matrix. - ubo_view_projection.view = glm::mat4{1.0f}; - ubo_view_projection.view = glm::translate( - ubo_view_projection.view, *this->camera_position); - ubo_view_projection.view = glm::rotate( - ubo_view_projection.view, this->camera_rotation->y, - glm::vec3{0.0, 1.0, 0.0}); - ubo_view_projection.view = glm::rotate( - ubo_view_projection.view, this->camera_rotation->x, - glm::vec3{1.0, 0.0, 0.0}); - ubo_view_projection.view = glm::rotate( - ubo_view_projection.view, this->camera_rotation->z, - glm::vec3{0.0, 0.0, 1.0}); - ubo_view_projection.view = glm::inverse(ubo_view_projection.view); - - // Projection matrix. - ubo_view_projection.proj = glm::perspective( - glm::radians(45.0f), - cg_core.screen_width / static_cast(cg_core.screen_height), - 0.1f, 100.0f); - ubo_view_projection.proj[1][1] *= -1; - - ubo_view_projection.ambient_color = glm::vec4{0.25, 0.25, 0.25, 1.0}; - - this->ub_view_projection[image_index].copy_data(&ubo_view_projection); - } - - // Update directional light uniform buffers - { - UBODirectionalLight ubo_directional_light{}; - ubo_directional_light.direction = glm::vec3{-0.57735, -0.57735, -0.57735}; - ubo_directional_light.color = glm::vec4{0.8, 0.8, 0.8, 1.0}; - - 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(); - } -} - -} diff --git a/src/vk/graphics_pipeline.hpp b/src/vk/graphics_pipeline.hpp deleted file mode 100644 index b4a282a..0000000 --- a/src/vk/graphics_pipeline.hpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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_H -#define CANDY_GEAR_VK_GRAPHICS_PIPELINE_H 1 - -#include -#include - -#include "../command.hpp" -#include "core.hpp" -#include "command_pool.hpp" -#include "model.hpp" -#include "model_instance.hpp" -#include "uniform_buffer.hpp" - -namespace VK -{ - -struct GraphicsPipeline -{ - VkDescriptorSetLayout descriptor_set_layout_model_instance; - VkDescriptorSetLayout descriptor_set_layout_world_view; - VkPipelineLayout pipeline_layout; - - // Depth image. - VkImage depth_image; - VkDeviceMemory depth_image_memory; - VkImageView depth_image_view; - - // FIXME: if this vector get resized, it will cause a segmentation fault! - std::vector ub_view_projection; - std::vector ub_directional_light; - - VkDescriptorPool descriptor_pool; - std::vector world_view_descriptor_sets; - - VkRenderPass render_pass; - std::vector swapchain_framebuffers; - VkPipeline graphic_pipeline; - - QueueFamily *queue_family; - VkCommandPool command_pool; - std::vector draw_command_buffers; - - // Buffering control. - static const int max_frames_in_flight{2}; - size_t current_frame; - std::vector image_available_semaphores; - std::vector render_finished_semaphores; - std::vector in_flight_fences; - - std::shared_ptr camera_position; - std::shared_ptr camera_rotation; - std::vector, std::vector>> - models_to_draw; - - GraphicsPipeline(); - ~GraphicsPipeline(); - - void - draw(); -}; - -} - -#endif /* CANDY_GEAR_VK_GRAPHICS_PIPELINE_H */ 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 + +#include "../core.hpp" +#include "sprite.hpp" + +namespace +{ + +void +load_descriptor_set_layout_image_sprites(void *obj) +{ + auto self = static_cast(obj); + + std::array 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(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(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(obj); + + std::array 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(obj); + + vkDestroyPipelineLayout( + cg_core.vk_device_with_swapchain->device, self->pipeline_layout, nullptr); +} + +void +load_render_pass(void *obj) +{ + auto self = static_cast(obj); + + std::array 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(obj); + + vkDestroyRenderPass( + cg_core.vk_device_with_swapchain->device, self->render_pass, nullptr); +} + +void +load_framebuffer(void *obj) +{ + auto self = static_cast(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 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(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(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 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(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(cg_core.screen_width); + viewport.height = static_cast(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(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(cg_core.screen_width); + vk_viewport.height = static_cast(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 + +#include "core.hpp" +#include "sprite.hpp" + +namespace VK +{ + +struct GraphicsPipeline2D +{ + VkDescriptorSetLayout descriptor_set_layout_sprites; + VkPipelineLayout pipeline_layout; + + VkRenderPass render_pass; + std::vector swapchain_framebuffers; + VkPipeline graphic_pipeline; + + std::vector< + std::unordered_map, std::vector>> + 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_3d.cpp b/src/vk/graphics_pipeline_3d.cpp new file mode 100644 index 0000000..04a2adb --- /dev/null +++ b/src/vk/graphics_pipeline_3d.cpp @@ -0,0 +1,880 @@ +/* + * 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_3d.hpp" + +#include +#include + +#include "../core.hpp" +#include "core.hpp" +#include "image.hpp" +#include "uniform_buffer.hpp" +#include "vertex_3d.hpp" + +namespace +{ + +void +load_descriptor_set_layout_model_instance(void *obj) +{ + auto self = static_cast(obj); + + std::array 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(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_model_instance) != VK_SUCCESS) + throw CommandError{ + "Failed to create Vulkan descriptor set layout for model instance."}; +} + +void +unload_descriptor_set_layout_model_instance(void *obj) +{ + auto self = static_cast(obj); + + vkDestroyDescriptorSetLayout( + cg_core.vk_device_with_swapchain->device, + self->descriptor_set_layout_model_instance, nullptr); +} + +void +load_descriptor_set_layout_world_view(void *obj) +{ + auto self = static_cast(obj); + + std::array set_layouts{}; + set_layouts[0].binding = 0; + set_layouts[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + set_layouts[0].descriptorCount = 1; + set_layouts[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + set_layouts[0].pImmutableSamplers = nullptr; + + set_layouts[1].binding = 1; + set_layouts[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + set_layouts[1].descriptorCount = 1; + set_layouts[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + set_layouts[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 = set_layouts.size(); + layout_info.pBindings = set_layouts.data(); + + if(vkCreateDescriptorSetLayout( + cg_core.vk_device_with_swapchain->device, &layout_info, nullptr, + &self->descriptor_set_layout_world_view) != VK_SUCCESS) + throw CommandError{ + "Failed to create Vulkan descriptor set layout for world view."}; +} + +void +unload_descriptor_set_layout_world_view(void *obj) +{ + auto self = static_cast(obj); + + vkDestroyDescriptorSetLayout( + cg_core.vk_device_with_swapchain->device, + self->descriptor_set_layout_world_view, nullptr); +} + +void +load_pipeline_layout(void *obj) +{ + auto self = static_cast(obj); + + std::array set_layouts{ + self->descriptor_set_layout_world_view, + self->descriptor_set_layout_model_instance}; + + 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(obj); + + vkDestroyPipelineLayout( + cg_core.vk_device_with_swapchain->device, self->pipeline_layout, nullptr); +} + +void +load_view_projection_uniform_buffer(void *obj) +{ + auto self = static_cast(obj); + + try + { + self->ub_view_projection.reserve(cg_core.vk_swapchain->images_count); + for(auto i{0}; i < cg_core.vk_swapchain->images_count; i++) + self->ub_view_projection.emplace_back( + cg_core.vk_device_with_swapchain, sizeof(VK::UBOViewProjection)); + } + catch(const std::exception& e) + { + throw CommandError{e.what()}; + } +} + +void +unload_view_projection_uniform_buffer(void *obj) +{ + auto self = static_cast(obj); + + self->ub_view_projection.clear(); +} + +void +load_directional_light_uniform_buffer(void *obj) +{ + auto self = static_cast(obj); + + try + { + self->ub_view_projection.reserve(cg_core.vk_swapchain->images_count); + for(auto i{0}; i < cg_core.vk_swapchain->images_count; i++) + self->ub_directional_light.emplace_back( + cg_core.vk_device_with_swapchain, sizeof(VK::UBODirectionalLight)); + } + catch(const std::exception& e) + { + throw CommandError{e.what()}; + } +} + +void +unload_directional_light_uniform_buffer(void *obj) +{ + auto self = static_cast(obj); + + self->ub_directional_light.clear(); +} + +void +load_descriptor_pool(void *obj) +{ + auto self = static_cast(obj); + + VkDescriptorPoolSize descriptor_pool_size{}; + descriptor_pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptor_pool_size.descriptorCount = + self->ub_view_projection.size() + self->ub_directional_light.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->ub_view_projection.size() + self->ub_directional_light.size(); + pool_info.poolSizeCount = 1; + pool_info.pPoolSizes = &descriptor_pool_size; + + if(vkCreateDescriptorPool( + cg_core.vk_device_with_swapchain->device, + &pool_info, nullptr, &self->descriptor_pool) != VK_SUCCESS) + throw CommandError{"Failed to create a Vulkan descriptor pool."}; +} + +void +unload_descriptor_pool(void *obj) +{ + auto self = static_cast(obj); + + vkDestroyDescriptorPool( + cg_core.vk_device_with_swapchain->device, self->descriptor_pool, nullptr); +} + +void +load_world_view_descriptor_sets(void *obj) +{ + auto self = static_cast(obj); + + std::vector layouts( + cg_core.vk_swapchain->images_count, + self->descriptor_set_layout_world_view); + + VkDescriptorSetAllocateInfo alloc_info{}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.descriptorPool = self->descriptor_pool; + alloc_info.descriptorSetCount = self->ub_view_projection.size(); + alloc_info.pSetLayouts = layouts.data(); + + self->world_view_descriptor_sets.resize( + cg_core.vk_swapchain->images_count); + if(vkAllocateDescriptorSets( + cg_core.vk_device_with_swapchain->device, &alloc_info, + self->world_view_descriptor_sets.data()) != VK_SUCCESS) + throw CommandError{"Failed to create Vulkan world view descriptor set."}; +} + +void +load_resources_to_descriptor_sets(void *obj) +{ + auto self = static_cast(obj); + + for(auto i{0}; i < self->ub_view_projection.size(); i++) + { + VkDescriptorBufferInfo view_projection_info{}; + view_projection_info.buffer = self->ub_view_projection[i].buffer; + view_projection_info.offset = 0; + view_projection_info.range = sizeof(VK::UBOViewProjection); + + VkDescriptorBufferInfo directional_light_info{}; + directional_light_info.buffer = self->ub_directional_light[i].buffer; + directional_light_info.offset = 0; + directional_light_info.range = sizeof(VK::UBODirectionalLight); + + std::array write_descriptors{}; + write_descriptors[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_descriptors[0].dstSet = self->world_view_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 = &view_projection_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->world_view_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_UNIFORM_BUFFER; + write_descriptors[1].pBufferInfo = &directional_light_info; + write_descriptors[1].pImageInfo = nullptr; + write_descriptors[1].pTexelBufferView = nullptr; + + vkUpdateDescriptorSets( + cg_core.vk_device_with_swapchain->device, write_descriptors.size(), + write_descriptors.data(), 0, nullptr); + } +} + +void +load_depth_image(void *obj) +{ + auto self = static_cast(obj); + + VkExtent3D extent3d{}; + extent3d.width = cg_core.screen_width; + extent3d.height = cg_core.screen_height; + extent3d.depth = 1; + + try + { + VK::Image::create( + cg_core.vk_device_with_swapchain, + &self->depth_image, + &self->depth_image_memory, + VK_FORMAT_D32_SFLOAT, + extent3d, + 1, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT + ); + } + catch(VK::Image::Error error) + { + std::string error_message{"Failed to create depth image → "}; + error_message += error.what(); + throw CommandError{error_message}; + } +} + +void +unload_depth_image(void *obj) +{ + auto self = static_cast(obj); + + vkDestroyImage( + cg_core.vk_device_with_swapchain->device, self->depth_image, nullptr); + vkFreeMemory( + cg_core.vk_device_with_swapchain->device, self->depth_image_memory, + nullptr); +} + +void +load_depth_image_view(void *obj) +{ + auto self = static_cast(obj); + + try + { + VK::Image::create_view( + cg_core.vk_device_with_swapchain, &self->depth_image_view, + self->depth_image, + VK_FORMAT_D32_SFLOAT, VK_IMAGE_ASPECT_DEPTH_BIT); + } + catch(VK::Image::Error error) + { + std::string error_message{"Failed to create depth image view → "}; + error_message += error.what(); + throw CommandError{error_message}; + } +} + +void +unload_depth_image_view(void *obj) +{ + auto self = static_cast(obj); + + vkDestroyImageView( + cg_core.vk_device_with_swapchain->device, self->depth_image_view, nullptr); +} + +void +load_render_pass(void *obj) +{ + auto self = static_cast(obj); + + std::array 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_CLEAR; + 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_UNDEFINED; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + // Depth attachment. + attachments[1].flags = 0; + attachments[1].format = VK_FORMAT_D32_SFLOAT; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[1].finalLayout = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkAttachmentReference color_attachment_ref{}; + color_attachment_ref.attachment = 0; + color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkAttachmentReference depth_attachment_ref{}; + depth_attachment_ref.attachment = 1; + depth_attachment_ref.layout = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_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 = &depth_attachment_ref; + 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(obj); + + vkDestroyRenderPass( + cg_core.vk_device_with_swapchain->device, self->render_pass, nullptr); +} + +void +load_framebuffer(void *obj) +{ + auto self = static_cast(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 attachments = { + cg_core.vk_swapchain->image_views[i], + self->depth_image_view + }; + + 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(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(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->vert3d_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->frag3d_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(VK::Vertex3D); + vertex_input_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + std::array vertex_attribute{}; + // Position. + vertex_attribute[0].location = 0; + vertex_attribute[0].binding = 0; + vertex_attribute[0].format = VK_FORMAT_R32G32B32_SFLOAT; + 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::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::Vertex3D, texture_coord); + + 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(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_LIST; + input_assembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport = {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(cg_core.screen_width); + viewport.height = static_cast(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; + + VkPipelineDepthStencilStateCreateInfo depth_stencil = {}; + depth_stencil.sType = + VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depth_stencil.depthTestEnable = VK_TRUE; + depth_stencil.depthWriteEnable = VK_TRUE; + depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS; + depth_stencil.depthBoundsTestEnable = VK_FALSE; + depth_stencil.minDepthBounds = 0.0f; + depth_stencil.maxDepthBounds = 1.0f; + depth_stencil.stencilTestEnable = VK_FALSE; + depth_stencil.front = {}; + depth_stencil.back = {}; + + 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 = &depth_stencil; + 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(obj); + + vkDestroyPipeline( + cg_core.vk_device_with_swapchain->device, self->graphic_pipeline, nullptr); +} + +const CommandChain loader{ + {&load_descriptor_set_layout_model_instance, + &unload_descriptor_set_layout_model_instance}, + {&load_descriptor_set_layout_world_view, + &unload_descriptor_set_layout_world_view}, + {&load_pipeline_layout, &unload_pipeline_layout}, + {&load_view_projection_uniform_buffer, + &unload_view_projection_uniform_buffer}, + {&load_directional_light_uniform_buffer, + &unload_directional_light_uniform_buffer}, + {&load_descriptor_pool, &unload_descriptor_pool}, + // By destroying the pool the sets are also destroyed. + {&load_world_view_descriptor_sets, nullptr}, + {&load_resources_to_descriptor_sets, nullptr}, + {&load_depth_image, &unload_depth_image}, + {&load_depth_image_view, &unload_depth_image_view}, + {&load_render_pass, &unload_render_pass}, + {&load_framebuffer, &unload_framebuffer}, + {&load_pipeline, &unload_pipeline} +}; + +} + +namespace VK +{ + +GraphicsPipeline3D::GraphicsPipeline3D(): + camera_position{std::make_shared(0.0f, 0.0f, 0.0f)}, + camera_rotation{std::make_shared(0.0f, 0.0f, 0.0f)}, + models_to_draw{cg_core.vk_swapchain->images_count} +{ + loader.execute(this); +} + +GraphicsPipeline3D::~GraphicsPipeline3D() +{ + loader.revert(this); +} + +void +GraphicsPipeline3D::draw( + const VkCommandBuffer draw_command_buffer, const size_t current_frame, + const size_t next_frame, const uint32_t image_index) +{ + // Load command. + { + // Dark gray blue. + std::array clear_values{}; + clear_values[0].color = {0.12f, 0.12f, 0.18f, 1.0f}; + clear_values[1].depthStencil = {1.0f, 0}; + + 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 = clear_values.size(); + render_pass_begin.pClearValues = clear_values.data(); + + vkCmdBeginRenderPass( + draw_command_buffer, &render_pass_begin,VK_SUBPASS_CONTENTS_INLINE); + + VkViewport vk_viewport{}; + vk_viewport.width = static_cast(cg_core.screen_width); + vk_viewport.height = static_cast(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 models + for(auto& [model, instances] : this->models_to_draw[current_frame]) + { + // Commands + { + std::array vk_descriptor_sets{ + this->world_view_descriptor_sets[image_index], + model->descriptor_sets[image_index]}; + VkBuffer vertex_buffers[]{model->vertex_buffer->buffer}; + VkDeviceSize offsets[]{0}; + + vkCmdBindDescriptorSets( + draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + this->pipeline_layout, 0, vk_descriptor_sets.size(), + vk_descriptor_sets.data(), 0, nullptr); + vkCmdBindPipeline( + draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + this->graphic_pipeline); + vkCmdBindVertexBuffers( + draw_command_buffer, 0, 1, vertex_buffers, offsets); + vkCmdBindIndexBuffer( + draw_command_buffer, model->index_buffer->buffer, 0, + VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed( + draw_command_buffer, model->index_count, instances.size(), 0, 0, 0); + } + + VK::UBOModelInstance ubo_model_instance; + + for(int i{0}; i < instances.size(); i++) + { + // Object matrix. + glm::mat4 instance_matrix{1.0f}; + instance_matrix = glm::translate( + instance_matrix, instances[i].position); + instance_matrix = glm::rotate( + instance_matrix, instances[i].rotation.x, glm::vec3{1.0, 0.0, 0.0}); + instance_matrix = glm::rotate( + instance_matrix, instances[i].rotation.y, glm::vec3{0.0, 1.0, 0.0}); + instance_matrix = glm::rotate( + instance_matrix, instances[i].rotation.z, glm::vec3{0.0, 0.0, 1.0}); + + ubo_model_instance.instances[i] = instance_matrix; + } + + model->ub_model_instance[image_index].copy_data(&ubo_model_instance); + } + vkCmdEndRenderPass(draw_command_buffer); + + } + + // Update view projection uniform buffers + { + VK::UBOViewProjection ubo_view_projection{}; + + // View matrix. + ubo_view_projection.view = glm::mat4{1.0f}; + ubo_view_projection.view = glm::translate( + ubo_view_projection.view, *this->camera_position); + ubo_view_projection.view = glm::rotate( + ubo_view_projection.view, this->camera_rotation->y, + glm::vec3{0.0, 1.0, 0.0}); + ubo_view_projection.view = glm::rotate( + ubo_view_projection.view, this->camera_rotation->x, + glm::vec3{1.0, 0.0, 0.0}); + ubo_view_projection.view = glm::rotate( + ubo_view_projection.view, this->camera_rotation->z, + glm::vec3{0.0, 0.0, 1.0}); + ubo_view_projection.view = glm::inverse(ubo_view_projection.view); + + // Projection matrix. + ubo_view_projection.proj = glm::perspective( + glm::radians(45.0f), + cg_core.screen_width / static_cast(cg_core.screen_height), + 0.1f, 100.0f); + ubo_view_projection.proj[1][1] *= -1; + + ubo_view_projection.ambient_color = glm::vec4{0.25, 0.25, 0.25, 1.0}; + + this->ub_view_projection[image_index].copy_data(&ubo_view_projection); + } + + // Update directional light uniform buffers + { + UBODirectionalLight ubo_directional_light{}; + ubo_directional_light.direction = glm::vec3{-0.57735, -0.57735, -0.57735}; + ubo_directional_light.color = glm::vec4{0.8, 0.8, 0.8, 1.0}; + + this->ub_directional_light[image_index].copy_data(&ubo_directional_light); + } + + // Prepare for the next frame. + this->models_to_draw[next_frame].clear(); +} + +} diff --git a/src/vk/graphics_pipeline_3d.hpp b/src/vk/graphics_pipeline_3d.hpp new file mode 100644 index 0000000..2efbc00 --- /dev/null +++ b/src/vk/graphics_pipeline_3d.hpp @@ -0,0 +1,71 @@ +/* + * 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_3D_H +#define CANDY_GEAR_VK_GRAPHICS_PIPELINE_3D_H 1 + +#include +#include + +#include "../command.hpp" +#include "core.hpp" +#include "command_pool.hpp" +#include "model.hpp" +#include "model_instance.hpp" +#include "uniform_buffer.hpp" + +namespace VK +{ + +struct GraphicsPipeline3D +{ + VkDescriptorSetLayout descriptor_set_layout_model_instance; + VkDescriptorSetLayout descriptor_set_layout_world_view; + VkPipelineLayout pipeline_layout; + + // Depth image. + VkImage depth_image; + VkDeviceMemory depth_image_memory; + VkImageView depth_image_view; + + // FIXME: if this vector get resized, it will cause a segmentation fault! + std::vector ub_view_projection; + std::vector ub_directional_light; + + VkDescriptorPool descriptor_pool; + std::vector world_view_descriptor_sets; + + VkRenderPass render_pass; + std::vector swapchain_framebuffers; + VkPipeline graphic_pipeline; + + std::shared_ptr camera_position; + std::shared_ptr camera_rotation; + std::vector, std::vector>> + models_to_draw; + + GraphicsPipeline3D(); + ~GraphicsPipeline3D(); + + 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_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 vertexes{vertex_count}; + std::vector 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(obj); @@ -196,7 +195,7 @@ load_descriptor_sets(void *obj) std::vector 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(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(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(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(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(obj); + + vkWaitForFences(cg_core.vk_device_with_swapchain->device, 2, + self->in_flight_fences.data(), VK_TRUE, + std::numeric_limits::max()); + vkDestroyCommandPool( + self->queue_family->device->device, self->command_pool, nullptr); +} + +void +load_draw_command_buffer(void *obj) +{ + auto self = static_cast(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::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::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 + +#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 image_available_semaphores; + std::vector render_finished_semaphores; + std::vector in_flight_fences; + + QueueFamily *queue_family; + VkCommandPool command_pool; + std::vector 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 + +#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(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(obj); + + delete self->sprite->vertex_buffer; + delete self->sprite->source_buffer; +} + +void +load_uniform_buffers(void *obj) +{ + auto self = static_cast(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(obj); + + self->sprite->ub_sprite_positions.clear(); +} + +void +load_descriptor_set_pool(void *obj) +{ + auto self = static_cast(obj); + + std::array 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(obj); + + vkDestroyDescriptorPool( + self->sprite->queue_family->device->device, self->sprite->descriptor_pool, + nullptr); +} + +void +load_descriptor_sets(void *obj) +{ + auto self = static_cast(obj); + + std::vector 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(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 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, 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 +#include +#include + +#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 ub_sprite_positions; + + VkDescriptorPool descriptor_pool; + std::vector descriptor_sets; + + std::shared_ptr texture; + + Sprite(std::shared_ptr 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.hpp deleted file mode 100644 index 07332a8..0000000 --- a/src/vk/vertex.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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_VERTEX_H -#define CANDY_GEAR_VK_VERTEX_H 1 - -#include "core.hpp" - -namespace VK -{ - -struct Vertex -{ - glm::vec3 position; - glm::vec3 normal; - glm::vec2 texture_coord; -}; - -} - -#endif /* CANDY_GEAR_VK_VERTEX_H */ diff --git a/src/vk/vertex_3d.hpp b/src/vk/vertex_3d.hpp new file mode 100644 index 0000000..8bb8675 --- /dev/null +++ b/src/vk/vertex_3d.hpp @@ -0,0 +1,34 @@ +/* + * 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_VERTEX_3D_H +#define CANDY_GEAR_VK_VERTEX_3D_H 1 + +#include "core.hpp" + +namespace VK +{ + +struct Vertex3D +{ + glm::vec3 position; + glm::vec3 normal; + glm::vec2 texture_coord; +}; + +} + +#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); -- cgit v1.2.3