From 6059db00c23ff915cdaadc4d78b767820925f8a6 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 22 Aug 2017 01:57:19 -0700 Subject: [PATCH] implemented getting vertex positions and clipping triangles --- src/pipeline/CMakeLists.txt | 8 +- src/pipeline/pipeline.cpp | 313 +++++++++++++++++++++++++++++++++++- src/pipeline/pipeline.h | 19 ++- 3 files changed, 333 insertions(+), 7 deletions(-) diff --git a/src/pipeline/CMakeLists.txt b/src/pipeline/CMakeLists.txt index c091aa9..1b73327 100644 --- a/src/pipeline/CMakeLists.txt +++ b/src/pipeline/CMakeLists.txt @@ -22,4 +22,10 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) set(sources pipeline.cpp) add_library(vulkan_cpu_pipeline STATIC ${sources}) -target_link_libraries(vulkan_cpu_pipeline vulkan_cpu_spirv_to_llvm vulkan_cpu_json vulkan_cpu_util vulkan_cpu_spirv vulkan_cpu_llvm_wrapper vulkan_cpu_vulkan vulkan_cpu_image) +target_link_libraries(vulkan_cpu_pipeline vulkan_cpu_spirv_to_llvm + vulkan_cpu_json + vulkan_cpu_util + vulkan_cpu_spirv + vulkan_cpu_llvm_wrapper + vulkan_cpu_vulkan + vulkan_cpu_image) diff --git a/src/pipeline/pipeline.cpp b/src/pipeline/pipeline.cpp index cbe9ba2..de12963 100644 --- a/src/pipeline/pipeline.cpp +++ b/src/pipeline/pipeline.cpp @@ -406,6 +406,238 @@ void Graphics_pipeline::dump_vertex_shader_output_struct(const void *output_stru << std::endl; } +void Graphics_pipeline::run(std::uint32_t vertex_start_index, + std::uint32_t vertex_end_index, + std::uint32_t instance_id, + image::Image &color_attachment) +{ + constexpr std::size_t vec4_native_alignment = alignof(float) * 4; + constexpr std::size_t max_alignment = alignof(std::max_align_t); + constexpr std::size_t vec4_alignment = + vec4_native_alignment > max_alignment ? max_alignment : vec4_native_alignment; + struct alignas(vec4_alignment) Vec4 + { + float x; + float y; + float z; + float w; + constexpr Vec4() noexcept : x(), y(), z(), w() + { + } + constexpr explicit Vec4(float x, float y, float z, float w) noexcept : x(x), + y(y), + z(z), + w(w) + { + } + }; + auto interpolate_float = [](float t, float v0, float v1) noexcept->float + { + return t * v1 + (1.0f - t) * v0; + }; + auto interpolate_vec4 = [interpolate_float]( + float t, const Vec4 &v0, const Vec4 &v1) noexcept->Vec4 + { + return Vec4(interpolate_float(t, v0.x, v1.x), + interpolate_float(t, v0.y, v1.y), + interpolate_float(t, v0.z, v1.z), + interpolate_float(t, v0.w, v1.w)); + }; + static constexpr std::size_t triangle_vertex_count = 3; + struct Triangle + { + Vec4 vertexes[triangle_vertex_count]; + constexpr Triangle() noexcept : vertexes{} + { + } + constexpr Triangle(const Vec4 &v0, const Vec4 &v1, const Vec4 &v2) noexcept + : vertexes{v0, v1, v2} + { + } + }; + auto solve_for_t = [](float v0, float v1) noexcept->float + { + // solves interpolate(t, v0, v1) == 0 + return v0 / (v0 - v1); + }; + auto clip_edge = [solve_for_t, interpolate_vec4](const Vec4 &start_vertex, + const Vec4 &end_vertex, + Vec4 *output_vertexes, + std::size_t &output_vertex_count, + auto eval_vertex) -> bool + { + // eval_vertex returns a non-negative number if the vertex is inside the clip volume + float start_vertex_signed_distance = eval_vertex(start_vertex); + float end_vertex_signed_distance = eval_vertex(end_vertex); + if(start_vertex_signed_distance != start_vertex_signed_distance) + return false; // triangle has a NaN coordinate; skip it + if(start_vertex_signed_distance < 0) + { + // start_vertex is outside + if(end_vertex_signed_distance < 0) + { + // end_vertex is outside; do nothing + } + else + { + // end_vertex is inside + output_vertexes[output_vertex_count++] = interpolate_vec4( + solve_for_t(start_vertex_signed_distance, end_vertex_signed_distance), + start_vertex, + end_vertex); + output_vertexes[output_vertex_count++] = end_vertex; + } + } + else + { + // start_vertex is inside + if(end_vertex_signed_distance < 0) + { + // end_vertex is outside + output_vertexes[output_vertex_count++] = interpolate_vec4( + solve_for_t(start_vertex_signed_distance, end_vertex_signed_distance), + start_vertex, + end_vertex); + } + else + { + // end_vertex is inside + output_vertexes[output_vertex_count++] = end_vertex; + } + } + return true; + }; + auto clip_triangles = [clip_edge]( + std::vector &triangles, std::vector &temp_triangles, auto eval_vertex) + { + temp_triangles.clear(); + for(auto &input_ref : triangles) + { + Triangle input = input_ref; // copy to enable compiler optimizations + constexpr std::size_t max_clipped_output_vertex_count = 4; + Vec4 output_vertexes[max_clipped_output_vertex_count]; + std::size_t output_vertex_count = 0; + bool skip_triangle = false; + std::size_t end_vertex_index = 1; + for(std::size_t start_vertex_index = 0; start_vertex_index < triangle_vertex_count; + start_vertex_index++) + { + if(!clip_edge(input.vertexes[start_vertex_index], + input.vertexes[end_vertex_index], + output_vertexes, + output_vertex_count, + eval_vertex)) + { + skip_triangle = true; + break; + } + if(++end_vertex_index >= triangle_vertex_count) + end_vertex_index = 0; + } + if(skip_triangle) + continue; + switch(output_vertex_count) + { + case 0: + case 1: + case 2: + continue; + case 3: + temp_triangles.push_back( + Triangle(output_vertexes[0], output_vertexes[1], output_vertexes[2])); + continue; + case 4: + temp_triangles.push_back( + Triangle(output_vertexes[0], output_vertexes[1], output_vertexes[2])); + temp_triangles.push_back( + Triangle(output_vertexes[0], output_vertexes[2], output_vertexes[3])); + continue; + } + assert(!"clipping algorithm failed"); + } + temp_triangles.swap(triangles); + }; + std::vector triangles; + std::vector temp_triangles; + constexpr std::size_t chunk_max_size = 96; + static_assert(chunk_max_size % triangle_vertex_count == 0, ""); + std::unique_ptr chunk_vertex_buffer( + new unsigned char[get_vertex_shader_output_struct_size() * chunk_max_size]); + while(vertex_start_index < vertex_end_index) + { + std::uint32_t chunk_size = vertex_end_index - vertex_start_index; + if(chunk_size > chunk_max_size) + chunk_size = chunk_max_size; + run_vertex_shader(vertex_start_index, + vertex_start_index + chunk_size, + instance_id, + chunk_vertex_buffer.get()); + const unsigned char *current_vertex = + chunk_vertex_buffer.get() + vertex_shader_position_output_offset; + triangles.clear(); + for(std::uint32_t i = 0; i + triangle_vertex_count <= chunk_size; + i += triangle_vertex_count) + { + Triangle triangle; + for(std::size_t j = 0; j < triangle_vertex_count; j++) + { + triangle.vertexes[j] = *reinterpret_cast(current_vertex); + current_vertex += vertex_shader_output_struct_size; + } + triangles.push_back(triangle); + } + // clip to 0 <= vertex.z + clip_triangles(triangles, + temp_triangles, + [](const Vec4 &vertex) noexcept->float + { + return vertex.z; + }); + // clip to vertex.z <= vertex.w + clip_triangles(triangles, + temp_triangles, + [](const Vec4 &vertex) noexcept->float + { + return vertex.w - vertex.z; + }); + // clip to -vertex.w <= vertex.x + clip_triangles(triangles, + temp_triangles, + [](const Vec4 &vertex) noexcept->float + { + return vertex.x + vertex.w; + }); + // clip to vertex.x <= vertex.w + clip_triangles(triangles, + temp_triangles, + [](const Vec4 &vertex) noexcept->float + { + return vertex.w - vertex.x; + }); + // clip to -vertex.w <= vertex.y + clip_triangles(triangles, + temp_triangles, + [](const Vec4 &vertex) noexcept->float + { + return vertex.y + vertex.w; + }); + // clip to vertex.y <= vertex.w + clip_triangles(triangles, + temp_triangles, + [](const Vec4 &vertex) noexcept->float + { + return vertex.w - vertex.y; + }); + [[gnu::unused]] auto rasterize_triangle = [this](Triangle triangle) + { +#warning finish implementing Graphics_pipeline::run::rasterize_triangle + assert(!"finish implementing Graphics_pipeline::run::rasterize_triangle"); + }; +#warning finish implementing Graphics_pipeline::run + assert(!"finish implementing Graphics_pipeline::run"); + } +} + std::unique_ptr Graphics_pipeline::make( Pipeline_cache *pipeline_cache, const VkGraphicsPipelineCreateInfo &create_info) { @@ -426,6 +658,7 @@ std::unique_ptr Graphics_pipeline::make( auto llvm_target_machine = llvm_wrapper::Target_machine::create_native_target_machine(optimization_level); implementation->compiled_shaders.reserve(create_info.stageCount); + util::Enum_set found_shader_stages; for(std::size_t i = 0; i < create_info.stageCount; i++) { auto &stage_info = create_info.pStages[i]; @@ -433,6 +666,10 @@ std::unique_ptr Graphics_pipeline::make( vulkan::get_execution_models_from_shader_stage_flags(stage_info.stage); assert(execution_models.size() == 1); auto execution_model = *execution_models.begin(); + bool added_to_found_shader_stages = + 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); assert(shader_module); { @@ -468,6 +705,8 @@ std::unique_ptr Graphics_pipeline::make( llvm_wrapper::Orc_compile_stack::create(std::move(llvm_target_machine), optimize_module); Vertex_shader_function vertex_shader_function = nullptr; std::size_t vertex_shader_output_struct_size = 0; + util::optional vertex_shader_position_output_offset; + Fragment_shader_function fragment_shader_function = nullptr; for(auto &compiled_shader : implementation->compiled_shaders) { vertex_shader_output_struct_size = implementation->jit_stack.add_eagerly_compiled_ir( @@ -482,6 +721,10 @@ std::unique_ptr Graphics_pipeline::make( switch(compiled_shader.execution_model) { case spirv::Execution_model::fragment: + fragment_shader_function = + reinterpret_cast(shader_entry_point_address); +#warning finish implementing Graphics_pipeline::make + continue; #warning finish implementing Graphics_pipeline::make throw std::runtime_error("creating fragment shaders is not implemented"); case spirv::Execution_model::geometry: @@ -499,9 +742,66 @@ std::unique_ptr Graphics_pipeline::make( vertex_shader_function = reinterpret_cast(shader_entry_point_address); implementation->vertex_shader_output_struct = compiled_shader.outputs_struct; + auto llvm_vertex_shader_output_struct = + implementation->vertex_shader_output_struct->get_or_make_type().type; vertex_shader_output_struct_size = ::LLVMABISizeOfType( - implementation->data_layout.get(), - implementation->vertex_shader_output_struct->get_or_make_type().type); + implementation->data_layout.get(), llvm_vertex_shader_output_struct); + for(auto &member : implementation->vertex_shader_output_struct->get_members(true)) + { + for(auto &decoration : member.decorations) + { + if(decoration.value == spirv::Decoration::built_in) + { + auto &builtin = + util::get(decoration.parameters); + if(builtin.built_in == spirv::Built_in::position) + { + vertex_shader_position_output_offset = + ::LLVMOffsetOfElement(implementation->data_layout.get(), + llvm_vertex_shader_output_struct, + member.llvm_member_index); + break; + } + } + } + if(vertex_shader_position_output_offset) + break; + if(auto *struct_type = + dynamic_cast(member.type.get())) + { + std::size_t struct_offset = + ::LLVMOffsetOfElement(implementation->data_layout.get(), + llvm_vertex_shader_output_struct, + member.llvm_member_index); + auto llvm_struct_type = struct_type->get_or_make_type().type; + for(auto &submember : struct_type->get_members(true)) + { + for(auto &decoration : submember.decorations) + { + if(decoration.value == spirv::Decoration::built_in) + { + auto &builtin = util::get( + decoration.parameters); + if(builtin.built_in == spirv::Built_in::position) + { + vertex_shader_position_output_offset = + struct_offset + + ::LLVMOffsetOfElement(implementation->data_layout.get(), + llvm_struct_type, + submember.llvm_member_index); + break; + } + } + } + if(vertex_shader_position_output_offset) + break; + } + } + if(vertex_shader_position_output_offset) + break; + } + if(!vertex_shader_position_output_offset) + throw std::runtime_error("can't find vertex shader Position output"); #warning finish implementing Graphics_pipeline::make continue; } @@ -511,8 +811,13 @@ std::unique_ptr Graphics_pipeline::make( #warning finish implementing Graphics_pipeline::make if(!vertex_shader_function) throw std::runtime_error("graphics pipeline doesn't have vertex shader"); - return std::unique_ptr(new Graphics_pipeline( - std::move(implementation), vertex_shader_function, vertex_shader_output_struct_size)); + assert(vertex_shader_position_output_offset); + return std::unique_ptr( + new Graphics_pipeline(std::move(implementation), + vertex_shader_function, + vertex_shader_output_struct_size, + *vertex_shader_position_output_offset, + fragment_shader_function)); } } } diff --git a/src/pipeline/pipeline.h b/src/pipeline/pipeline.h index b4887e1..2667da5 100644 --- a/src/pipeline/pipeline.h +++ b/src/pipeline/pipeline.h @@ -31,6 +31,7 @@ #include "llvm_wrapper/llvm_wrapper.h" #include "vulkan/vulkan.h" #include "spirv/spirv.h" +#include "image/image.h" namespace vulkan_cpu { @@ -221,6 +222,14 @@ public: return vertex_shader_output_struct_size; } void dump_vertex_shader_output_struct(const void *output_struct) const; + void run_fragment_shader(std::uint32_t *color_attachment_pixel) const noexcept + { + fragment_shader_function(color_attachment_pixel); + } + void run(std::uint32_t vertex_start_index, + std::uint32_t vertex_end_index, + std::uint32_t instance_id, + image::Image &color_attachment); static std::unique_ptr make(Pipeline_cache *pipeline_cache, const VkGraphicsPipelineCreateInfo &create_info); static std::unique_ptr move_from_handle(VkPipeline pipeline) noexcept @@ -237,10 +246,14 @@ public: private: Graphics_pipeline(std::shared_ptr implementation, Vertex_shader_function vertex_shader_function, - std::size_t vertex_shader_output_struct_size) noexcept + std::size_t vertex_shader_output_struct_size, + std::size_t vertex_shader_position_output_offset, + Fragment_shader_function fragment_shader_function) noexcept : implementation(std::move(implementation)), vertex_shader_function(vertex_shader_function), - vertex_shader_output_struct_size(vertex_shader_output_struct_size) + vertex_shader_output_struct_size(vertex_shader_output_struct_size), + vertex_shader_position_output_offset(vertex_shader_position_output_offset), + fragment_shader_function(fragment_shader_function) { } @@ -248,6 +261,8 @@ private: std::shared_ptr implementation; Vertex_shader_function vertex_shader_function; std::size_t vertex_shader_output_struct_size; + std::size_t vertex_shader_position_output_offset; + Fragment_shader_function fragment_shader_function; }; inline VkPipeline to_handle(Graphics_pipeline *pipeline) noexcept -- 2.30.2