draws triangle properly
authorJacob Lifshay <programmerjake@gmail.com>
Wed, 23 Aug 2017 00:37:09 +0000 (17:37 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Wed, 23 Aug 2017 00:37:09 +0000 (17:37 -0700)
.gitignore
src/demo/demo.cpp
src/image/image.h
src/llvm_wrapper/llvm_wrapper.cpp
src/pipeline/pipeline.cpp
src/pipeline/pipeline.h

index bc0817153b9923c8aea85e571b7121eb90ddab04..347870ace08f1de0bbb400c52c919fd8a2b306eb 100644 (file)
@@ -11,3 +11,4 @@ compile_commands.json
 *.kdev4
 /.cproject
 /.project
+/output.bmp
index b351bd0c49a17a8da0de2a6ea07a4bc97290abd9..20b5d470ce3f0a61753bc1c3bb7a34ec7fccb8f8 100644 (file)
@@ -34,6 +34,7 @@
 #include "util/string_view.h"
 #include "pipeline/pipeline.h"
 #include "vulkan/vulkan.h"
+#include "util/void_t.h"
 
 #if SDL_MAJOR_VERSION != 2
 #error wrong SDL varsion
@@ -242,6 +243,18 @@ pipeline::Pipeline_layout_handle make_pipeline_layout()
 
 int test_main(int argc, char **argv)
 {
+    if(SDL_Init(0) < 0)
+    {
+        std::cerr << "SDL_Init failed: " << SDL_GetError() << std::endl;
+        return 1;
+    }
+    struct Shutdown_sdl
+    {
+        ~Shutdown_sdl()
+        {
+            SDL_Quit();
+        }
+    } shutdown_sdl;
     const char *vertex_shader_filename = "test-files/tri.vert.spv";
     const char *fragment_shader_filename = "test-files/tri.frag.spv";
     if(argc > 1)
@@ -456,6 +469,85 @@ int test_main(int argc, char **argv)
         };
         auto graphics_pipeline =
             pipeline::Graphics_pipeline::make(nullptr, graphics_pipeline_create_info);
+        VkImageCreateInfo image_create_info = {
+            .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+            .pNext = nullptr,
+            .flags = 0,
+            .imageType = VK_IMAGE_TYPE_2D,
+            .format = VK_FORMAT_B8G8R8A8_UNORM,
+            .extent =
+                {
+                    .width = window_width, .height = window_height, .depth = 1,
+                },
+            .mipLevels = 1,
+            .arrayLayers = 1,
+            .samples = VK_SAMPLE_COUNT_1_BIT,
+            .tiling = VK_IMAGE_TILING_LINEAR,
+            .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
+            .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+            .queueFamilyIndexCount = 0,
+            .pQueueFamilyIndices = nullptr,
+            .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+        };
+        image::Image color_attachment(image::Image_descriptor(image_create_info),
+                                      image::allocate_memory_tag);
+        VkClearColorValue clear_color;
+        // set clear_color to opaque red
+        clear_color.float32[0] = 1;
+        clear_color.float32[1] = 0;
+        clear_color.float32[2] = 0;
+        clear_color.float32[3] = 1;
+        color_attachment.clear(clear_color);
+        constexpr std::uint32_t vertex_start_index = 0;
+        constexpr std::uint32_t vertex_end_index = 3;
+        constexpr std::uint32_t instance_id = 0;
+        graphics_pipeline->run(vertex_start_index, vertex_end_index, instance_id, color_attachment);
+        typedef std::uint32_t Pixel_type;
+        // check Pixel_type
+        static_assert(std::is_void<util::void_t<decltype(graphics_pipeline->run_fragment_shader(
+                          static_cast<Pixel_type *>(nullptr)))>>::value,
+                      "");
+        auto rgba = [](std::uint8_t r,
+                       std::uint8_t g,
+                       std::uint8_t b,
+                       std::uint8_t a) noexcept->Pixel_type
+        {
+            union
+            {
+                Pixel_type retval;
+                std::uint8_t bytes[4];
+            };
+            static_assert(sizeof(retval) == sizeof(bytes), "");
+            bytes[0] = b;
+            bytes[1] = g;
+            bytes[2] = r;
+            bytes[3] = a;
+            return retval;
+        };
+        constexpr std::size_t bits_per_pixel = 32;
+        struct Surface_deleter
+        {
+            void operator()(SDL_Surface *v) const noexcept
+            {
+                ::SDL_FreeSurface(v);
+            }
+        };
+        std::unique_ptr<SDL_Surface, Surface_deleter> surface(
+            SDL_CreateRGBSurfaceFrom(color_attachment.memory.get(),
+                                     window_width,
+                                     window_height,
+                                     bits_per_pixel,
+                                     color_attachment.descriptor.get_memory_stride(),
+                                     rgba(0xFF, 0, 0, 0),
+                                     rgba(0, 0xFF, 0, 0),
+                                     rgba(0, 0, 0xFF, 0),
+                                     rgba(0, 0, 0, 0xFF)));
+        if(!surface)
+            throw std::runtime_error(std::string("SDL_CreateRGBSurfaceFrom failed: ") + SDL_GetError());
+        const char *output_file = "output.bmp";
+        if(SDL_SaveBMP(surface.get(), output_file) < 0)
+            throw std::runtime_error(std::string("SDL_SaveBMP failed: ") + SDL_GetError());
+        std::cerr << "saved output image to " << output_file << std::endl;
     }
     catch(std::runtime_error &e)
     {
index b3c62ab19149e118c818d424b74987a860e1e818..3ed16675f3077fd64a7209c92555e2efb2cd8ea1 100644 (file)
@@ -111,6 +111,43 @@ struct Image_descriptor
         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
@@ -135,6 +172,61 @@ struct Image
         : 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<std::size_t>(descriptor.extent.width) * descriptor.extent.height;
+        std::uint32_t *pixels = reinterpret_cast<std::uint32_t *>(memory.get());
+        for(std::size_t i = 0; i < pixel_count; i++)
+        {
+            pixels[i] = clear_color.u32;
+        }
+    }
 #warning finish implementing Image
 };
 }
index e1a4b64f8818c6e5951b85d7dbb9a7272cbf1d71..46c6cfd8d36babb494998bfa79892330529a3e20 100644 (file)
@@ -53,6 +53,13 @@ void Context::init_helper()
         throw std::runtime_error("LLVMInitializeNativeAsmPrinter failed");
     if(::LLVMInitializeNativeDisassembler() != 0)
         throw std::runtime_error("LLVMInitializeNativeDisassembler failed");
+    static struct LLVM_shutdown
+    {
+        ~LLVM_shutdown()
+        {
+            ::LLVMShutdown();
+        }
+    } llvm_shutdown;
 }
 
 LLVM_string Target::get_process_target_triple()
index de12963b6766394baebaa5aa59995663eaf3b8de..1f20c3260934189eb56e61a18f7c1047cc8de025 100644 (file)
@@ -409,12 +409,36 @@ 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,
-                            image::Image &color_attachment)
+                            const image::Image &color_attachment)
 {
+    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();
+    float viewport_x_scale, viewport_x_offset, viewport_y_scale, viewport_y_offset,
+        viewport_z_scale, viewport_z_offset;
+    {
+        float px = viewport.width;
+        float ox = viewport.x + 0.5f * viewport.width;
+        float py = viewport.height;
+        float oy = viewport.y + 0.5f * viewport.height;
+        float pz = viewport.maxDepth - viewport.minDepth;
+        float oz = viewport.minDepth;
+        viewport_x_scale = px * 0.5f;
+        viewport_x_offset = ox;
+        viewport_y_scale = py * 0.5f;
+        viewport_y_offset = oy;
+        viewport_z_scale = pz;
+        viewport_z_offset = oz;
+    }
     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;
+    constexpr std::size_t ivec4_native_alignment = alignof(std::int32_t) * 4;
+    constexpr std::size_t ivec4_alignment =
+        ivec4_native_alignment > max_alignment ? max_alignment : ivec4_native_alignment;
     struct alignas(vec4_alignment) Vec4
     {
         float x;
@@ -431,6 +455,25 @@ void Graphics_pipeline::run(std::uint32_t vertex_start_index,
         {
         }
     };
+    struct alignas(ivec4_alignment) Ivec4
+    {
+        std::int32_t x;
+        std::int32_t y;
+        std::int32_t z;
+        std::int32_t w;
+        constexpr Ivec4() noexcept : x(), y(), z(), w()
+        {
+        }
+        constexpr explicit Ivec4(std::int32_t x,
+                                 std::int32_t y,
+                                 std::int32_t z,
+                                 std::int32_t 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;
@@ -568,8 +611,10 @@ void Graphics_pipeline::run(std::uint32_t vertex_start_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,
+        auto current_vertex_start_index = vertex_start_index;
+        vertex_start_index += chunk_size;
+        run_vertex_shader(current_vertex_start_index,
+                          current_vertex_start_index + chunk_size,
                           instance_id,
                           chunk_vertex_buffer.get());
         const unsigned char *current_vertex =
@@ -628,13 +673,180 @@ void Graphics_pipeline::run(std::uint32_t vertex_start_index,
                        {
                            return vertex.w - vertex.y;
                        });
-        [[gnu::unused]] auto rasterize_triangle = [this](Triangle triangle)
+        VkOffset2D clipped_scissor_rect_min = scissor_rect.offset;
+        VkOffset2D clipped_scissor_rect_end = {
+            .x = scissor_rect.offset.x + static_cast<std::int32_t>(scissor_rect.extent.width),
+            .y = scissor_rect.offset.y + static_cast<std::int32_t>(scissor_rect.extent.height),
+        };
+        if(clipped_scissor_rect_min.x < 0)
+            clipped_scissor_rect_min.x = 0;
+        if(clipped_scissor_rect_min.y < 0)
+            clipped_scissor_rect_min.y = 0;
+        if(clipped_scissor_rect_end.x > color_attachment.descriptor.extent.width)
+            clipped_scissor_rect_end.x = color_attachment.descriptor.extent.width;
+        if(clipped_scissor_rect_end.y < color_attachment.descriptor.extent.height)
+            clipped_scissor_rect_end.y = color_attachment.descriptor.extent.height;
+        if(clipped_scissor_rect_end.x <= clipped_scissor_rect_min.x)
+            continue;
+        if(clipped_scissor_rect_end.y <= clipped_scissor_rect_min.y)
+            continue;
+        for(std::size_t triangle_index = 0; triangle_index < triangles.size(); triangle_index++)
         {
-#warning finish implementing Graphics_pipeline::run::rasterize_triangle
-            assert(!"finish implementing Graphics_pipeline::run::rasterize_triangle");
+            Triangle triangle = triangles[triangle_index];
+            Vec4 projected_triangle_and_inv_w[triangle_vertex_count];
+            Vec4 framebuffer_coordinates[triangle_vertex_count];
+            for(std::size_t i = 0; i < triangle_vertex_count; i++)
+            {
+                projected_triangle_and_inv_w[i].w = 1.0f / triangle.vertexes[i].w;
+                projected_triangle_and_inv_w[i].x =
+                    triangle.vertexes[i].x * projected_triangle_and_inv_w[i].w;
+                projected_triangle_and_inv_w[i].y =
+                    triangle.vertexes[i].y * projected_triangle_and_inv_w[i].w;
+                projected_triangle_and_inv_w[i].z =
+                    triangle.vertexes[i].z * projected_triangle_and_inv_w[i].w;
+                framebuffer_coordinates[i] =
+                    Vec4(projected_triangle_and_inv_w[i].x * viewport_x_scale + viewport_x_offset,
+                         projected_triangle_and_inv_w[i].y * viewport_y_scale + viewport_y_offset,
+                         projected_triangle_and_inv_w[i].z * viewport_z_scale + viewport_z_offset,
+                         0);
+            }
+            float orientation = 0;
+            for(std::size_t start_vertex_index = 0, end_vertex_index = 1;
+                start_vertex_index < triangle_vertex_count;
+                start_vertex_index++)
+            {
+                float x1 = framebuffer_coordinates[start_vertex_index].x;
+                float y1 = framebuffer_coordinates[start_vertex_index].y;
+                float x2 = framebuffer_coordinates[end_vertex_index].x;
+                float y2 = framebuffer_coordinates[end_vertex_index].y;
+                orientation += x2 * y1 - x1 * y2;
+                if(++end_vertex_index >= triangle_vertex_count)
+                    end_vertex_index = 0;
+            }
+            if(!(orientation < 0)
+               && !(orientation > 0)) // zero area triangle or triangle coordinate is NaN
+                continue;
+            // orientation > 0 for counter-clockwise triangle
+            // orientation < 0 for clockwise triangle
+            std::int32_t min_x, end_x, min_y, end_y;
+            bool first = true;
+            for(std::size_t i = 0; i < triangle_vertex_count; i++)
+            {
+                // x and y will be >= 0 so we can use truncate instead of floor for speed
+                auto current_min_x = static_cast<std::int32_t>(framebuffer_coordinates[i].x);
+                auto current_min_y = static_cast<std::int32_t>(framebuffer_coordinates[i].y);
+                std::int32_t current_end_x = current_min_x + 1;
+                std::int32_t current_end_y = current_min_y + 1;
+                if(first || current_min_x < min_x)
+                    min_x = current_min_x;
+                if(first || current_end_x > end_x)
+                    end_x = current_end_x;
+                if(first || current_min_y < min_y)
+                    min_y = current_min_y;
+                if(first || current_end_y > end_y)
+                    end_y = current_end_y;
+                first = false;
+            }
+            if(min_x < clipped_scissor_rect_min.x)
+                min_x = clipped_scissor_rect_min.x;
+            if(end_x > clipped_scissor_rect_end.x)
+                end_x = clipped_scissor_rect_end.x;
+            if(min_y < clipped_scissor_rect_min.y)
+                min_y = clipped_scissor_rect_min.y;
+            if(end_y > clipped_scissor_rect_end.y)
+                end_y = clipped_scissor_rect_end.y;
+            constexpr int scale = 1 << 8;
+            struct Edge_equation
+            {
+                std::int32_t a;
+                std::int32_t b;
+                std::int32_t c;
+                std::int32_t padding;
+                constexpr Edge_equation() noexcept : a(), b(), c(), padding()
+                {
+                }
+                constexpr Edge_equation(std::int32_t a, std::int32_t b, std::int32_t c) noexcept
+                    : a(a),
+                      b(b),
+                      c(c),
+                      padding()
+                {
+                }
+                constexpr bool inside(std::int32_t x, std::int32_t y) const noexcept
+                {
+                    return a * x + b * y + c >= 0;
+                }
+            };
+            Edge_equation edge_equations[triangle_vertex_count];
+            for(std::size_t start_vertex_index = 0, end_vertex_index = 1, other_vertex_index = 2;
+                start_vertex_index < triangle_vertex_count;
+                start_vertex_index++)
+            {
+                float x1 = framebuffer_coordinates[start_vertex_index].x;
+                float y1 = framebuffer_coordinates[start_vertex_index].y;
+                float x2 = framebuffer_coordinates[end_vertex_index].x;
+                float y2 = framebuffer_coordinates[end_vertex_index].y;
+                [[gnu::unused]] float x3 = framebuffer_coordinates[other_vertex_index].x;
+                [[gnu::unused]] float y3 = framebuffer_coordinates[other_vertex_index].y;
+                std::int32_t a;
+                std::int32_t b;
+                std::int32_t c;
+                {
+                    // solve a * x1 + b * y1 + c == 0 &&
+                    // a * x2 + b * y2 + c == 0 &&
+                    // max(abs(a), abs(b)) == scale &&
+                    // a * x3 + b * y3 + c >= 0
+                    float divisor = std::fmax(std::fabs(x2 - x1), std::fabs(y1 - y2));
+                    float factor = scale / divisor;
+                    float a_float = (y1 - y2) * factor;
+                    float b_float = (x2 - x1) * factor;
+                    float c_float = (x1 * y2 - x2 * y1) * factor;
+
+                    // offset to end up checking at pixel center instead of top-left pixel corner
+                    c_float += 0.5f * (a_float + b_float);
+
+                    a = a_float;
+                    b = b_float;
+                    c = c_float;
+                    if(orientation > 0)
+                    {
+                        // fix sign
+                        a = -a;
+                        b = -b;
+                        c = -c;
+                    }
+                }
+                // handle top-left fill rule
+                if(a < 0 || (a == 0 && b < 0))
+                {
+                    // not a top-left edge, fixup c
+                    c--;
+                }
+
+                edge_equations[start_vertex_index] = Edge_equation(a, b, c);
+                if(++end_vertex_index >= triangle_vertex_count)
+                    end_vertex_index = 0;
+                if(++other_vertex_index >= triangle_vertex_count)
+                    other_vertex_index = 0;
+            }
+            auto fs = this->fragment_shader_function;
+            for(std::int32_t y = min_y; y < end_y; y++)
+            {
+                for(std::int32_t x = min_x; x < end_x; x++)
+                {
+                    bool inside = true;
+                    for(auto &edge_equation : edge_equations)
+                    {
+                        inside &= edge_equation.inside(x, y);
+                    }
+                    if(inside)
+                        fs(reinterpret_cast<Pixel_type *>(
+                            color_attachment_memory
+                            + (static_cast<std::size_t>(x) * color_attachment_pixel_size
+                               + static_cast<std::size_t>(y) * color_attachment_stride)));
+                }
+            }
         };
-#warning finish implementing Graphics_pipeline::run
-        assert(!"finish implementing Graphics_pipeline::run");
     }
 }
 
@@ -709,7 +921,7 @@ std::unique_ptr<Graphics_pipeline> Graphics_pipeline::make(
     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(
+        implementation->jit_stack.add_eagerly_compiled_ir(
             std::move(compiled_shader.module),
             &spirv_to_llvm::Jit_symbol_resolver::resolve,
             static_cast<void *>(&implementation->jit_symbol_resolver));
@@ -811,13 +1023,23 @@ std::unique_ptr<Graphics_pipeline> Graphics_pipeline::make(
 #warning finish implementing Graphics_pipeline::make
     if(!vertex_shader_function)
         throw std::runtime_error("graphics pipeline doesn't have vertex shader");
+    if(!create_info.pViewportState)
+        throw std::runtime_error("missing viewport state");
+    if(create_info.pViewportState->viewportCount != 1)
+        throw std::runtime_error("unimplemented viewport count");
+    if(!create_info.pViewportState->pViewports)
+        throw std::runtime_error("missing viewport list");
+    if(!create_info.pViewportState->pScissors)
+        throw std::runtime_error("missing scissor rectangle list");
     assert(vertex_shader_position_output_offset);
     return std::unique_ptr<Graphics_pipeline>(
         new Graphics_pipeline(std::move(implementation),
                               vertex_shader_function,
                               vertex_shader_output_struct_size,
                               *vertex_shader_position_output_offset,
-                              fragment_shader_function));
+                              fragment_shader_function,
+                              create_info.pViewportState->pViewports[0],
+                              create_info.pViewportState->pScissors[0]));
 }
 }
 }
index 2667da5497a85fdf8082fe5f3ab0b632bdee9c15..8850d82407094450c4d55376562bd840f3f152fd 100644 (file)
@@ -229,7 +229,7 @@ public:
     void run(std::uint32_t vertex_start_index,
              std::uint32_t vertex_end_index,
              std::uint32_t instance_id,
-             image::Image &color_attachment);
+             const image::Image &color_attachment);
     static std::unique_ptr<Graphics_pipeline> make(Pipeline_cache *pipeline_cache,
                                                    const VkGraphicsPipelineCreateInfo &create_info);
     static std::unique_ptr<Graphics_pipeline> move_from_handle(VkPipeline pipeline) noexcept
@@ -248,12 +248,16 @@ private:
                       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) noexcept
+                      Fragment_shader_function fragment_shader_function,
+                      VkViewport viewport,
+                      VkRect2D scissor_rect) noexcept
         : implementation(std::move(implementation)),
           vertex_shader_function(vertex_shader_function),
           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)
+          fragment_shader_function(fragment_shader_function),
+          viewport(viewport),
+          scissor_rect(scissor_rect)
     {
     }
 
@@ -263,6 +267,8 @@ private:
     std::size_t vertex_shader_output_struct_size;
     std::size_t vertex_shader_position_output_offset;
     Fragment_shader_function fragment_shader_function;
+    VkViewport viewport;
+    VkRect2D scissor_rect;
 };
 
 inline VkPipeline to_handle(Graphics_pipeline *pipeline) noexcept