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