From 35b841cbf3148356f43a2f4d7f376900ecd60ab7 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 18 Sep 2017 18:52:33 -0700 Subject: [PATCH] working on getting SDL2's testvulkan to run --- src/CMakeLists.txt | 1 - src/demo/demo.cpp | 30 ++--- src/image/CMakeLists.txt | 24 ---- src/image/image.cpp | 23 ---- src/image/image.h | 236 ---------------------------------- src/pipeline/CMakeLists.txt | 3 +- src/pipeline/pipeline.cpp | 55 +------- src/pipeline/pipeline.h | 157 ++++++---------------- src/util/circular_queue.h | 182 ++++++++++++++++++++++++++ src/vulkan/api_objects.cpp | 108 +++++++++++++++- src/vulkan/api_objects.h | 202 ++++++++++++++++++++++++++++- src/vulkan_icd/CMakeLists.txt | 1 + src/vulkan_icd/vulkan_icd.cpp | 186 ++++++++++++++++++++++----- src/vulkan_icd/wsi.h | 4 + src/vulkan_icd/x11_wsi.cpp | 193 +++++++++++++++++++++------ 15 files changed, 859 insertions(+), 546 deletions(-) delete mode 100644 src/image/CMakeLists.txt delete mode 100644 src/image/image.cpp delete mode 100644 src/image/image.h create mode 100644 src/util/circular_queue.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 20d202e..324b647 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,7 +22,6 @@ cmake_minimum_required(VERSION 3.3 FATAL_ERROR) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) add_subdirectory(demo) add_subdirectory(generate_spirv_parser) -add_subdirectory(image) add_subdirectory(json) add_subdirectory(llvm_wrapper) add_subdirectory(pipeline) diff --git a/src/demo/demo.cpp b/src/demo/demo.cpp index f304c97..022bfc8 100644 --- a/src/demo/demo.cpp +++ b/src/demo/demo.cpp @@ -211,7 +211,7 @@ void dump_words(const std::vector &words) dump_words(words.data(), words.size()); } -pipeline::Shader_module_handle load_shader(const char *filename) +std::unique_ptr load_shader(const char *filename) { std::cerr << "loading " << filename << std::endl; auto file = load_file(filename); @@ -226,10 +226,10 @@ pipeline::Shader_module_handle load_shader(const char *filename) .codeSize = file->size() * sizeof(spirv::Word), .pCode = file->data(), }; - return pipeline::Shader_module_handle::make(shader_module_create_info); + return pipeline::Shader_module::make(shader_module_create_info); } -pipeline::Pipeline_layout_handle make_pipeline_layout() +std::unique_ptr make_pipeline_layout() { VkPipelineLayoutCreateInfo pipeline_layout_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, @@ -240,7 +240,7 @@ pipeline::Pipeline_layout_handle make_pipeline_layout() .pushConstantRangeCount = 0, .pPushConstantRanges = nullptr, }; - return pipeline::Pipeline_layout_handle::make(pipeline_layout_create_info); + return pipeline::Pipeline_layout::make(pipeline_layout_create_info); } template @@ -653,7 +653,7 @@ int test_main(int argc, char **argv) .dependencyCount = 0, .pDependencies = nullptr, }; - auto render_pass = pipeline::Render_pass_handle::make(render_pass_create_info); + auto render_pass = pipeline::Render_pass::make(render_pass_create_info); constexpr std::size_t stage_index_vertex = 0; constexpr std::size_t stage_index_fragment = stage_index_vertex + 1; constexpr std::size_t stage_count = stage_index_fragment + 1; @@ -663,7 +663,7 @@ int test_main(int argc, char **argv) .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_VERTEX_BIT, - .module = pipeline::to_handle(vertex_shader.get()), + .module = to_handle(vertex_shader.get()), .pName = "main", .pSpecializationInfo = nullptr, }; @@ -672,7 +672,7 @@ int test_main(int argc, char **argv) .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, - .module = pipeline::to_handle(fragment_shader.get()), + .module = to_handle(fragment_shader.get()), .pName = "main", .pSpecializationInfo = nullptr, }; @@ -817,8 +817,8 @@ int test_main(int argc, char **argv) .pDepthStencilState = nullptr, .pColorBlendState = &pipeline_color_blend_state_create_info, .pDynamicState = nullptr, - .layout = pipeline::to_handle(pipeline_layout.get()), - .renderPass = pipeline::to_handle(render_pass.get()), + .layout = to_handle(pipeline_layout.get()), + .renderPass = to_handle(render_pass.get()), .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = -1, @@ -845,15 +845,15 @@ int test_main(int argc, char **argv) .pQueueFamilyIndices = nullptr, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, }; - image::Image color_attachment(image::Image_descriptor(image_create_info), - image::allocate_memory_tag); + auto color_attachment = vulkan::Vulkan_image::create_with_memory( + vulkan::Vulkan_image_descriptor(image_create_info)); VkClearColorValue clear_color; // set clear_color to opaque gray clear_color.float32[0] = 0.25; clear_color.float32[1] = 0.25; clear_color.float32[2] = 0.25; clear_color.float32[3] = 1; - color_attachment.clear(clear_color); + color_attachment->clear(clear_color); constexpr std::uint32_t vertex_start_index = 0; std::uint32_t vertex_end_index = vertexes.size(); constexpr std::uint32_t instance_id = 0; @@ -861,7 +861,7 @@ int test_main(int argc, char **argv) vertexes.data(), }; graphics_pipeline->run( - vertex_start_index, vertex_end_index, instance_id, color_attachment, bindings); + vertex_start_index, vertex_end_index, instance_id, *color_attachment, bindings); typedef std::uint32_t Pixel_type; // check Pixel_type static_assert(std::is_voidrun_fragment_shader( @@ -893,11 +893,11 @@ int test_main(int argc, char **argv) } }; std::unique_ptr surface( - SDL_CreateRGBSurfaceFrom(color_attachment.memory.get(), + SDL_CreateRGBSurfaceFrom(color_attachment->memory.get(), window_width, window_height, bits_per_pixel, - color_attachment.descriptor.get_memory_stride(), + color_attachment->descriptor.get_memory_stride(), rgba(0xFF, 0, 0, 0), rgba(0, 0xFF, 0, 0), rgba(0, 0, 0xFF, 0), diff --git a/src/image/CMakeLists.txt b/src/image/CMakeLists.txt deleted file mode 100644 index d8c7931..0000000 --- a/src/image/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2017 Jacob Lifshay -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# -cmake_minimum_required(VERSION 3.3 FATAL_ERROR) -set(sources image.cpp) -add_library(kazan_image STATIC ${sources}) -target_link_libraries(kazan_image kazan_util kazan_llvm_wrapper kazan_vulkan) diff --git a/src/image/image.cpp b/src/image/image.cpp deleted file mode 100644 index 7f59214..0000000 --- a/src/image/image.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Jacob Lifshay - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ -#include "image.h" diff --git a/src/image/image.h b/src/image/image.h deleted file mode 100644 index 60657b9..0000000 --- a/src/image/image.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright 2017 Jacob Lifshay - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ -#ifndef IMAGE_IMAGE_H_ -#define IMAGE_IMAGE_H_ - -#include "vulkan/vulkan.h" -#include "vulkan/remove_xlib_macros.h" -#include -#include -#include -#include "util/enum.h" - -namespace kazan -{ -namespace image -{ -struct Image_descriptor -{ - static constexpr VkImageCreateFlags supported_flags = - VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; - VkImageCreateFlags flags; - VkImageType type; - VkFormat format; - VkExtent3D extent; - std::uint32_t mip_levels; - std::uint32_t array_layers; - static constexpr VkSampleCountFlags supported_samples = VK_SAMPLE_COUNT_1_BIT; - VkSampleCountFlagBits samples; - VkImageTiling tiling; - constexpr explicit Image_descriptor(const VkImageCreateInfo &image_create_info) noexcept - : flags(image_create_info.flags), - type(image_create_info.imageType), - format(image_create_info.format), - extent(image_create_info.extent), - mip_levels(image_create_info.mipLevels), - array_layers(image_create_info.arrayLayers), - samples(image_create_info.samples), - tiling(image_create_info.tiling) - { - assert(image_create_info.sType == VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO); - assert((flags & ~supported_flags) == 0); - assert((samples & ~supported_samples) == 0); - assert(extent.width > 0); - assert(extent.height > 0); - assert(extent.depth > 0); - -#warning finish implementing Image - assert(type == VK_IMAGE_TYPE_2D && "unimplemented image type"); - assert(extent.depth == 1); - - assert(format == VK_FORMAT_B8G8R8A8_UNORM && "unimplemented image format"); - assert(mip_levels == 1 && "mipmapping is unimplemented"); - assert(array_layers == 1 && "array images are unimplemented"); - assert(tiling == VK_IMAGE_TILING_LINEAR && "non-linear image tiling is unimplemented"); - assert(image_create_info.initialLayout == VK_IMAGE_LAYOUT_UNDEFINED - && "preinitialized images are unimplemented"); - } - constexpr Image_descriptor(VkImageCreateFlags flags, - VkImageType type, - VkFormat format, - VkExtent3D extent, - std::uint32_t mip_levels, - std::uint32_t array_layers, - VkSampleCountFlagBits samples, - VkImageTiling tiling) noexcept : flags(flags), - type(type), - format(format), - extent(extent), - mip_levels(mip_levels), - array_layers(array_layers), - samples(samples), - tiling(tiling) - { - } - constexpr std::size_t get_memory_size() const noexcept - { -#warning finish implementing Image - assert(samples == VK_SAMPLE_COUNT_1_BIT && "multisample images are unimplemented"); - assert(extent.width > 0); - assert(extent.height > 0); - assert(extent.depth > 0); - - assert(type == VK_IMAGE_TYPE_2D && "unimplemented image type"); - assert(extent.depth == 1); - - assert(format == VK_FORMAT_B8G8R8A8_UNORM && "unimplemented image format"); - assert(mip_levels == 1 && "mipmapping is unimplemented"); - assert(array_layers == 1 && "array images are unimplemented"); - assert(tiling == VK_IMAGE_TILING_LINEAR && "non-linear image tiling is unimplemented"); - std::size_t retval = sizeof(std::uint32_t); - retval *= extent.width; - retval *= extent.height; - return retval; - } - constexpr std::size_t get_memory_stride() const noexcept - { -#warning finish implementing Image - assert(samples == VK_SAMPLE_COUNT_1_BIT && "multisample images are unimplemented"); - assert(extent.width > 0); - assert(extent.height > 0); - assert(extent.depth > 0); - - assert(type == VK_IMAGE_TYPE_2D && "unimplemented image type"); - assert(extent.depth == 1); - - assert(format == VK_FORMAT_B8G8R8A8_UNORM && "unimplemented image format"); - assert(mip_levels == 1 && "mipmapping is unimplemented"); - assert(array_layers == 1 && "array images are unimplemented"); - assert(tiling == VK_IMAGE_TILING_LINEAR && "non-linear image tiling is unimplemented"); - std::size_t retval = sizeof(std::uint32_t); - retval *= extent.width; - return retval; - } - constexpr std::size_t get_memory_pixel_size() const noexcept - { -#warning finish implementing Image - assert(samples == VK_SAMPLE_COUNT_1_BIT && "multisample images are unimplemented"); - assert(extent.width > 0); - assert(extent.height > 0); - assert(extent.depth > 0); - - assert(type == VK_IMAGE_TYPE_2D && "unimplemented image type"); - assert(extent.depth == 1); - - assert(format == VK_FORMAT_B8G8R8A8_UNORM && "unimplemented image format"); - assert(mip_levels == 1 && "mipmapping is unimplemented"); - assert(array_layers == 1 && "array images are unimplemented"); - assert(tiling == VK_IMAGE_TILING_LINEAR && "non-linear image tiling is unimplemented"); - std::size_t retval = sizeof(std::uint32_t); - return retval; - } -}; - -struct Allocate_memory_tag -{ - explicit constexpr Allocate_memory_tag(int) noexcept - { - } -}; - -constexpr Allocate_memory_tag allocate_memory_tag{0}; - -struct Image -{ - const Image_descriptor descriptor; - std::unique_ptr memory; - Image(const Image_descriptor &descriptor, - std::unique_ptr memory = nullptr) noexcept : descriptor(descriptor), - memory(std::move(memory)) - { - } - Image(const Image_descriptor &descriptor, Allocate_memory_tag) - : descriptor(descriptor), memory(new unsigned char[descriptor.get_memory_size()]) - { - } - void clear(VkClearColorValue color) noexcept - { - assert(memory); - assert(descriptor.samples == VK_SAMPLE_COUNT_1_BIT - && "multisample images are unimplemented"); - assert(descriptor.extent.width > 0); - assert(descriptor.extent.height > 0); - assert(descriptor.extent.depth > 0); - - assert(descriptor.type == VK_IMAGE_TYPE_2D && "unimplemented image type"); - assert(descriptor.extent.depth == 1); - - assert(descriptor.format == VK_FORMAT_B8G8R8A8_UNORM && "unimplemented image format"); - assert(descriptor.mip_levels == 1 && "mipmapping is unimplemented"); - assert(descriptor.array_layers == 1 && "array images are unimplemented"); - assert(descriptor.tiling == VK_IMAGE_TILING_LINEAR - && "non-linear image tiling is unimplemented"); - union - { - std::uint8_t bytes[4]; - std::uint32_t u32; - } clear_color; - float r_float = color.float32[0]; - float g_float = color.float32[1]; - float b_float = color.float32[2]; - float a_float = color.float32[3]; - auto float_to_byte = [](float v) noexcept->std::uint8_t - { - if(!(v >= 0)) - v = 0; - else if(v > 1) - v = 1; - union - { - std::uint32_t i; - float f; - } u; - static_assert(sizeof(std::uint32_t) == sizeof(float), ""); - u.f = 0x100; - u.i--; // u.f = nextafter(u.f, -1) - v *= u.f; - return (int)v; - }; - clear_color.bytes[0] = float_to_byte(b_float); - clear_color.bytes[1] = float_to_byte(g_float); - clear_color.bytes[2] = float_to_byte(r_float); - clear_color.bytes[3] = float_to_byte(a_float); - std::size_t pixel_count = - static_cast(descriptor.extent.width) * descriptor.extent.height; - std::uint32_t *pixels = reinterpret_cast(memory.get()); - for(std::size_t i = 0; i < pixel_count; i++) - { - pixels[i] = clear_color.u32; - } - } -#warning finish implementing Image -}; -} -} - -#endif // IMAGE_IMAGE_H_ diff --git a/src/pipeline/CMakeLists.txt b/src/pipeline/CMakeLists.txt index 149f665..a394223 100644 --- a/src/pipeline/CMakeLists.txt +++ b/src/pipeline/CMakeLists.txt @@ -27,5 +27,4 @@ target_link_libraries(kazan_pipeline kazan_spirv_to_llvm kazan_util kazan_spirv kazan_llvm_wrapper - kazan_vulkan - kazan_image) + kazan_vulkan) diff --git a/src/pipeline/pipeline.cpp b/src/pipeline/pipeline.cpp index a0b9771..dda7491 100644 --- a/src/pipeline/pipeline.cpp +++ b/src/pipeline/pipeline.cpp @@ -36,49 +36,6 @@ namespace kazan { namespace pipeline { -class Pipeline_cache -{ -}; - -void Api_object_deleter::operator()(Pipeline_cache *pipeline_cache) const noexcept -{ - delete pipeline_cache; -} - -class Render_pass -{ -}; - -void Api_object_deleter::operator()(Render_pass *render_pass) const noexcept -{ - delete render_pass; -} - -template <> -Render_pass_handle Render_pass_handle::make(const VkRenderPassCreateInfo &render_pass_create_info) -{ -#warning finish implementing Render_pass_handle::make - return Render_pass_handle(new Render_pass()); -} - -class Pipeline_layout -{ -}; - -void Api_object_deleter::operator()(Pipeline_layout *pipeline_layout) const - noexcept -{ - delete pipeline_layout; -} - -template <> -Pipeline_layout_handle Pipeline_layout_handle::make( - const VkPipelineLayoutCreateInfo &pipeline_layout_create_info) -{ -#warning finish implementing Pipeline_layout_handle::make - return Pipeline_layout_handle(new Pipeline_layout()); -} - llvm_wrapper::Module Pipeline::optimize_module(llvm_wrapper::Module module, ::LLVMTargetMachineRef target_machine) { @@ -412,14 +369,14 @@ void Graphics_pipeline::dump_vertex_shader_output_struct(const void *output_stru void Graphics_pipeline::run(std::uint32_t vertex_start_index, std::uint32_t vertex_end_index, std::uint32_t instance_id, - const image::Image &color_attachment, + const vulkan::Vulkan_image &color_attachment, void *const *bindings) { typedef std::uint32_t Pixel_type; assert(color_attachment.descriptor.tiling == VK_IMAGE_TILING_LINEAR); std::size_t color_attachment_stride = color_attachment.descriptor.get_memory_stride(); std::size_t color_attachment_pixel_size = color_attachment.descriptor.get_memory_pixel_size(); - unsigned char *color_attachment_memory = color_attachment.memory.get(); + void *color_attachment_memory = color_attachment.memory.get(); float viewport_x_scale, viewport_x_offset, viewport_y_scale, viewport_y_offset, viewport_z_scale, viewport_z_offset; { @@ -866,7 +823,7 @@ void Graphics_pipeline::run(std::uint32_t vertex_start_index, if(inside) { auto *pixel = reinterpret_cast( - color_attachment_memory + static_cast(color_attachment_memory) + (static_cast(x) * color_attachment_pixel_size + static_cast(y) * color_attachment_stride)); fs(pixel); @@ -881,9 +838,9 @@ std::unique_ptr Graphics_pipeline::make( Pipeline_cache *pipeline_cache, const VkGraphicsPipelineCreateInfo &create_info) { assert(create_info.sType == VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO); - auto *render_pass = Render_pass_handle::from_handle(create_info.renderPass); + auto *render_pass = Render_pass::from_handle(create_info.renderPass); assert(render_pass); - auto *pipeline_layout = Pipeline_layout_handle::from_handle(create_info.layout); + auto *pipeline_layout = Pipeline_layout::from_handle(create_info.layout); assert(pipeline_layout); if(create_info.flags & VK_PIPELINE_CREATE_DERIVATIVE_BIT) { @@ -909,7 +866,7 @@ std::unique_ptr Graphics_pipeline::make( std::get<1>(found_shader_stages.insert(execution_model)); if(!added_to_found_shader_stages) throw std::runtime_error("duplicate shader stage"); - auto *shader_module = Shader_module_handle::from_handle(stage_info.module); + auto *shader_module = Shader_module::from_handle(stage_info.module); assert(shader_module); { spirv::Dump_callbacks dump_callbacks; diff --git a/src/pipeline/pipeline.h b/src/pipeline/pipeline.h index b355456..fc47b7a 100644 --- a/src/pipeline/pipeline.h +++ b/src/pipeline/pipeline.h @@ -32,89 +32,49 @@ #include "vulkan/vulkan.h" #include "vulkan/remove_xlib_macros.h" #include "spirv/spirv.h" -#include "image/image.h" +#include "vulkan/api_objects.h" namespace kazan { namespace pipeline { -template -struct Api_object_deleter +class Pipeline_cache : public vulkan::Vulkan_nondispatchable_object { - void operator()(T *) const noexcept = delete; - typedef void Vulkan_handle; - typedef void Create_info; -}; - -template -struct Api_object_handle : public std::unique_ptr> -{ - typedef typename Api_object_deleter::Vulkan_handle Vulkan_handle; - typedef typename Api_object_deleter::Create_info Create_info; - typedef Object_ Object; - using std::unique_ptr>::unique_ptr; - static Object *from_handle(Vulkan_handle vulkan_handle) noexcept - { - return reinterpret_cast(vulkan_handle); - } - static Api_object_handle move_from_handle(Vulkan_handle vulkan_handle) noexcept +#warning finish implementing Pipeline_cache +public: + static std::unique_ptr make( + const VkPipelineCacheCreateInfo &pipeline_cache_create_info) { - return Api_object_handle(from_handle(vulkan_handle)); +#warning finish implementing Pipeline_cache::make + return std::make_unique(); } - static Api_object_handle make(const Create_info &create_info); }; -template -inline typename Api_object_deleter::Vulkan_handle to_handle(Object *object) noexcept -{ - static_assert(!std::is_void::Vulkan_handle>::value, ""); - return reinterpret_cast::Vulkan_handle>(object); -} - -template -inline typename Api_object_deleter::Vulkan_handle move_to_handle( - Api_object_handle object) noexcept -{ - return to_handle(object.release()); -} - -class Pipeline_cache; - -template <> -struct Api_object_deleter +class Render_pass : public vulkan::Vulkan_nondispatchable_object { - void operator()(Pipeline_cache *pipeline_cache) const noexcept; - typedef VkPipelineCache Vulkan_handle; - typedef VkPipelineCacheCreateInfo Create_info; -}; - -typedef Api_object_handle Pipeline_cache_handle; - -class Render_pass; - -template <> -struct Api_object_deleter -{ - void operator()(Render_pass *render_pass) const noexcept; - typedef VkRenderPass Vulkan_handle; - typedef VkRenderPassCreateInfo Create_info; +#warning finish implementing Render_pass +public: + static std::unique_ptr make(const VkRenderPassCreateInfo &render_pass_create_info) + { +#warning finish implementing Render_pass::make + return std::make_unique(); + } }; -typedef Api_object_handle Render_pass_handle; - -class Pipeline_layout; - -template <> -struct Api_object_deleter +class Pipeline_layout + : public vulkan::Vulkan_nondispatchable_object { - void operator()(Pipeline_layout *pipeline_layout) const noexcept; - typedef VkPipelineLayout Vulkan_handle; - typedef VkPipelineLayoutCreateInfo Create_info; +#warning finish implementing Pipeline_layout +public: + static std::unique_ptr make( + const VkPipelineLayoutCreateInfo &pipeline_layout_create_info) + { +#warning finish implementing Pipeline_layout::make + return std::make_unique(); + } }; -typedef Api_object_handle Pipeline_layout_handle; - -struct Shader_module +struct Shader_module : public vulkan::Vulkan_nondispatchable_object { std::shared_ptr bytes; std::size_t byte_count; @@ -132,38 +92,23 @@ struct Shader_module assert(byte_count % sizeof(spirv::Word) == 0); return byte_count / sizeof(spirv::Word); } -}; - -template <> -struct Api_object_deleter -{ - void operator()(Shader_module *shader_module) const noexcept + static std::unique_ptr make(const VkShaderModuleCreateInfo &create_info) { - delete shader_module; + struct Code_deleter + { + void operator()(unsigned char *bytes) const noexcept + { + delete[] bytes; + } + }; + auto bytes = + std::shared_ptr(new unsigned char[create_info.codeSize], Code_deleter{}); + std::memcpy(bytes.get(), create_info.pCode, create_info.codeSize); + return std::make_unique(std::move(bytes), create_info.codeSize); } - typedef VkShaderModule Vulkan_handle; - typedef VkShaderModuleCreateInfo Create_info; }; -typedef Api_object_handle Shader_module_handle; - -template <> -inline Shader_module_handle Shader_module_handle::make(const VkShaderModuleCreateInfo &create_info) -{ - struct Code_deleter - { - void operator()(unsigned char *bytes) const noexcept - { - delete[] bytes; - } - }; - auto bytes = - std::shared_ptr(new unsigned char[create_info.codeSize], Code_deleter{}); - std::memcpy(bytes.get(), create_info.pCode, create_info.codeSize); - return Shader_module_handle(new Shader_module(std::move(bytes), create_info.codeSize)); -} - -class Pipeline +class Pipeline : public vulkan::Vulkan_nondispatchable_object { Pipeline(const Pipeline &) = delete; Pipeline &operator=(const Pipeline &) = delete; @@ -187,16 +132,6 @@ protected: ::LLVMTargetMachineRef target_machine); }; -inline VkPipeline to_handle(Pipeline *pipeline) noexcept -{ - return reinterpret_cast(pipeline); -} - -inline VkPipeline move_to_handle(std::unique_ptr pipeline) noexcept -{ - return to_handle(pipeline.release()); -} - class Graphics_pipeline final : public Pipeline { private: @@ -233,7 +168,7 @@ public: void run(std::uint32_t vertex_start_index, std::uint32_t vertex_end_index, std::uint32_t instance_id, - const image::Image &color_attachment, + const vulkan::Vulkan_image &color_attachment, void *const *bindings); static std::unique_ptr make(Pipeline_cache *pipeline_cache, const VkGraphicsPipelineCreateInfo &create_info); @@ -275,16 +210,6 @@ private: VkViewport viewport; VkRect2D scissor_rect; }; - -inline VkPipeline to_handle(Graphics_pipeline *pipeline) noexcept -{ - return to_handle(static_cast(pipeline)); -} - -inline VkPipeline move_to_handle(std::unique_ptr pipeline) noexcept -{ - return to_handle(pipeline.release()); -} } } diff --git a/src/util/circular_queue.h b/src/util/circular_queue.h new file mode 100644 index 0000000..d3b8119 --- /dev/null +++ b/src/util/circular_queue.h @@ -0,0 +1,182 @@ +/* + * Copyright 2017 Jacob Lifshay + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#ifndef UTIL_CIRCULAR_QUEUE_H_ +#define UTIL_CIRCULAR_QUEUE_H_ + +#include +#include +#include +#include + +namespace kazan +{ +namespace util +{ +template +class Static_circular_deque +{ + static_assert(Capacity != 0, ""); + static_assert(std::is_nothrow_destructible::value, ""); + +private: + union + { + T objects[Capacity]; + alignas(T) char bytes[sizeof(T) * Capacity]; + }; + std::size_t front_index = 0; + std::size_t back_index = Capacity - 1; + std::size_t enqueued_count = 0; + static constexpr std::size_t prev_index(std::size_t index) noexcept + { + return index == 0 ? Capacity - 1 : index - 1; + } + static constexpr std::size_t next_index(std::size_t index) noexcept + { + return index == Capacity - 1 ? 0 : index + 1; + } + +public: + constexpr Static_circular_deque() noexcept : bytes{} + { + } + ~Static_circular_deque() + { + while(!empty()) + pop_back(); + } + Static_circular_deque(Static_circular_deque &&rt) noexcept( + std::is_nothrow_move_constructible::value) + : Static_circular_deque() + { + try + { + while(!rt.empty()) + { + push_back(std::move(rt.front())); + rt.pop_front(); + } + } + catch(...) + { + while(!empty()) + pop_back(); + throw; + } + } + Static_circular_deque &operator=(Static_circular_deque &&rt) noexcept( + std::is_nothrow_move_constructible::value) + { + if(this == &rt) + return *this; + while(!empty()) + pop_back(); + while(!rt.empty()) + { + push_back(std::move(rt.front())); + rt.pop_front(); + } + return *this; + } + std::size_t size() const noexcept + { + return enqueued_count; + } + std::size_t capacity() const noexcept + { + return Capacity; + } + bool empty() const noexcept + { + return enqueued_count == 0; + } + bool full() const noexcept + { + return enqueued_count == Capacity; + } + T &front() noexcept + { + assert(!empty()); + return objects[front_index]; + } + T &back() noexcept + { + assert(!empty()); + return objects[back_index]; + } + void pop_back() noexcept + { + assert(!empty()); + std::size_t new_index = prev_index(back_index); + objects[back_index].~T(); + enqueued_count--; + back_index = new_index; + } + void pop_front() noexcept + { + assert(!empty()); + std::size_t new_index = next_index(front_index); + objects[front_index].~T(); + enqueued_count--; + front_index = new_index; + } + template + void emplace_back(Args &&... args) noexcept(std::is_nothrow_constructible::value) + { + assert(!full()); + std::size_t new_index = next_index(back_index); + ::new(std::addressof(objects[new_index])) T(std::forward(args)...); + enqueued_count++; + back_index = new_index; + } + template + void emplace_front(Args &&... args) noexcept(std::is_nothrow_constructible::value) + { + assert(!full()); + std::size_t new_index = prev_index(front_index); + ::new(std::addressof(objects[new_index])) T(std::forward(args)...); + enqueued_count++; + front_index = new_index; + } + void push_back(const T &new_value) noexcept(std::is_nothrow_copy_constructible::value) + { + emplace_back(new_value); + } + void push_back(T &&new_value) noexcept(std::is_nothrow_move_constructible::value) + { + emplace_back(std::move(new_value)); + } + void push_front(const T &new_value) noexcept(std::is_nothrow_copy_constructible::value) + { + emplace_front(new_value); + } + void push_front(T &&new_value) noexcept(std::is_nothrow_move_constructible::value) + { + emplace_front(std::move(new_value)); + } +}; +} +} + +#endif + diff --git a/src/vulkan/api_objects.cpp b/src/vulkan/api_objects.cpp index 6591b3d..9549795 100644 --- a/src/vulkan/api_objects.cpp +++ b/src/vulkan/api_objects.cpp @@ -459,25 +459,125 @@ std::unique_ptr Vulkan_fence::create(Vulkan_device &device, return std::make_unique(create_info.flags); } +void Vulkan_image::clear(VkClearColorValue color) noexcept +{ + assert(memory); + assert(descriptor.samples == VK_SAMPLE_COUNT_1_BIT && "multisample images are unimplemented"); + assert(descriptor.extent.width > 0); + assert(descriptor.extent.height > 0); + assert(descriptor.extent.depth > 0); + + assert(descriptor.type == VK_IMAGE_TYPE_2D && "unimplemented image type"); + assert(descriptor.extent.depth == 1); + + assert(descriptor.format == VK_FORMAT_B8G8R8A8_UNORM && "unimplemented image format"); + assert(descriptor.mip_levels == 1 && "mipmapping is unimplemented"); + assert(descriptor.array_layers == 1 && "array images are unimplemented"); + assert(descriptor.tiling == VK_IMAGE_TILING_LINEAR + && "non-linear image tiling is unimplemented"); + union + { + std::uint8_t bytes[4]; + std::uint32_t u32; + } clear_color; + float r_float = color.float32[0]; + float g_float = color.float32[1]; + float b_float = color.float32[2]; + float a_float = color.float32[3]; + auto float_to_byte = [](float v) noexcept->std::uint8_t + { + if(!(v >= 0)) + v = 0; + else if(v > 1) + v = 1; + union + { + std::uint32_t i; + float f; + } u; + static_assert(sizeof(std::uint32_t) == sizeof(float), ""); + u.f = 0x100; + u.i--; // u.f = nextafter(u.f, -1) + v *= u.f; + return (int)v; + }; + clear_color.bytes[0] = float_to_byte(b_float); + clear_color.bytes[1] = float_to_byte(g_float); + clear_color.bytes[2] = float_to_byte(r_float); + clear_color.bytes[3] = float_to_byte(a_float); + std::size_t pixel_count = + static_cast(descriptor.extent.width) * descriptor.extent.height; + std::uint32_t *pixels = static_cast(memory.get()); + for(std::size_t i = 0; i < pixel_count; i++) + { + pixels[i] = clear_color.u32; + } +} + std::unique_ptr Vulkan_image::create(Vulkan_device &device, const VkImageCreateInfo &create_info) { #warning finish implementing Vulkan_image::create - return std::make_unique(); + return std::make_unique(Vulkan_image_descriptor(create_info)); } +void Vulkan_command_buffer::Command::on_record_end(Vulkan_command_buffer &command_buffer) +{ + static_cast(command_buffer); +} + + Vulkan_command_buffer::Vulkan_command_buffer( std::list>::iterator iter, Vulkan_command_pool &command_pool, Vulkan_device &device) noexcept : iter(iter), command_pool(command_pool), - device(device) + device(device), + commands(), + state(Command_buffer_state::Initial) +{ +} + +void Vulkan_command_buffer::reset(VkCommandBufferResetFlags flags) +{ + commands.clear(); + state = Command_buffer_state::Initial; +} + +void Vulkan_command_buffer::begin(const VkCommandBufferBeginInfo &begin_info) +{ + commands.clear(); + state = Command_buffer_state::Recording; +} + +VkResult Vulkan_command_buffer::end() noexcept { + if(state == Command_buffer_state::Out_of_memory) + return VK_ERROR_OUT_OF_HOST_MEMORY; + assert(state == Command_buffer_state::Recording); + try + { + for(auto &command : commands) + { + assert(command); + command->on_record_end(*this); + } + } + catch(std::bad_alloc &) + { + state = Command_buffer_state::Out_of_memory; + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + state = Command_buffer_state::Executable; + return VK_SUCCESS; } -void Vulkan_command_buffer::reset(VkCommandPoolResetFlags flags) +void Vulkan_command_buffer::run() const noexcept { -#warning finish implementing Vulkan_command_buffer::reset + assert(state == Command_buffer_state::Executable); + Running_state running_state(*this); + for(auto &command : commands) + command->run(running_state); } void Vulkan_command_pool::allocate_multiple(Vulkan_device &device, diff --git a/src/vulkan/api_objects.h b/src/vulkan/api_objects.h index c35ed35..68b949f 100644 --- a/src/vulkan/api_objects.h +++ b/src/vulkan/api_objects.h @@ -1570,6 +1570,9 @@ struct Vulkan_device : public Vulkan_dispatchable_object { + void signal() // empty function for if semaphores are needed later + { + } static std::unique_ptr create(Vulkan_device &device, const VkSemaphoreCreateInfo &create_info); }; @@ -1655,8 +1658,153 @@ public: const VkFenceCreateInfo &create_info); }; +struct Vulkan_image_descriptor +{ + static constexpr VkImageCreateFlags supported_flags = + VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + VkImageCreateFlags flags; + VkImageType type; + VkFormat format; + VkExtent3D extent; + std::uint32_t mip_levels; + std::uint32_t array_layers; + static constexpr VkSampleCountFlags supported_samples = VK_SAMPLE_COUNT_1_BIT; + VkSampleCountFlagBits samples; + VkImageTiling tiling; + constexpr Vulkan_image_descriptor() noexcept : flags(), + type(), + format(), + extent(), + mip_levels(), + array_layers(), + samples(), + tiling() + { + } + constexpr explicit Vulkan_image_descriptor(const VkImageCreateInfo &image_create_info) noexcept + : flags(image_create_info.flags), + type(image_create_info.imageType), + format(image_create_info.format), + extent(image_create_info.extent), + mip_levels(image_create_info.mipLevels), + array_layers(image_create_info.arrayLayers), + samples(image_create_info.samples), + tiling(image_create_info.tiling) + { + assert(image_create_info.sType == VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO); + assert((flags & ~supported_flags) == 0); + assert((samples & ~supported_samples) == 0); + assert(extent.width > 0); + assert(extent.height > 0); + assert(extent.depth > 0); + +#warning finish implementing Image + assert(type == VK_IMAGE_TYPE_2D && "unimplemented image type"); + assert(extent.depth == 1); + + assert(format == VK_FORMAT_B8G8R8A8_UNORM && "unimplemented image format"); + assert(mip_levels == 1 && "mipmapping is unimplemented"); + assert(array_layers == 1 && "array images are unimplemented"); + assert(tiling == VK_IMAGE_TILING_LINEAR && "non-linear image tiling is unimplemented"); + assert(image_create_info.initialLayout == VK_IMAGE_LAYOUT_UNDEFINED + && "preinitialized images are unimplemented"); + } + constexpr Vulkan_image_descriptor(VkImageCreateFlags flags, + VkImageType type, + VkFormat format, + VkExtent3D extent, + std::uint32_t mip_levels, + std::uint32_t array_layers, + VkSampleCountFlagBits samples, + VkImageTiling tiling) noexcept : flags(flags), + type(type), + format(format), + extent(extent), + mip_levels(mip_levels), + array_layers(array_layers), + samples(samples), + tiling(tiling) + { + } + constexpr std::size_t get_memory_size() const noexcept + { +#warning finish implementing Image + assert(samples == VK_SAMPLE_COUNT_1_BIT && "multisample images are unimplemented"); + assert(extent.width > 0); + assert(extent.height > 0); + assert(extent.depth > 0); + + assert(type == VK_IMAGE_TYPE_2D && "unimplemented image type"); + assert(extent.depth == 1); + + assert(format == VK_FORMAT_B8G8R8A8_UNORM && "unimplemented image format"); + assert(mip_levels == 1 && "mipmapping is unimplemented"); + assert(array_layers == 1 && "array images are unimplemented"); + assert(tiling == VK_IMAGE_TILING_LINEAR && "non-linear image tiling is unimplemented"); + std::size_t retval = sizeof(std::uint32_t); + retval *= extent.width; + retval *= extent.height; + return retval; + } + constexpr std::size_t get_memory_stride() const noexcept + { +#warning finish implementing Image + assert(samples == VK_SAMPLE_COUNT_1_BIT && "multisample images are unimplemented"); + assert(extent.width > 0); + assert(extent.height > 0); + assert(extent.depth > 0); + + assert(type == VK_IMAGE_TYPE_2D && "unimplemented image type"); + assert(extent.depth == 1); + + assert(format == VK_FORMAT_B8G8R8A8_UNORM && "unimplemented image format"); + assert(mip_levels == 1 && "mipmapping is unimplemented"); + assert(array_layers == 1 && "array images are unimplemented"); + assert(tiling == VK_IMAGE_TILING_LINEAR && "non-linear image tiling is unimplemented"); + std::size_t retval = sizeof(std::uint32_t); + retval *= extent.width; + return retval; + } + constexpr std::size_t get_memory_pixel_size() const noexcept + { +#warning finish implementing Image + assert(samples == VK_SAMPLE_COUNT_1_BIT && "multisample images are unimplemented"); + assert(extent.width > 0); + assert(extent.height > 0); + assert(extent.depth > 0); + + assert(type == VK_IMAGE_TYPE_2D && "unimplemented image type"); + assert(extent.depth == 1); + + assert(format == VK_FORMAT_B8G8R8A8_UNORM && "unimplemented image format"); + assert(mip_levels == 1 && "mipmapping is unimplemented"); + assert(array_layers == 1 && "array images are unimplemented"); + assert(tiling == VK_IMAGE_TILING_LINEAR && "non-linear image tiling is unimplemented"); + std::size_t retval = sizeof(std::uint32_t); + return retval; + } +}; + struct Vulkan_image : public Vulkan_nondispatchable_object { + const Vulkan_image_descriptor descriptor; + std::shared_ptr memory; + Vulkan_image(const Vulkan_image_descriptor &descriptor, + std::shared_ptr memory = nullptr) noexcept : descriptor(descriptor), + memory(std::move(memory)) + { + } + static std::unique_ptr create_with_memory( + const Vulkan_image_descriptor &descriptor) + { + std::shared_ptr memory(new unsigned char[descriptor.get_memory_size()], + [](unsigned char *p) noexcept + { + delete[] p; + }); + return std::make_unique(descriptor, std::move(memory)); + } + void clear(VkClearColorValue color) noexcept; virtual ~Vulkan_image() = default; #warning finish implementing Vulkan_image static std::unique_ptr create(Vulkan_device &device, @@ -1668,14 +1816,58 @@ struct Vulkan_command_pool; struct Vulkan_command_buffer : public Vulkan_dispatchable_object { + struct Running_state + { + const Vulkan_command_buffer &command_buffer; + Vulkan_device &device; + explicit Running_state(const Vulkan_command_buffer &command_buffer) noexcept + : command_buffer(command_buffer), + device(command_buffer.device) + { + } +#warning finish implementing Vulkan_command_buffer + }; + class Command + { + public: + virtual ~Command() = default; + virtual void run(Running_state &state) noexcept = 0; + virtual void on_record_end(Vulkan_command_buffer &command_buffer); + }; + enum class Command_buffer_state + { + Initial, + Recording, + Executable, + Out_of_memory, + }; std::list>::iterator iter; Vulkan_command_pool &command_pool; Vulkan_device &device; + std::vector> commands; + Command_buffer_state state; Vulkan_command_buffer(std::list>::iterator iter, Vulkan_command_pool &command_pool, Vulkan_device &device) noexcept; - void reset(VkCommandPoolResetFlags flags); -#warning finish implementing Vulkan_command_buffer + void reset(VkCommandBufferResetFlags flags); + void begin(const VkCommandBufferBeginInfo &begin_info); + template + void record_command_and_keep_errors(Fn fn) noexcept + { + if(state == Command_buffer_state::Out_of_memory) + return; + assert(state == Command_buffer_state::Recording); + try + { + fn(); + } + catch(std::bad_alloc &) + { + state = Command_buffer_state::Out_of_memory; + } + } + VkResult end() noexcept; + void run() const noexcept; }; struct Vulkan_command_pool @@ -1684,8 +1876,12 @@ struct Vulkan_command_pool std::list> command_buffers; void reset(VkCommandPoolResetFlags flags) { + VkCommandBufferResetFlags buffer_flags = 0; + assert((flags & ~(VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT)) == 0); + if(flags & VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT) + buffer_flags |= VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT; for(auto &command_buffer : command_buffers) - command_buffer->reset(flags); + command_buffer->reset(buffer_flags); } void allocate_multiple(Vulkan_device &device, const VkCommandBufferAllocateInfo &allocate_info, diff --git a/src/vulkan_icd/CMakeLists.txt b/src/vulkan_icd/CMakeLists.txt index 8b7dd20..1f30081 100644 --- a/src/vulkan_icd/CMakeLists.txt +++ b/src/vulkan_icd/CMakeLists.txt @@ -24,6 +24,7 @@ set(sources vulkan_icd.cpp x11_wsi.cpp) add_library(kazan_vulkan_icd MODULE ${sources}) target_link_libraries(kazan_vulkan_icd + kazan_pipeline kazan_vulkan kazan_util) file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/kazan_vulkan_icd.json INPUT ${CMAKE_CURRENT_SOURCE_DIR}/kazan_vulkan_icd.json.in) diff --git a/src/vulkan_icd/vulkan_icd.cpp b/src/vulkan_icd/vulkan_icd.cpp index aa6363b..df682ce 100644 --- a/src/vulkan_icd/vulkan_icd.cpp +++ b/src/vulkan_icd/vulkan_icd.cpp @@ -24,7 +24,9 @@ #include "util/string_view.h" #include #include +#include #include "wsi.h" +#include "pipeline/pipeline.h" using namespace kazan; @@ -983,23 +985,35 @@ extern "C" VKAPI_ATTR void VKAPI_CALL vkFreeCommandBuffers(VkDevice device, } extern "C" VKAPI_ATTR VkResult VKAPI_CALL - vkBeginCommandBuffer(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo *pBeginInfo) + vkBeginCommandBuffer(VkCommandBuffer command_buffer, const VkCommandBufferBeginInfo *begin_info) { -#warning finish implementing vkBeginCommandBuffer - assert(!"vkBeginCommandBuffer is not implemented"); + assert(command_buffer); + assert(begin_info); + assert(begin_info->sType == VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO); + return vulkan_icd::catch_exceptions_and_return_result( + [&]() + { + vulkan::Vulkan_command_buffer::from_handle(command_buffer)->begin(*begin_info); + return VK_SUCCESS; + }); } -extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkEndCommandBuffer(VkCommandBuffer commandBuffer) +extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkEndCommandBuffer(VkCommandBuffer command_buffer) { -#warning finish implementing vkEndCommandBuffer - assert(!"vkEndCommandBuffer is not implemented"); + assert(command_buffer); + return vulkan::Vulkan_command_buffer::from_handle(command_buffer)->end(); } -extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkResetCommandBuffer(VkCommandBuffer commandBuffer, +extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkResetCommandBuffer(VkCommandBuffer command_buffer, VkCommandBufferResetFlags flags) { -#warning finish implementing vkResetCommandBuffer - assert(!"vkResetCommandBuffer is not implemented"); + assert(command_buffer); + return vulkan_icd::catch_exceptions_and_return_result( + [&]() + { + vulkan::Vulkan_command_buffer::from_handle(command_buffer)->reset(flags); + return VK_SUCCESS; + }); } extern "C" VKAPI_ATTR void VKAPI_CALL vkCmdBindPipeline(VkCommandBuffer commandBuffer, @@ -1251,15 +1265,61 @@ extern "C" VKAPI_ATTR void VKAPI_CALL vkCmdFillBuffer(VkCommandBuffer commandBuf assert(!"vkCmdFillBuffer is not implemented"); } -extern "C" VKAPI_ATTR void VKAPI_CALL vkCmdClearColorImage(VkCommandBuffer commandBuffer, +extern "C" VKAPI_ATTR void VKAPI_CALL vkCmdClearColorImage(VkCommandBuffer command_buffer, VkImage image, - VkImageLayout imageLayout, - const VkClearColorValue *pColor, - uint32_t rangeCount, - const VkImageSubresourceRange *pRanges) -{ -#warning finish implementing vkCmdClearColorImage - assert(!"vkCmdClearColorImage is not implemented"); + VkImageLayout image_layout, + const VkClearColorValue *color, + uint32_t range_count, + const VkImageSubresourceRange *ranges) +{ + assert(command_buffer); + assert(image); + assert(image_layout == VK_IMAGE_LAYOUT_GENERAL + || image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + assert(color); + assert(range_count > 0); + assert(ranges); + auto command_buffer_pointer = vulkan::Vulkan_command_buffer::from_handle(command_buffer); + command_buffer_pointer->record_command_and_keep_errors( + [&]() + { + auto image_pointer = vulkan::Vulkan_image::from_handle(image); + assert(range_count == 1 + && "vkCmdClearColorImage with multiple subresource ranges is not implemented"); + for(std::uint32_t i = 0; i < range_count; i++) + { + auto &range = ranges[i]; + assert(range.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT); + assert(range.baseMipLevel == 0 + && (range.levelCount == image_pointer->descriptor.mip_levels + || range.levelCount == VK_REMAINING_MIP_LEVELS) + && "vkCmdClearColorImage with clearing only some of the mipmap levels is not implemented"); + assert(range.baseArrayLayer == 0 + && (range.layerCount == image_pointer->descriptor.array_layers + || range.layerCount == VK_REMAINING_ARRAY_LAYERS) + && "vkCmdClearColorImage with clearing only some of the array layers is not implemented"); + static_cast(range); + } +#warning finish implementing non-linear image layouts + struct Clear_command final : public vulkan::Vulkan_command_buffer::Command + { + VkClearColorValue clear_color; + vulkan::Vulkan_image *image; + Clear_command(const VkClearColorValue &clear_color, + vulkan::Vulkan_image *image) noexcept : clear_color(clear_color), + image(image) + { + } + virtual void run( + vulkan::Vulkan_command_buffer::Running_state &state) noexcept override + { + static_cast(state); + image->clear(clear_color); + } + }; + command_buffer_pointer->commands.push_back( + std::make_unique(*color, image_pointer)); + }); } extern "C" VKAPI_ATTR void VKAPI_CALL @@ -1330,19 +1390,68 @@ extern "C" VKAPI_ATTR void VKAPI_CALL } extern "C" VKAPI_ATTR void VKAPI_CALL - vkCmdPipelineBarrier(VkCommandBuffer commandBuffer, - VkPipelineStageFlags srcStageMask, - VkPipelineStageFlags dstStageMask, - VkDependencyFlags dependencyFlags, - uint32_t memoryBarrierCount, - const VkMemoryBarrier *pMemoryBarriers, - uint32_t bufferMemoryBarrierCount, - const VkBufferMemoryBarrier *pBufferMemoryBarriers, - uint32_t imageMemoryBarrierCount, - const VkImageMemoryBarrier *pImageMemoryBarriers) -{ -#warning finish implementing vkCmdPipelineBarrier - assert(!"vkCmdPipelineBarrier is not implemented"); + vkCmdPipelineBarrier(VkCommandBuffer command_buffer, + VkPipelineStageFlags src_stage_mask, + VkPipelineStageFlags dst_stage_mask, + VkDependencyFlags dependency_flags, + uint32_t memory_barrier_count, + const VkMemoryBarrier *memory_barriers, + uint32_t buffer_memory_barrier_count, + const VkBufferMemoryBarrier *buffer_memory_barriers, + uint32_t image_memory_barrier_count, + const VkImageMemoryBarrier *image_memory_barriers) +{ + assert(command_buffer); + assert(src_stage_mask != 0); + assert(dst_stage_mask != 0); + assert((dependency_flags & ~VK_DEPENDENCY_BY_REGION_BIT) == 0); + assert(memory_barrier_count == 0 || memory_barriers); + assert(buffer_memory_barrier_count == 0 || buffer_memory_barriers); + assert(image_memory_barrier_count == 0 || image_memory_barriers); + auto command_buffer_pointer = vulkan::Vulkan_command_buffer::from_handle(command_buffer); + command_buffer_pointer->record_command_and_keep_errors( + [&]() + { + bool any_memory_barriers = false; + for(std::uint32_t i = 0; i < memory_barrier_count; i++) + { + auto &memory_barrier = memory_barriers[i]; + assert(memory_barrier.sType == VK_STRUCTURE_TYPE_MEMORY_BARRIER); +#warning finish implementing vkCmdPipelineBarrier for VkMemoryBarrier + assert(!"vkCmdPipelineBarrier for VkMemoryBarrier is not implemented"); + any_memory_barriers = true; + } + for(std::uint32_t i = 0; i < buffer_memory_barrier_count; i++) + { + auto &buffer_memory_barrier = buffer_memory_barriers[i]; + assert(buffer_memory_barrier.sType == VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER); +#warning finish implementing vkCmdPipelineBarrier for VkBufferMemoryBarrier + assert(!"vkCmdPipelineBarrier for VkBufferMemoryBarrier is not implemented"); + any_memory_barriers = true; + } + for(std::uint32_t i = 0; i < image_memory_barrier_count; i++) + { + auto &image_memory_barrier = image_memory_barriers[i]; + assert(image_memory_barrier.sType == VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER); +#warning finish implementing non-linear image layouts + any_memory_barriers = true; + } + if(any_memory_barriers) + { + struct Generic_memory_barrier_command final + : public vulkan::Vulkan_command_buffer::Command + { + void run(kazan::vulkan::Vulkan_command_buffer::Running_state + &state) noexcept override + { + static_cast(state); + std::atomic_thread_fence(std::memory_order_acq_rel); + } + }; + command_buffer_pointer->commands.push_back( + std::make_unique()); + } + }); } extern "C" VKAPI_ATTR void VKAPI_CALL vkCmdBeginQuery(VkCommandBuffer commandBuffer, @@ -1643,10 +1752,21 @@ extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImageKHR(VkDevice device, uint64_t timeout, VkSemaphore semaphore, VkFence fence, - uint32_t *pImageIndex) + uint32_t *image_index) { -#warning finish implementing vkAcquireNextImageKHR - assert(!"vkAcquireNextImageKHR is not implemented"); + assert(device); + assert(swapchain); + assert(image_index); + return vulkan_icd::catch_exceptions_and_return_result( + [&]() + { + auto *swapchain_pointer = vulkan_icd::Vulkan_swapchain::from_handle(swapchain); + return swapchain_pointer->acquire_next_image( + timeout, + vulkan::Vulkan_semaphore::from_handle(semaphore), + vulkan::Vulkan_fence::from_handle(fence), + *image_index); + }); } extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkQueuePresentKHR(VkQueue queue, diff --git a/src/vulkan_icd/wsi.h b/src/vulkan_icd/wsi.h index 75645a2..ad69747 100644 --- a/src/vulkan_icd/wsi.h +++ b/src/vulkan_icd/wsi.h @@ -50,6 +50,10 @@ public: { } virtual ~Vulkan_swapchain() = default; + virtual VkResult acquire_next_image(std::uint64_t timeout, + vulkan::Vulkan_semaphore *semaphore, + vulkan::Vulkan_fence *fence, + std::uint32_t &returned_image_index) = 0; }; struct Wsi diff --git a/src/vulkan_icd/x11_wsi.cpp b/src/vulkan_icd/x11_wsi.cpp index c31f19a..9656ca4 100644 --- a/src/vulkan_icd/x11_wsi.cpp +++ b/src/vulkan_icd/x11_wsi.cpp @@ -37,7 +37,9 @@ #include #include #include +#include #include "util/optional.h" +#include "util/circular_queue.h" namespace kazan { @@ -45,6 +47,7 @@ namespace vulkan_icd { struct Xcb_wsi::Implementation { + static constexpr std::size_t max_swapchain_image_count = 16; static std::uint32_t u32_from_bytes(std::uint8_t b0, std::uint8_t b1, std::uint8_t b2, @@ -222,6 +225,7 @@ struct Xcb_wsi::Implementation std::size_t image_pixel_size; std::size_t scanline_alignment; xcb_shm_query_version_cookie_t shm_query_version_cookie; + vulkan::Vulkan_image_descriptor image_descriptor; Start_setup_results(Gc gc, bool shm_is_supported, unsigned window_depth, @@ -232,7 +236,8 @@ struct Xcb_wsi::Implementation const VkSurfaceCapabilitiesKHR &capabilities, std::size_t image_pixel_size, std::size_t scanline_alignment, - xcb_shm_query_version_cookie_t shm_query_version_cookie) noexcept + xcb_shm_query_version_cookie_t shm_query_version_cookie, + const vulkan::Vulkan_image_descriptor &image_descriptor) noexcept : status(Status::Success), gc(std::move(gc)), shm_is_supported(shm_is_supported), @@ -244,7 +249,8 @@ struct Xcb_wsi::Implementation capabilities(capabilities), image_pixel_size(image_pixel_size), scanline_alignment(scanline_alignment), - shm_query_version_cookie(shm_query_version_cookie) + shm_query_version_cookie(shm_query_version_cookie), + image_descriptor(image_descriptor) { } constexpr Start_setup_results(Status status) noexcept : status(status), @@ -258,7 +264,8 @@ struct Xcb_wsi::Implementation capabilities{}, image_pixel_size(), scanline_alignment(), - shm_query_version_cookie() + shm_query_version_cookie(), + image_descriptor() { assert(status != Status::Success); } @@ -399,7 +406,7 @@ struct Xcb_wsi::Implementation }; VkSurfaceCapabilitiesKHR capabilities = { .minImageCount = 2, - .maxImageCount = 0, + .maxImageCount = max_swapchain_image_count, .currentExtent = { .width = image_width, .height = image_height, @@ -433,46 +440,67 @@ struct Xcb_wsi::Implementation capabilities, image_pixel_size, scanline_alignment, - shm_query_version_cookie); + shm_query_version_cookie, + vulkan::Vulkan_image_descriptor( + 0, + VK_IMAGE_TYPE_2D, + VK_FORMAT_UNDEFINED, + VkExtent3D{ + .width = image_width, .height = image_height, .depth = 1, + }, + 1, + 1, + VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_TILING_OPTIMAL)); } struct Swapchain final : public Vulkan_swapchain { + enum class Image_owner + { + Swapchain, + Application, + Presentation_engine, + }; + enum class Status + { + Setup_failed, + No_surface, + Out_of_date, + Good, + }; struct Swapchain_image final : public vulkan::Vulkan_image { - std::shared_ptr pixels; - std::size_t row_pitch; - std::uint32_t width; - std::uint32_t height; - std::size_t pixel_size; Shared_memory_segment shared_memory_segment; Server_shm_seg server_shm_seg; Pixmap pixmap; - std::list::iterator iter; - Swapchain_image(std::shared_ptr pixels, - std::size_t row_pitch, - std::uint32_t width, - std::uint32_t height, - std::size_t pixel_size, + Image_owner owner; + xcb_get_geometry_cookie_t get_geometry_cookie{}; + Swapchain_image(const vulkan::Vulkan_image_descriptor &descriptor, + std::shared_ptr pixels, Shared_memory_segment shared_memory_segment, Server_shm_seg server_shm_seg, - Pixmap pixmap, - std::list::iterator iter) noexcept - : pixels(std::move(pixels)), - row_pitch(row_pitch), - width(width), - height(height), - pixel_size(pixel_size), + Pixmap pixmap) noexcept + : Vulkan_image(descriptor, std::move(pixels)), shared_memory_segment(std::move(shared_memory_segment)), server_shm_seg(std::move(server_shm_seg)), pixmap(std::move(pixmap)), - iter(iter) + owner(Image_owner::Swapchain) { } }; + Swapchain_image &get_image(std::size_t index) noexcept + { + assert(index < images.size()); + assert(dynamic_cast(images[index].get())); + return *static_cast(images[index].get()); + } xcb_connection_t *connection; xcb_window_t window; bool shm_is_supported; - std::list free_list; + Status status; + util::Static_circular_deque presenting_image_queue; + std::uint32_t swapchain_width; + std::uint32_t swapchain_height; explicit Swapchain(Start_setup_results start_setup_results, xcb_connection_t *connection, xcb_window_t window, @@ -480,7 +508,9 @@ struct Xcb_wsi::Implementation : Vulkan_swapchain({}), connection(connection), window(window), - shm_is_supported(start_setup_results.shm_is_supported) + shm_is_supported(start_setup_results.shm_is_supported), + status(Status::Good), + presenting_image_queue() { assert(create_info.sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR); #warning formats other than VK_FORMAT_B8G8R8A8_UNORM are unimplemented @@ -497,6 +527,18 @@ struct Xcb_wsi::Implementation assert((create_info.compositeAlpha & ~start_setup_results.capabilities.supportedCompositeAlpha) == 0); + switch(start_setup_results.status) + { + case Start_setup_results::Status::Bad_surface: + case Start_setup_results::Status::No_support: + status = Status::Setup_failed; + return; + case Start_setup_results::Status::Success: + break; + } + start_setup_results.image_descriptor.format = create_info.imageFormat; + swapchain_width = start_setup_results.image_width; + swapchain_height = start_setup_results.image_height; const char *warning_message_present_mode_name = nullptr; switch(create_info.presentMode) { @@ -593,32 +635,93 @@ struct Xcb_wsi::Implementation delete[] p; }); } - auto image_iter = free_list.insert(free_list.end(), nullptr); - auto image = - std::make_unique(std::move(pixels), - padded_scanline_size, - start_setup_results.image_width, - start_setup_results.image_height, - start_setup_results.image_pixel_size, + images.push_back( + std::make_unique(start_setup_results.image_descriptor, + std::move(pixels), std::move(shared_memory_segment), std::move(server_shm_seg), - std::move(pixmap), - image_iter); - *image_iter = image.get(); - images.push_back(std::move(image)); + std::move(pixmap))); } if(shm_failed) { std::cerr << "using shared memory failed, falling back to using core X protocol" << std::endl; shm_is_supported = false; - free_list.clear(); images.clear(); continue; } break; } } + virtual VkResult acquire_next_image(std::uint64_t timeout, + vulkan::Vulkan_semaphore *semaphore, + vulkan::Vulkan_fence *fence, + std::uint32_t &returned_image_index) override + { +#warning figure out how to use timeouts with xcb blocking for X server responses + switch(status) + { + case Status::No_surface: + case Status::Setup_failed: + return VK_ERROR_SURFACE_LOST_KHR; + case Status::Out_of_date: + return VK_ERROR_OUT_OF_DATE_KHR; + case Status::Good: + break; + } + while(true) + { + for(std::size_t i = 0; i < images.size(); i++) + { + auto &image = get_image(i); + if(image.owner == Image_owner::Swapchain) + { + image.owner = Image_owner::Application; + returned_image_index = i; + if(semaphore) + semaphore->signal(); + if(fence) + fence->signal(); + return VK_SUCCESS; + } + } + if(presenting_image_queue.empty()) + { + std::cerr << "vkAcquireNextImageKHR called when application has already " + "acquired all swapchain images; aborting" + << std::endl; + std::abort(); + } + assert(shm_is_supported); + std::size_t image_index = presenting_image_queue.front(); + presenting_image_queue.pop_front(); + auto &image = get_image(image_index); + // wait for the presentation request to finish + // we use a xcb_get_geometry command after the xcb_copy_area command, so we can wait + // on the xcb_get_geometry command since the X server processes commands in order + auto get_geometry_reply = Get_geometry_reply( + xcb_get_geometry_reply(connection, image.get_geometry_cookie, nullptr)); + image.owner = Image_owner::Swapchain; + if(!get_geometry_reply) + { + status = Status::No_surface; + return VK_ERROR_SURFACE_LOST_KHR; + } + if(get_geometry_reply->width != swapchain_width + || get_geometry_reply->height != swapchain_height) + { + status = Status::Out_of_date; + return VK_ERROR_OUT_OF_DATE_KHR; + } + image.owner = Image_owner::Application; + returned_image_index = image_index; + if(semaphore) + semaphore->signal(); + if(fence) + fence->signal(); + return VK_SUCCESS; + } + } }; }; @@ -741,13 +844,23 @@ VkResult Xcb_wsi::get_surface_capabilities(VkIcdSurfaceBase *surface_, util::variant> Xcb_wsi::create_swapchain( vulkan::Vulkan_device &device, const VkSwapchainCreateInfoKHR &create_info) const { -#warning finish implementing Xcb_wsi::create_swapchain auto &surface = *reinterpret_cast(create_info.surface); - return std::make_unique( + auto swapchain = std::make_unique( Implementation::start_setup(surface.connection, surface.window, true), surface.connection, surface.window, create_info); + switch(swapchain->status) + { + case Implementation::Swapchain::Status::Setup_failed: + case Implementation::Swapchain::Status::Out_of_date: + case Implementation::Swapchain::Status::No_surface: + return VK_ERROR_SURFACE_LOST_KHR; + case Implementation::Swapchain::Status::Good: + return swapchain; + } + assert(!"unreachable"); + return {}; } const Xcb_wsi &Xcb_wsi::get() noexcept -- 2.30.2